gpt4 book ai didi

java - 性能优化 : C++ vs Java not performing as expected

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:35:27 25 4
gpt4 key购买 nike

我编写了两个程序来实现一个简单的矩阵乘法算法,一个用 C++ 编写,一个用 Java 编写。与我的预期相反,Java 程序的运行速度比 C++ 程序快大约 2.5 倍。我是 C++ 的新手,希望就我可以在 C++ 程序中进行哪些更改以使其运行更快提出建议。

我的程序从这篇博文中借用了代码和数据http://martin-thoma.com/matrix-multiplication-python-java-cpp .

以下是我正在使用的当前编译标志:

g++ -O3 main.cc    

javac Main.java

以下是当前的编译器/运行时版本:

$ g++ --version
g++.exe (GCC) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ java -version
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

我的电脑是一台 ~2012 时代的酷睿 i3 笔记本电脑,运行 Windows 和 MinGW。以下是当前的性能结果:

$ time ./a.exe < ../Testing/2000.in
507584919
real 0m36.469s
user 0m0.031s
sys 0m0.030s

$ time java Main < ../Testing/2000.in
507584919
real 0m14.299s
user 0m0.031s
sys 0m0.015s

这是C++程序:

#include <iostream>
#include <cstdio>
using namespace std;

int *A;
int *B;
int height;
int width;

int * matMult(int A[], int B[]) {
int * C = new int[height*width];
int n = height;
for (int i = 0; i < n; i++) {
for (int k = 0; k < n; k++) {
for (int j = 0; j < n; j++) {
C[width*i+j]+=A[width*i+k] * B[width*k+j];
}
}
}
return C;
}

int main() {
std::ios::sync_with_stdio(false);
cin >> height;
cin >> width;
A = new int[width*height];
B = new int[width*height];
for (int i = 0; i < width*height; i++) {
cin >> A[i];
}

for (int i = 0; i < width*height; i++) {
cin >> B[i];
}

int *result = matMult(A,B);
cout << result[2];
}

这是java程序:

import java.util.*;
import java.io.*;

public class Main {

static int[] A;
static int[] B;
static int height;
static int width;

public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
height = Integer.parseInt(reader.readLine());
width = Integer.parseInt(reader.readLine());
A=new int[width*height];
B=new int[width*height];
int index = 0;

String thisLine;
while ((thisLine = reader.readLine()) != null) {
if (thisLine.trim().equals("")) {
break;
} else {
String[] lineArray = thisLine.split("\t");
for (String number : lineArray) {
A[index] = Integer.parseInt(number);
index++;
}
}
}

index = 0;
while ((thisLine = reader.readLine()) != null) {
if (thisLine.trim().equals("")) {
break;
} else {
String[] lineArray = thisLine.split("\t");
for (String number : lineArray) {
B[index] = Integer.parseInt(number);
index++;
}
}
}

int[] result = matMult(A,B);
System.out.println(result[2]);

reader.close();


} catch (Exception e) {
e.printStackTrace();
}
}

public static int[] matMult(int[] A, int[] B) {
int[] C = new int[height*width];
int n = height;
for (int i = 0; i < n; i++) {
for (int k = 0; k < n; k++) {
for (int j = 0; j < n; j++) {
C[width*i+j]+=A[width*i+k] * B[width*k+j];
}
}
}
return C;
}
}

这是一个 2000x2000 测试用例的链接:https://mega.nz/#!sglWxZqb!HBts_UlZnR4X9gZR7bG-ej3xf2A5vUv0wTDUW-kqFMA

这是一个 2x2 测试用例的链接:https://mega.nz/#!QwkV2SII!AtfGuxPV5bQeZtt9eHNNn36rnV4sGq0_sJzitjiFE8s

任何解释我在 C++ 中做错了什么,或者为什么我的 C++ 实现运行速度比 Java 慢得多的建议,将不胜感激!

编辑:按照建议,我修改了程序,使它们实际上不执行乘法,而只是读入数组并从每个数组中打印出一个数字。这是它的性能结果。 C++ 程序的 IO 较慢。然而,这只是部分差异。

$ time ./IOonly.exe < ../Testing/2000.in
7
944
real 0m8.158s
user 0m0.000s
sys 0m0.046s

$ time java IOOnly < ../Testing/2000.in
7
944
real 0m1.461s
user 0m0.000s
sys 0m0.047s

最佳答案

我无法分析 java 执行,因为它创建了一个临时可执行模块,该模块在“使用”后消失。但是,我假设它确实执行 SSE 指令以获得该速度[或者它展开循环,如果禁用 SSE 指令,clang++ 会执行]

但是用 g++ (4.9.2) 和 clang++ 编译,我可以清楚地看到 clang 优化了循环以使用 SSE 指令,而 gcc 没有。因此,生成的代码正好慢了 4 倍。更改代码,使其在每个维度中使用常量值 2000 [因此编译器“知道”高度和宽度的维度],gcc 编译器生成的代码也需要大约 8 秒(在我的机器上!),而 27 秒具有“可变”值 [这里的 clang 编译代码也稍微快一些,但在噪音范围内我会说]。

总体结论:编译器的质量/智能程度将极大地影响紧密循环的性能。代码越复杂和多变,C++ 解决方案就越有可能生成更好的代码,而简单且易于编译的问题很可能在 Java 代码中更好[作为规则,但不能保证]。例如,我希望 java 编译器使用分析来确定循环数。

编辑:

time 的结果可以用来判断文件的读取是否花费了很长时间,但是你需要某种分析工具来判断实际输入是否使用了很多时间CPU 时间等。

Java 引擎使用“即时编译器”,它使用分析来确定特定代码段被命中的次数(您也可以为 C++ 这样做,大型项目经常这样做!),例如,这允许它展开循环,或在运行时确定循环中的迭代次数。鉴于此代码执行 2000 * 2000 * 2000 次循环,并且 C++ 编译器在知道值的大小告诉我们 Java 运行时实际上并没有做得更好(至少最初没有)时实际上做得更好,只是它设法随着时间的推移提高性能。

不幸的是,由于 java 运行时的工作方式,它不会留下二进制代码,所以我无法真正分析它的作用。

这里的关键是您正在执行的实际操作很简单,逻辑也很简单,只是数量非常多,而且您使用的是简单的实现。例如,Java 和 C++ 都将受益于手动展开循环。

关于java - 性能优化 : C++ vs Java not performing as expected,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33703412/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com