本研究的目的是探讨JIT(即时编译)和AOT(提前编译)策略之间的性能差异,并了解它们各自的优缺点。其目的并不是声称一种语言比另一种语言慢或差。 在我们的测试中,我们观察到使用 JIT 编译(JVMCI 和 C2)的 HotSpot JVM 23 可以获得更好的结果。使用 C++(使用 Clang 18 编译)、GraalVM 23(使用本机映像编译)和带有 -Xcomp 标志的 HotSpot JVM 23(JVMCI 和 C2),我们得到的结果较慢。我们正在寻求理解为什么会发生这种情况,如果可能的话,找出提高 C++ 版本性能的方法,以匹配 Java 的 JIT 编译的结果。
我们的基准测试涉及将 Java 中简单且小型的哈希表(映射)实现与 C++ 中的等效(逐行)实现进行比较。我们尽一切努力确保两个实现之间的一致性。目标是不是比较哈希表的标准库实现(
java.util.HashMap
和std::unordered_map
),因为它们当然是使用非等效源代码实现的。
该基准测试创建了一个包含 20,000 个存储桶的哈希表并插入了 2,000,000 个对象。我们插入一次,清除映射,然后再次插入,以利用哈希表的
Entry
对象内部对象池。换句话说,第一次插入时,对象将在堆中分配。第二次插入对象时,将从内部对象池中重新使用。
处理测量的
Bench
类在两种语言中也应该是等效的。
考虑到这些细节,有谁知道为什么 C++ 映射实现比 Java 映射实现慢?是否有我们忽略的东西或者 C++ 实现的某个方面可以进一步优化?也许我们应该探索特定的 Clang 优化选项?
所有源代码(不多)以及编译和执行的脚本以及我们的结果都在项目的Github中。
从下面的评论来看,C++ 代码改进的主要来源可能是用于哈希表内部Entry
对象的自定义分配器
new
关键字,Java 比 C++ 更容易/更快地在堆中分配对象,但在 C++ 中可以通过使用 自定义分配器 来解决这个缺点。不幸的是,我对 C++ 的了解有限,我必须做一些研究才能了解如何做到这一点。 如果能在 C++ 代码中进行这样的更改/改进,那就太好了。
需要注意的是,在第二次 put 基准测试中,C++ 代码不会在堆
中分配任何
Entry
,因为对象将在内部对象池中全部可用(从第一次 put 开始)。因此,我不确定为什么 C++ 在第二个 put 基准测试中仍然较慢。
FTR,下面是我们当前针对 Java 和 C++ 的结果:
HotSpotVM JIT:(使用 Graal JVMCI JIT)
PUT => Avg: 371 ns | Min: 28 ns | 99.9% = [avg: 367 ns, max: 1.743 micros]
PUT => Avg: 613 ns | Min: 27 ns | 99.9% = [avg: 606 ns, max: 2.184 micros]
GET => Avg: 615 ns | Min: 14 ns | 99.9% = [avg: 607 ns, max: 2.549 micros]
DEL => Avg: 662 ns | Min: 18 ns | 99.9% = [avg: 658 ns, max: 2.538 micros]
C++ LLVM:(叮当声)
PUT => Avg: 726 ns | Min: 30 ns | 99.9% = [avg: 720 ns, max: 4.097 micros]
PUT => Avg: 857 ns | Min: 18 ns | 99.9% = [avg: 848 ns, max: 2.933 micros]
GET => Avg: 874 ns | Min: 18 ns | 99.9% = [avg: 865 ns, max: 3.010 micros]
DEL => Avg: 875 ns | Min: 19 ns | 99.9% = [avg: 871 ns, max: 2.810 micros]
基准环境:
$ uname -a
Linux hivelocity 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/issue | head -n 1
Ubuntu 18.04.6 LTS \n \l
$ cat /proc/cpuinfo | grep "model name" | head -n 1 | awk -F ": " '{print $NF}'
Intel(R) Xeon(R) E-2288G CPU @ 3.70GHz
$ arch
x86_64
$ clang++ --version
Ubuntu clang version 18.1.0 (++20240220094926+390dcd4cbbf5-1~exp1~20240220214944.50)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ java -version
java version "23.0.1" 2024-10-15
Java(TM) SE Runtime Environment Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01, mixed mode, sharing)
$ native-image --version
native-image 23.0.1 2024-10-15
GraalVM Runtime Environment Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01)
Substrate VM Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11, serial gc, compressed references)