为什么相同的 memcpy glibc 实现在 Linux 上更快而在 Windows 上更慢?

Why same memcpy glibc implementations is faster on Linux and slower on Windows?

我注意到 memcpy 在 Linux 上比在相同硬件上 Windows 更快。我使用 Intel i7 4770 CPU 和 16Gb RAM 和 运行 编译的相同 c++ 代码双启动同一个盒子。我正在尝试使用此代码

memccpy
#include <iostream>
#include <chrono>
#include <cstring>

typedef std::chrono::high_resolution_clock Clock;

int main() {
    const int mb = 300;
    int size = mb * 1024 * 1024 / sizeof(int);
    auto buffer = new int[size];
    srand(1);

    for(int i = 0; i < size; i++) {
        auto r = abs(rand()) % 2048;
        buffer[i] = std::max<int>(r, 1);
    }

    auto buffer2 = new int[size];

    const int repeats = 100;
    for (int j = 2; j < mb; j+=2) {
        auto start = Clock::now();

        // Copy j Mb
        int size = j * 1024 * 1024 / sizeof(int);
        for (int i = 0; i < repeats; i++) {
            int offset = 0;
            while (offset < size) {
                // Run memcpy on random sizes
                int copySize = buffer[offset];
                memcpy(buffer2, buffer, copySize * sizeof(int));
                offset += copySize;
            }
        }

        auto end = Clock::now();
        auto diff = std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count();
        // Time taken per 1Mb
        std::cout << j << "," << diff / j / repeats  << std::endl;
    }
}

Linux 执行速度平均提高 10%。在 Linux 上平均需要 20 微 sec/Mb,在 Windows 上平均需要 22 微 sec/Mb。它在两种情况下都使用 gcc 10.2 m64 -O3 -mavx 标志进行编译。我正在处理的项目是 OS 数据库,我发现 memcpy 和 memset 在 Linux 上的速度更快,小缓冲区的随机长度副本的速度提高了大约 30%。

知道为什么 Windows 上的 memcpy 与 Linux 不同吗?我希望 memcpy 是用汇编语言编写的,不依赖于 OS 而只依赖于 CPU 体系结构。

memcpy 是标准 C 库的一部分,因此,由您 运行 您的代码所在的操作系统提供(或者如果您使用不同的 libc,则由替代提供程序提供) .对于已知大小的小副本,GCC 通常会内联这些操作,因为它通常可以避免函数调用的开销,但对于大或未知大小,它通常会使用系统函数。

在这种情况下,您会看到 glibc 和 Windows 有不同的实现,glibc 提供了更好的选择。 glibc 确实根据最适合给定 CPU 的方式在不同平台上提供了几种不同的变体,但 Windows 可能不会这样做,或者可能有一个不太优化的实现。

过去,glibc 甚至利用了 memcpy 不能有重叠参数的事实,并在某些 CPU 上向后复制,但不幸的是,这破坏了一些不符合的程序标准,尤其是 Adob​​e Flash Player。但是,这样的实现是允许的,而且确实更快。

而不是 memcpy 变慢,您可能会发现 Windows 具有不同的内存处理策略。例如,在第一次分配内存时,通常不会在所有内存中出错。您可能会发现 Linux,它在某些情况下会预断后续页面,由于该优化或不同的优化,此处可能表现更好。如果 Windows 选择不这样做,可能是因为它使代码复杂化,或者因为它在 Windows 上通常 运行 的实际用例中表现不佳.在综合基准测试中表现良好的部分可能与现实世界中表现良好的部分相匹配,也可能不匹配。

归根结底,这是一个实施质量问题。该标准要求它指定的功能以指定的方式运行,但不指定性能特征。如果该功能的性能对他们来说非常重要,一些项目会选择包括优化的 memcpy 实现。其他人则选择不这样做,而是更愿意建议用户选择最能满足他们需求的平台,同时考虑到某些平台的性能可能优于其他平台。