【问题标题】:How can I write code to hint to the JVM to use vector operations?如何编写代码来提示 JVM 使用向量操作?
【发布时间】:2014-05-03 22:12:43
【问题描述】:

有点相关的问题,一岁了:Do any JVM's JIT compilers generate code that uses vectorized floating point instructions?

前言:我正在尝试在纯 Java 中执行此操作(没有 JNI 到 C++,没有 GPGPU 工作等......)。我已经分析过,大部分处理时间来自此方法中的数学运算(可能是 95% 的浮点数学和 5% 的整数数学)。我已经将所有 Math.xxx() 调用减少到一个足够好的近似值,所以现在大部分数学都是浮点乘法和一些加法。

我有一些处理音频处理的代码。我一直在进行调整,并且已经获得了巨大的收益。现在我正在研究手动展开循环,看看是否有任何好处(至少手动展开 2,我看到大约 25% 的改进)。在尝试手动展开 4 时(这开始变得非常复杂,因为我正在展开嵌套循环的两个循环)我想知道是否有什么我可以做的来暗示 jvm 在运行时它可以使用向量操作(例如 SSE2、AVX 等)。音频的每个样本都可以完全独立于其他样本进行计算,这就是为什么我已经能够看到 25% 的改进(减少了对浮点计算的依赖量)。

例如,我有 4 个浮点数,循环的 4 个展开中的每一个都有一个浮点数,用于保存部分计算的值。我如何声明和使用这些花车重要吗?如果我将其设为浮点数 [4],这是否会向 jvm 暗示它们彼此无关,而不是具有浮点数、浮点数、浮点数、浮点数甚至是一类 4 个公共浮点数?有什么我可以做的毫无意义的事情会扼杀我对代码进行矢量化的机会吗?

我在网上看到过有关“正常”编写代码的文章,因为编译器/jvm 知道常见模式以及如何优化它们,而偏离这些模式可能意味着更少的优化。然而,至少在这种情况下,我不会期望将循环展开 2 来提高性能,所以我想知道是否还有其他事情可以做(或者至少 没有 em> 做)来帮助我的机会。我知道编译器/jvm 只会变得更好,所以我也想提防将来做会伤害我的事情。

为好奇编辑:展开 4 将性能提高 another 比展开 2 提高约 25%,所以我真的认为如果 jvm 支持向量操作(或者可能已经正在使用它们)。

谢谢!

【问题讨论】:

  • 1.如果内循环重复多次,我认为展开外循环没有任何意义。 2. JVM 本身做了很多展开,但有时它无法利用它。 This question 在一个简单的案例中显示了近 4 倍的改进。 3. 编写清晰简单的代码在 99.9% 的情况下都是正确的,但如果您知道自己在做什么并努力并为维护成本做好准备,那么您可以做得比 JIT 更好。
  • @maaartinus 感谢您的链接。内部循环通常执行 10 到 300 次,具体取决于一些用户选择(通常在 30-40 左右的低端)。然而,外部循环被执行了数万或数十万次。我尝试只展开内部循环,但它增加了执行时间。我只尝试展开外循环,它确实减少了执行时间,但只是一点点。我想当两者都展开时,CPU 真的可以更容易地挑选出依赖链。
  • 我的想法是:当内部循环执行 10 次时,它内部的每条指令的权重是外部指令的 10 倍;因此我会忽略外面。我只能猜测,但我敢打赌,JVM 无论如何都会展开(有时甚至是too much),而减速只是 JIT 故障(它以不同的方式优化两个等效块,结果时间差异很大)。恐怕查看生成的汇编程序是要走的路。
  • 你明确排除了 GPGPU(我想知道为什么......),但也许你还是应该看一下 code.google.com/p/aparapi :它将使用 OpenCL 进行计算(这可以是GPU CPU!),或者当没有可用的 OpenCL 时使用 Java 线程池。
  • @Marco13 我不包括 GPGPU 选项,因为虽然是的,这几乎是 GPU 工作的完美候选者,但它否定了 java 所追求的“编写一次运行任何地方”的巨大好处。我想让它在纯 java 中尽可能高性能。目标不是“我需要尽可能节省时间吗?” (或者我会用 C 语言编写它并使用 CUDA/OpenCL),但是“我怎样才能在 Java 中尽可能地提高时间效率?”我对此提出的一个问题是,是否有办法向 jvm 提示向量优化会有所帮助。不过欣赏链接!我没听说过。

标签: java performance jvm-hotspot


【解决方案1】:

我如何..音频处理..纯 java(没有 JNI 到 C++,没有 GPGPU 工作等...)..使用矢量运算(例如 SSE2、AVX 等...)

Java 是high level 语言(Java 中的一条指令生成许多硬件指令),这是一种设计(例如垃圾收集器内存管理),不适合实时处理大量数据的任务。

通常有针对特定角色(例如 image processingspeech recognition)优化的特殊硬件,它们多次通过几个简化的处理管道利用并行化。

这类任务也有特殊的编程语言,主要是hardware description languagesassembly language

即使是 C++(被认为是快速语言)也不会自动为您使用一些超级优化的硬件操作。它可能只是在某些地方内联了几种手工制作的汇编语言方法之一。

所以我的回答是 “可能没有办法” 来指示 JVM 对您的代码使用一些硬件优化(例如 SSE),即使有一些然后是 Java 语言运行时仍然会有太多其他因素会降低您的代码速度。

使用为此任务设计的低级语言并将其链接到 Java 以实现高级逻辑。

编辑:根据 cmets 添加更多信息

如果您确信高级“一次编写,随处运行”的语言运行时肯定也应该为您做许多低级优化,并自动将您的高级代码转换为优化的低级代码,那么......方式JIT 编译器的优化依赖于Java Virtual Machine 的实现。有很多。

如果是 Oracle JVM (HotSpot),您可以通过 downloading the source code 开始寻找答案,文本 SSE2 出现在以下文件中:

  • openjdk/hotspot/src/cpu/x86/vm/assembler_x86.cpp
  • openjdk/hotspot/src/cpu/x86/vm/assembler_x86.hpp
  • openjdk/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp
  • openjdk/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp
  • openjdk/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp
  • openjdk/hotspot/src/cpu/x86/vm/vm_version_x86.cpp
  • openjdk/hotspot/src/cpu/x86/vm/vm_version_x86.hpp
  • openjdk/hotspot/src/cpu/x86/vm/x86_32.ad
  • openjdk/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp
  • openjdk/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp
  • openjdk/hotspot/src/share/vm/c1/c1_LinearScan.cpp
  • openjdk/hotspot/src/share/vm/runtime/globals.hpp

它们是 C++ 和汇编语言,所以无论如何你都必须学习一些低级语言才能阅读它们。

即使有 +500 赏金,我也不会打那么深。恕我直言,基于错误的假设,这个问题是错误的

【讨论】:

  • 我知道 Java 是一种高级语言,并且我知道您可以使用 JNI 调用 C/C++,但我说这不是一种选择。 Java 是“一次编写,随处运行”,使用 JNI 完全否定了这一巨大优势。此外,有一些 C++ 编译器会“自动”使用超级优化操作——它被称为自动矢量化,一些编译器(例如英特尔编译器)非常擅长它(尽管显然并不完美)。感谢您的回复以及您花时间链接到几个不同的主题,但它并没有真正回答我的问题。
  • 另外,我知道编译后的代码不会直接编译成例如SSE 指令,但由于这是代码路径中的热点,因此它是 JIT 编译的绝佳候选者,我想知道是否有什么我可以做的,这样 jvm 会看到这部分应该被 jitted 并且它可以利用向量操作。
【解决方案2】:

Hotspot 上的 SuperWord 优化有限且非常脆弱。由于它们通常落后于 C/C++ 编译器提供的功能,因此受到限制,并且由于它们依赖于特定的循环形状(并且仅受某些 CPU 支持)而脆弱。

我知道你想写一次在任何地方运行。听起来您已经有了一个纯 Java 解决方案。您可能需要考虑为已知的流行平台提供一个可选实现,以将该实现补充为“在某些地方快速”,这可能已经是真的了。

很难用一些代码给你更具体的反馈。我建议您采用有问题的循环并将其呈现在 JMH 基准测试中。这样便于分析和讨论。

【讨论】:

    【解决方案3】:

    看起来在Java 8/9 中进行了很多 SIMD/SSE 优化。

    【讨论】:

      猜你喜欢
      • 2020-06-01
      • 1970-01-01
      • 2019-11-19
      • 2020-02-19
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 2011-01-17
      • 2012-12-01
      相关资源
      最近更新 更多