我一直在用现有的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;


C ++和Java中的void swapElem(arr)相同。它比较和修改数组中的值,但是原始代码很长一段时间才能在此处发布。为了进行测试,我将其替换为以下函数,因此该循环不会成为无效代码:

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

爪哇:〜1400

C ++(无while循环):〜35

Java(没有while循环):〜40

在这些代码之前,我有一些东西会导致〜35ms性能(通常以-1填充数组),但这在这里并不重要。

最佳答案

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

无论如何,我将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/

10-12 00:24
查看更多