java - 迭代性能 Java 与 C++

标签 java c++ performance

这个问题让我困惑了 3 天。我有一个应用程序需要评估一组元素很少的整数多项式(多个参数)。我已经有一个用 Java 编写的实现,我目前正在移植到 C++。

在测试期间,我注意到 C++ 版本比 Java 变体慢几个数量级。我当然知道 JIT-ing 并且这种情况特别适合这种编译器,但我所看到的与我的预期相去甚远。

下面是示例代码,您需要 boost 来编译 C++ 代码(但只有简单的时间测量才需要这种依赖)。

choeger@daishi ~/uebb % clang++ -O3 -std=c++11 polytest.cpp -lboost_timer -lboost_system
choeger@daishi ~/uebb % ./a.out                                                         
 0.011694s wall, 0.010000s user + 0.000000s system = 0.010000s CPU (85.5%)
Ideal Result: 1e+07
 0.421986s wall, 0.420000s user + 0.000000s system = 0.420000s CPU (99.5%)
Result: 1e+07
choeger@daishi ~/uebb % javac PolyTest.java                                             
choeger@daishi ~/uebb % java PolyTest                                                   
evals: 10000000 runtime: 17ms
Ideal Result: 1.0E7
evals: 10000000 runtime: 78ms
Result: 1.0E7

显然,就纯计算能力而言,C++ 版本(使用 clang-3.3 编译)运行速度稍快,但在解释多项式时,Java (openjdk 1.7.0.60) 的表现要好得多。到目前为止,我的猜测是,由于对小(样本 1 元素) vector 的迭代,我的 C++ 代码不是很理想。我假设 JVM 在缓存命中未命中方面做得更好。

有什么方法可以使我的 C++ 版本性能更好?我只是没有看到其他原因吗?附带说明:有没有办法测量 C++ 和 Java 进程的缓存一致性?

C++ 代码如下所示:

#include <boost/timer/timer.hpp>

#include <iostream>
#include <vector>

using namespace std;

struct Product {
  int factor;
  vector<int> fields;
};


class SumOfProducts {
public:
  vector<Product> sum;

  /**
   * evaluate the polynomial with arguments separated by width
   */
  inline double eval(const double* arg, const int width) const {
    double res = 0.0;
    for (Product p : sum) {
      double prod = p.factor;
      for (int f : p.fields) {
    prod *= arg[f*width];
      }
      res += prod;
    }
    return res;
  };
};


double idealBenchmark(const double* arg, const int width) {
  boost::timer::auto_cpu_timer t;
  double res = 0.0;
  // run 10M evaluations 
  for (long l = 0; l < 10000000; l++) {
    res = res + arg[width] * arg[width];
  }
  return res;
}

double benchmark(const double* arg, const SumOfProducts& poly) {
  boost::timer::auto_cpu_timer t;
  double res = 0.0;
  // run 10M evaluations 
  for (long l = 0; l < 10000000; l++) {
    res = res + poly.eval(arg, 1);
  }
  return res;
}

int main() {
  //simple polynomial: x_1^2
  Product p;
  p.factor = 1;
  p.fields.push_back(1);
  p.fields.push_back(1);

  SumOfProducts poly;
  poly.sum.push_back(p);

  double arg[] = { 0, 1 };

  double res = idealBenchmark(arg, 1);
  cout << "Ideal Result: " << res << endl;

  res = benchmark(arg, poly);
  cout << "Result: " << res << endl;
}

Java 版本是这样的:

public class PolyTest {

    static class Product {
    public final int factor;
    public final int[] fields;

    public Product(int pFactor, int[] pFields) {
        factor = pFactor;
        fields = pFields;
    }
    }

    static class SumOfProducts {
    final Product[] sum;

    public SumOfProducts(Product[] pSum) {
        sum = pSum;
    }

    /**
     * evaluate the polynomial with arguments separated by width
     */
    double eval(final double[] arg, final int width) {
        double res = 0.0;
        for (Product p : sum) {
        double prod = p.factor;
        for (int f : p.fields) {
            prod *= arg[f*width];
        }
        res += prod;
        }
        return res;
    }
    }

    static double idealBenchmark(final double[] arg, final int width) {
    final long start = System.currentTimeMillis();
    double res = 0.0;
    long evals = 0;
    // run 10M evaluations 
    for (long l = 0; l < 10000000; l++) {
        evals++;
        res = res + arg[width] * arg[width];
    }
    System.out.println("evals: " + evals + " runtime: " + (System.currentTimeMillis() - start) + "ms");
    return res;
    }

    static double benchmark(final double[] arg, final SumOfProducts poly) {
    final long start = System.currentTimeMillis();
    double res = 0.0;
    long evals = 0;
    // run 10M evaluations 
    for (long l = 0; l < 10000000; l++) {
        evals++;
        res = res + poly.eval(arg, 1);
    }
    System.out.println("evals: " + evals + " runtime: " + (System.currentTimeMillis() - start) + "ms");
    return res;
    }

    public static void main(String[] args) {
    //simple polynomial: x_1^2
    Product p = new Product(1, new int[]{1, 1});

    SumOfProducts poly = new SumOfProducts(new Product[]{p});

    double arg[] = { 0, 1 };

    double res = idealBenchmark(arg, 1);
    System.out.println("Ideal Result: " + res);

    res = benchmark(arg, poly);
    System.out.println("Result: " + res);
    }

}

最佳答案

你在这里制作昂贵的拷贝:

for (Product p : sum)

每次复制意味着完全复制 std::vector<int>每个元素的数据成员。改用引用:

for (const Product& p : sum)

请注意,我制作了它们 const ,因为您不需要更改范围的元素。

关于java - 迭代性能 Java 与 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21007029/

相关文章:

java - 如何将多个动画 GIF 组合成一个 GIF 网格

java - 您可以将一个对象转换为实现接口(interface)的对象吗? (JAVA)

c++ - 确定真/假

python - 模型训练时间较长

java - JUnit 测试出现错误

java - 多次引用另一个类中的 HashMap 是否效率低下?

.net - 如何将 System::String^ 转换为 std::string?

函数声明中的 C++ const 变量

performance - MongoDB 分片集群比独立节点慢 25

scala - 获取两个列表的元素总和的最快方法