【问题标题】:Why same memcpy glibc implementations is faster on Linux and slower on Windows?为什么相同的 memcpy glibc 实现在 Linux 上更快而在 Windows 上更慢?
【发布时间】:2021-03-13 23:55:49
【问题描述】:

我注意到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 微秒/Mb,在 Windows 上平均需要 22 微秒/Mb。它在两种情况下都使用 gcc 10.2 m64 -O3 -mavx 标志编译。我正在处理的项目是 OS 数据库,在那里我发现 memcpy 和 memset 在 Linux 上的速度更快,小缓冲区的随机长度副本的速度提高了大约 30%。

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

【问题讨论】:

  • 这两个操作系统有不同的memcpy() 实现,显然Linux(嗯,glibc)优化得更好。或者还有其他问题会影响您的基准测试时间(实际 CPU 时间比挂钟时间更好)。

标签: linux windows glibc questdb


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 谢谢,很好的答案,但我仍然不确定。当我在 Windows 上使用 GCC 10.2 时,它不会链接到 memcpy 的 Glib C 实现吗?我没有看到依赖于平台的实现,这里只依赖于 CPU github.com/bminor/glibc/blob/master/sysdeps/x86_64/multiarch/…
  • 不,glibc 是 Linux 系统上使用的 C 运行时库。 Windows 通常使用 MSVCRT,但如果您使用 GCC,您可能会使用替代的 C 运行时库,这也不是 glibc。 Windows 根本不使用 glibc(WSL 除外)。
猜你喜欢
  • 2011-12-25
  • 2014-07-18
  • 2013-12-24
  • 2012-03-21
  • 2013-03-21
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
相关资源
最近更新 更多