c - 与 clang 相比,gcc 的具有一定字节数的 __builtin_memcpy 性能很糟糕

标签 c performance gcc clang

我想在做任何其他事情之前先在这里分享这个以征求您的意见。我在设计算法时发现,与 clang 相比,gcc 编译一些简单代码的代码性能是灾难性的。

如何重现

创建一个包含此代码的 test.c 文件:

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

int main(int argc, char *argv[]) {
    const uint64_t size = 1000000000;
    const size_t alloc_mem = size * sizeof(uint8_t);
    uint8_t *mem = (uint8_t*)malloc(alloc_mem);
    for (uint_fast64_t i = 0; i < size; i++)
        mem[i] = (uint8_t) (i >> 7);

    uint8_t block = 0;
    uint_fast64_t counter = 0;
    uint64_t total = 0x123456789abcdefllu;
    uint64_t receiver = 0;

    for(block = 1; block <= 8; block ++) {
        printf("%u ...\n", block);
        counter = 0;
        while (counter < size - 8) {
            __builtin_memcpy(&receiver, &mem[counter], block);
            receiver &= (0xffffffffffffffffllu >> (64 - ((block) << 3)));
            total += ((receiver * 0x321654987cbafedllu) >> 48);
            counter += block;
        }
    }

    printf("=> %llu\n", total);
    return EXIT_SUCCESS;
}

海湾合作委员会

编译运行:

gcc-7 -O3 test.c
time ./a.out
1 ...
2 ...
3 ...
4 ...
5 ...
6 ...
7 ...
8 ...
=> 82075168519762377

real    0m23.367s
user    0m22.634s
sys 0m0.495s

信息:

gcc-7 -v
Using built-in specs.
COLLECT_GCC=gcc-7
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/7.3.0/libexec/gcc/x86_64-apple-darwin17.4.0/7.3.0/lto-wrapper
Target: x86_64-apple-darwin17.4.0
Configured with: ../configure --build=x86_64-apple-darwin17.4.0 --prefix=/usr/local/Cellar/gcc/7.3.0 --libdir=/usr/local/Cellar/gcc/7.3.0/lib/gcc/7 --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-7 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --enable-checking=release --with-pkgversion='Homebrew GCC 7.3.0' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --disable-nls
Thread model: posix
gcc version 7.3.0 (Homebrew GCC 7.3.0)

所以我们得到大约 23 秒的用户时间。现在让我们对 cc(macOS 上的 clang)做同样的事情:

clang

cc -O3 test.c
time ./a.out
1 ...
2 ...
3 ...
4 ...
5 ...
6 ...
7 ...
8 ...
=> 82075168519762377

real    0m9.832s
user    0m9.310s
sys 0m0.442s

信息:

Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

快 2.5 倍 !!有什么想法吗?

我用 memcpy 替换了 __builtin_memcpy 函数来测试结果,这一次编译后的代码在双方大约 34 秒内运行 - 与预期一致且速度较慢。

看起来 __builtin_memcpy 和位掩码的组合被两个编译器解释得非常不同。 我查看了汇编代码,但由于我不是 asm 专家,所以看不到任何可以解释性能下降的突出内容。


编辑 03-05-2018 : 发布了这个错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84719


最佳答案

我怀疑您为 memcpy 和 __builtin_memcpy 获得了不同的代码。我认为这不应该发生,事实上我无法在我的 (linux) 系统上重现它。

如果在 for 循环之前添加 #pragma GCC unroll 16(在 gcc-8+ 中实现),gcc 将获得与 clang 相同的性能(使 block 成为常量是优化代码所必需的),所以基本上 llvm 的展开比 gcc 的展开更积极,这可能是好是坏取决于情况。尽管如此,请随时向 gcc 报告它,也许他们有一天会调整展开的启发式方法,额外的测试用例可能会有所帮助。

一旦处理好展开,gcc 对于某些值(block 尤其等于 4 或 8)确实没问题,但对于其他一些值就更糟了,尤其是 3。但是最好用 block 上没有循环的较小测试用例。 Gcc 似乎对 memcpy(,,3) 有问题,如果你总是读取 8 个字节(下一行已经处理了额外的字节 IIUC),它会工作得更好。另一件可以报告给 gcc 的事情。

关于c - 与 clang 相比,gcc 的具有一定字节数的 __builtin_memcpy 性能很糟糕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49098453/

相关文章:

c - 分析和提高嵌入式系统性能的步骤/策略是什么?

c - 在 Linux 上分发 .so 文件?

c - Linux shell 不执行我的 c 程序

c - 将标准输入重定向到简单 C 行阅读器应用程序的 csh 脚本时出现问题

php - PHP 中最昂贵的操作?

涉及Glib2库的C程序编译问题?

CreateFile/readfile & writefile 多线程 - 无效句柄

sql-server - SQL Server - Management Studio - 客户端统计 - 服务器回复等待时间与客户端处理时间

c - 在 ubuntu 中使用 gcc 生成特定频率的声音?

c++ - C程序生成运行时日志