首页 文章

在现代(流水线/超标量)处理器上执行x86 rep指令

提问于
浏览
15

我最近一直在写x86程序集(为了好玩),并且想知道rep前缀字符串指令是否实际上在现代处理器上具有性能优势,或者它们是否仅用于后向兼容性 .

我理解为什么当处理器一次只运行一条指令时,英特尔最初会实现rep指令,但是现在使用它们有什么好处呢?

通过循环可以编译更多指令,还有更多要填充管道和/或无序发布 . 现代处理器是为优化这些重复前缀指令而构建的,还是在现代代码中很少使用的rep指令,它们对制造商来说并不重要?

3 回答

  • 0

    在AMD和英特尔的优化指南中,这样的问题都有很多空间 . 在该领域给出的建议的有效性具有“半衰期” - 不同的CPU代表行为不同,例如:

    Intel Architecture Optimization Manual给出了表7-2中各种块复制技术(包括 rep stosd )的性能比较数据 . 内存复制例程的相对性能,pg . 7-37f . ,对于不同的CPU,再一次在其他CPU上最快的可能不是最快的 .

    在许多情况下,最近的x86 CPU(具有"string" SSE4.2操作)可以通过SIMD单元执行字符串操作,请参阅this investigation .

    要跟进所有这些(和/或当事情再次发生变化时不断更新),请阅读Agner Fog's Optimization guides/blogs .

  • 33

    既然没有人给你任何数字,我会给你一些我通过对我的垃圾收集器进行基准测试而发现的垃圾收集器 . 我要复制的对象长度为60%16字节,其余30%为500 - 8000字节左右 .

    • 前提条件: dstsrcn 都是8的倍数 .

    • 处理器:AMD Phenom(tm)II X6 1090T处理器64位/ linux

    以下是我的三个 memcpy 变体:

    手动编码while循环:

    if (n == 16) {
        *dst++ = *src++;
        *dst++ = *src++;
    } else {
        size_t n_ptrs = n / sizeof(ptr);
        ptr *end = dst + n_ptrs;
        while (dst < end) {
            *dst++ = *src++;
        }
    }
    

    ptruintptr_t 的别名) . 时间:101.16%

    rep movsb

    if (n == 16) {
        *dst++ = *src++;
        *dst++ = *src++;
    } else {
        asm volatile("cld\n\t"
                     "rep ; movsb"
                     : "=D" (dst), "=S" (src)
                     : "c" (n), "D" (dst), "S" (src)
                     : "memory");
    }
    

    时间:103.22%

    rep movsq

    if (n == 16) {
        *dst++ = *src++;
        *dst++ = *src++;
    } else {
        size_t n_ptrs = n / sizeof(ptr);
        asm volatile("cld\n\t"
                     "rep ; movsq"
                     : "=D" (dst), "=S" (src)
                     : "c" (n_ptrs), "D" (dst), "S" (src)
                     : "memory");
    }
    

    时间:100.00%

    req movsq 以微弱优势获胜 .

  • 8

    除了FrankH的优秀答案;我想指出哪种方法最好也取决于字符串的长度,它的对齐方式,以及长度是固定的还是可变的 .

    对于小字符串(可能高达约16个字节),使用简单指令手动执行它可能更快,因为它避免了更复杂技术的设置成本(并且对于固定大小的字符串可以很容易地展开) . 对于中等大小的字符串(可能从16字节到4 KiB),类似“REP MOVSD”(如果可能出现未对准,则会引入一些“MOVSB”指令)可能是最好的 .

    对于任何大于此的东西,有些人会被诱惑进入SSE / AVX和预取等 . 更好的想法是修复调用者/ s以便复制(或strlen()或其他)几乎总是找不到办法 . 注意:也要非常警惕"supposed"快速mempcy()例程 - 通常它们已经在大量字符串上进行了测试,并且没有在更可能的微小/小/中等字符串上进行测试 .

    还要注意(出于优化而不是方便的目的)由于所有这些差异(可能是长度,对齐,固定或可变大小,CPU类型等),为所有人提供一个多用途“memcpy()”的想法不同的情况是近视的 .

相关问题