java - mmap() vs Java MappedByteBuffer 性能?

标签 java c++ performance mmap mappedbytebuffer

我一直在从现有的 Java 代码开发一个 C++ 项目。我从同一个测试文件中读取了以下 C++ 代码和 Java 代码,该文件由数百万个整数组成。

C++:

    int * arr = new int[len]; //len is larger than the largest int from the data
    fill_n(arr, len, -1);  //fill with -1
    long loadFromIndex = 0;
    struct stat sizeResults;
    long size;
    if (stat(fileSrc, &sizeResults) == 0) {
        size = sizeResults.st_size; //here size would be ~551950000 for 552M test file
    }
    mmapFile = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, pageNum*pageSize);
    long offset = loadFromIndex % pageSize;
    while (offset < size) {
        int i = htonl(*((int *)(mmapFile + offset)));
        offset += sizeof(int);
        int j = htonl(*((int *)(mmapFile + offset)));
        offset += sizeof(int);
        swapElem(i, j, arr);
    }
    return arr;

Java:

    IntBuffer bb = srcFile.getChannel()
                    .map(MapMode.READ_ONLY, loadFromIndex, size)
                    .asIntBuffer().asReadOnlyBuffer();
    while (bb.hasRemaining()) {
        int i = bb.get();
        int j = bb.get();
        swapElem(i, j, arr); //arr is an int[] of the same size as the arr in C++ version, filled with -1
    }
    return arr;

void swapElem(arr) 在 C++ 和 Java 中是相同的。它比较和修改数组中的值,但原始代码有点长,无法在此处发布。出于测试目的,我将其替换为以下函数,这样循环就不会成为死代码:

void swapElem(int i, int j, int * arr){   // int[] in Java
    arr[i] = j;
}

我假设 C++ 版本应该优于 Java 版本,但测试给出了相反的结果——Java 代码几乎比 C++ 代码快两倍。有什么方法可以改进 C++ 代码吗?

我觉得 C++ 中的 mmapFile+offset 可能重复了太多次,所以它是 O(n) 加法和 O(n) 加法 offset+=sizeof(int) ,其中 n 是要读取的整数个数。对于 Java 的 IntBuffer.get(),它只是直接从缓冲区的索引中读取,所以除了 O(n) 次将缓冲区索引递增 1 之外不需要其他操作。因此,包括缓冲区索引的增量,C++ 需要 O(2n) 加法,而 Java 需要 O(n) 加法。当涉及到数百万数据时,可能会导致显着的性能差异。

按照这个思路,我修改了C++代码如下:

    mmapBin = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, pageNum*pageSize);
    int len = size - loadFromIndex % pageSize;
    char * offset = loadFromIndex % pageSize + mmapBin;
    int index = 0;
    while (index < len) {
        int i = htonl(*((int *)(offset)));
        offset += sizeof(int);
        int j = htonl(*((int *)(offset)));
        offset += sizeof(int);
        index+=2*sizeof(int);
    }

我原以为会有轻微的性能提升,但事实并非如此。

谁能解释为什么 C++ 代码比 Java 代码运行得慢?谢谢。

更新:

很抱歉,当我说 -O2 不起作用时,我这边出现了问题。我弄乱了 Makefile,所以 C++ 代码没有使用 -O2 重新编译。我已经更新了性能,使用 -O2 的 C++ 版本的性能优于 Java 版本。这可以解决问题,但如果有人想分享如何改进 C++ 代码,我会关注。通常我希望它比 Java 代码快 2 倍,但目前并非如此。感谢大家的投入。

编译器:g++

标志:-Wall -c -O2

Java 版本:1.8.0_05

文件大小:552MB,全部为 4 字节整数

处理器:2.53 GHz Intel Core 2 Duo

内存 4GB 1067 MHz DDR3

更新的基准:

版本时间(毫秒)

C++:~1100

Java:~1400

C++(没有 while 循环):~35

Java(没有 while 循环):~40

我在这些代码之前有一些导致 ~35ms 性能的东西(主要是用 -1 填充数组),但这在这里并不重要。

最佳答案

我有些怀疑基准方法是否正确。这两种代码都是“死”代码。您实际上并没有在任何地方使用 i 和 j,因此 gcc 编译器或 Java JIT 可能会决定实际删除循环,因为它对 future 的代码流没有影响。

无论如何,我会将 C++ 代码更改为:

mmapFile = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, pageNum*pageSize);
long offset = loadFromIndex % pageSize;
int i, j;
int szInc = 2 * sizeof(int);
while (offset < size) {
    scanf(mmapFile, "%d", &i);
    scanf(mmapFile, "%d", &j);
    offset += szInc; // offset += 8;
}

这相当于 Java 代码。此外,我会继续使用 -O2 作为编译标志。请记住,htonl 是 Java 代码似乎不会执行的额外转换。

关于java - mmap() vs Java MappedByteBuffer 性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26600486/

相关文章:

Java访问列表,这是在 map 中

java - 不理解 java 教程中提到的如何将接口(interface)用作 API 的主题。不要得到顿悟时刻

c++ - 如何让 Visual Studio 显示 "auto"类型

performance - Julia:使用恒定字段进行结构以优化性能

javascript - 有没有比硬编码正则表达式和字符串更好的方法将值传递给 .replace() ?

java - 在调用两种方法之间随机选择

java - 将网络元素的大小转换为整数变量时遇到问题

c++ - 在 MFC Direct3D 应用程序中使用 DXUTSetWindow

c++ - 纯虚拟和内联定义

php - 提高 Facebook FQL 的性能和可用性