繁体   English   中英

性能优化:C ++ vs Java没有按预期执行

[英]Performance optimization: C++ vs Java not performing as expected

我编写了两个程序,实现了一个简单的矩阵乘法算法,一个用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笔记本电脑,运行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编译器还生成大约8s的代码(在我的机器上!),而27s具有“变量”值[clang编译的代码在这里稍微快一点,但在我所说的噪声中]。

总体结论:编译器的质量/聪明度将严重影响紧密循环的性能。 代码越复杂和变化,C ++解决方案就越有可能生成更好的代码,在Java代码中,简单易编译的问题很可能会更好[通常,但不能保证]。 我希望java编译器使用分析来确定循环的数量。

编辑:

time的结果可用于确定文件的读取是否花费很长时间,但是您需要某种分析工具来确定实际输入是否使用了大量的CPU时间等。

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

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

这里的关键是你正在做的实际操作很简单,逻辑很简单,只是它们中的很多,你使用一个简单的实现来做它们。 例如,Java和C ++都将受益于手动展开循环。

默认情况下,C ++并不比Java快

C ++作为一种语言很快,但是当你将库合并到一起时,你就会受到这些库的速度的束缚。

这个标准很难建立在性能上。 标准库的编写考虑了设计和正确性。

C ++为您提供优化的机会!
如果您对标准库的性能不满意,您可以使用自己的优化版本。

例如,标准C ++ IO对象在设计(流,区域设置,构面,内部缓冲区)方面非常漂亮,但这会使它们在性能上变得非常糟糕。 如果您是为Windows操作系统编写的,则可以使用ReadFileWriteConsole作为IO的机制。
如果切换到这些函数而不是标准库 - 您的程序优于Java几个数量级。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM