我编写了两个程序来实现一个简单的矩阵乘法算法,一个用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测试用例的链接:UlZnR4X9gZR7bG-ej3xf2A5vUv0wTDUW-kqFMA
下面是2x2测试用例的链接: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发布于 2015-11-14 00:10:51
我无法分析java执行,因为它创建了一个临时可执行模块,该模块在被“使用”后消失。但是,我假设它确实执行SSE指令以获得这个速度,或者它展开循环,如果禁用SSE指令,clang++会这样做。
但是用g++ (4.9.2)和clang++编译,我可以清楚地看到,clang优化了循环,使其使用SSE指令,而gcc不使用。因此,生成的代码要慢4倍。修改代码,使其在每个维度中使用一个恒定值为2000,使编译器“知道”高度和宽度的维度,gcc编译器还生成了大约8s (在我的机器上!)的代码,与带有“变量”值的27s相比,clang编译的代码在这里也稍微快了一些,但在噪声范围内我会说。
总体结论:编译器的质量/机敏性将极大地影响紧循环的性能。代码越复杂,变化越大,C++解决方案就越有可能生成更好的代码,在这些代码中,简单和容易编译的问题很可能作为一种规则在Java代码中更好,但不能保证。例如,我希望java编译器使用分析来确定循环的数量。
编辑:
time的结果可以用来确定文件的读取是否需要很长时间,但是您需要某种分析工具来确定实际输入是否使用了大量的CPU时间等等。
java引擎使用“即时编译器”,它使用分析来确定特定代码被击中的次数(您也可以对C++这样做,大型项目也可以这样做!),这允许它例如展开一个循环,或者在运行时确定一个循环中的迭代次数。考虑到这段代码执行2000 * 2000 * 2000循环,而当C++编译器知道值的大小时,它实际上做得更好,这告诉我们Java运行时实际上并没有做得更好(至少一开始不是这样),只是随着时间的推移,它设法提高了性能。
不幸的是,由于java运行时的工作方式,它没有留下二进制代码,所以我无法真正分析它所做的事情。
这里的关键是,您正在执行的实际操作很简单,逻辑也很简单,它只是非常多的操作,而且您使用的是一个简单的实现。例如,通过手动展开循环,Java和C++都将受益。
发布于 2015-11-14 11:51:20
默认情况下,C++并不比快
C++作为一种语言是快速的,但是一旦您将库合并到混合中,您就会被限制在这些库的速度上。
这个标准几乎是为了性能而制定的,在此期间。标准库的编写考虑到了设计和正确性。
C++给了您优化的机会!
如果您对标准库的性能不满意,您可以也应该使用您自己的优化版本。
例如,标准的C++ IO对象在设计方面非常漂亮(流、区域设置、面、内部缓冲区),但这使得它们在性能上很糟糕。如果您是为Windows编写的,可以使用ReadFile和WriteConsole作为IO的机制。
如果您切换到这些函数而不是标准库--您的程序的性能比Java高几个数量级。
https://stackoverflow.com/questions/33703412
复制相似问题