6

我为使用动态编程找出最长公共子序列的方法创建了一个基准:

@Benchmark
  def longestCommonSubsequenceDP(): String = {
    val s1 = "Pellentesque lacinia"
    val s2 = "Mauris purus massa"
    val up = 1
    val left = 2
    val charMatched = 3

    val s1Length = s1.length()
    val s2Length = s2.length()

    val lcsLengths = Array.fill[Int](s1Length + 1, s2Length + 1)(0)

    for (i <- 0 until s1Length) {
      for (j <- 0 until s2Length) {
        if (s1.charAt(i) == s2.charAt(j)) {
          lcsLengths(i + 1)(j + 1) = lcsLengths(i)(j) + 1
        } else {
          if (lcsLengths(i)(j + 1) >= lcsLengths(i + 1)(j)) {
            lcsLengths(i + 1)(j + 1) = lcsLengths(i)(j + 1)
          } else {
            lcsLengths(i + 1)(j + 1) = lcsLengths(i + 1)(j)
          }
        }
      }
    }

    val subSeq = new StringBuilder()
    var s1Pos = s1Length
    var s2Pos = s2Length

    do {
      if (lcsLengths(s1Pos)(s2Pos) == lcsLengths(s1Pos -1)(s2Pos)) {
        s1Pos -= 1
      } else if (lcsLengths(s1Pos)(s2Pos) == lcsLengths(s1Pos)(s2Pos - 1)) {
        s2Pos -= 1
      } else {
        assert(s1.charAt(s1Pos - 1) == s2.charAt(s2Pos - 1))
        subSeq += s1.charAt(s1Pos - 1)
        s1Pos -= 1
        s2Pos -= 1
      }

    } while (s1Pos > 0 && s2Pos > 0)

    subSeq.toString.reverse
  }

并使用以下配置运行它jmh:run -i 10 -wi 10 -f1 -t1 并得到以下结果:

GraalVM EE 1.0.0-rc10

[info] Benchmark                        Mode  Cnt   Score   Error   Units
[info] LCS.longestCommonSubsequenceDP  thrpt   25  91.411 ± 4.355  ops/ms

GraalVM CE 1.0.0-rc10

[info] Benchmark                        Mode  Cnt   Score   Error   Units
[info] LCS.longestCommonSubsequenceDP  thrpt   25  26.741 ± 0.408  ops/ms

OpenJDK 1.8.0_192

[info] Benchmark                        Mode  Cnt   Score   Error   Units
[info] LCS.longestCommonSubsequenceDP  thrpt   25  45.216 ± 1.956  ops/ms

我还做了另一个测试,我创建了一个包含数千个对象的列表,对其进行了一些过滤和排序,并且thrpt在 GraalVM CE 上是最小的。

为什么会有这种差异?

4

1 回答 1

4

您会得到不同的结果,因为您使用的运行时启用了不同的顶级 JIT 编译器。除非另有说明(例如使用命令标志):

  • OpenJDK 1.8.0_192 使用 C2
  • GraalVM CE 1.0.0-rc10 使用 Graal 编译器。
  • GraalVM EE 1.0.0-rc10 使用企业版 Graal 编译器。

JIT 在运行时将您的代码编译成机器代码,这在很大程度上取决于原始代码、工作负载、JIT 配置、启用的优化等。

可以合理地预期 JIT 编译器的不同实现会在同一基准测试中显示不同的结果。

如果你问为什么 GraalVM CE 在这个特定的基准上没有显示出更好的结果,而不是关于一般差异的哲学问题;这是一个简短的解释。所有编译器都擅长某些方面,例如 Graal 具有出色的转义分析和内联算法,这在使用抽象的代码上显示出很好的结果:分配对象、调用方法等。

这个特定的基准用整数填充一个数组并运行一个循环。这可能并不完全允许 ​​Graal 做它擅长的事情。所以,这是一个 C2 擅长的微基准测试的例子。您可能可以构建一个类似的基准,GraalVM CE 会显示出优于 OpenJDK 的优势(也许您可以尝试这个:http ://www.graalvm.org/docs/examples/java-simple-stream-benchmark/ )。

GraalVM 团队运行大量基准测试,这就是 GraalVM CE 更好的知识来源。但是,需要了解将一组复杂的基准测试结果减少到一个数字对于评估任何特定代码及其工作负载的性能并不是最有意义的事情。人们应该始终努力评估他们的代码。

于 2018-12-17T09:47:26.973 回答