【问题标题】:Strange assembly output when optimizing string hashing优化字符串散列时奇怪的汇编输出
【发布时间】:2020-07-07 09:25:33
【问题描述】:

当试图创建一个编译时哈希宏时,它可以工作,但它有问题。所以我想如果字符串在编译时是已知的(它们是),整个散列应该被优化为一个常数。 此 gcc C99 代码启用了优化级别 -O3:

#include <stdio.h>


int main(void)
{
    char const *const string = "hello";
    int hash = 0;
    for (unsigned char i=0; i < sizeof string; ++i)
    {
        hash += string[i]; //reeaally simple hash :)
    }

    printf("%i", hash);
    return 0;
}

生成以下汇编代码:

.LC0:
        .string "hello"
.LC1:
        .string "%i"
main:
        sub     rsp, 8
        movsx   eax, BYTE PTR .LC0[rip+6]
        movsx   edx, BYTE PTR .LC0[rip+7]
        mov     edi, OFFSET FLAT:.LC1
        lea     esi, [rax+532+rdx]
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 8
        ret

虽然相同代码,我只是将“hello”更改为“hello w”,生成了这个汇编代码,它完全优化了散列:

.LC0:
        .string "%i"
main:
        sub     rsp, 8
        mov     esi, 683
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 8
        ret

Try it yourself

是什么原因?这是否意味着我不能使用这种散列方式,因为开销可能不会得到优化?我怎样才能确保不会有任何开销,有什么替代方案?

编辑 1: 我玩了一下,似乎如果字符串中的字符数是 6,如果字符数是 7,它就不会被优化掉,它会

【问题讨论】:

  • 是的,这很奇怪。我的意思是……它们都应该被优化,因为这里的一切都是不变的。
  • 用叮当它工作。问题是这个散列宏是库的一部分。这意味着如果您没有 clang 并包含该库,那么您的整个程序可能会比我奶奶运行得慢
  • 尝试以下字符串:“1234”、“12345”和“123456”。你会看到你得到三个不同的结果。优化器正在工作,它根据字符串的长度选择不同的策略。
  • 我有点认为这是展开循环的一些硬编码限制,但它似乎与sizeof 有关。如果将其设为 20 个字符的字符串并将循环条件硬编码为 i &lt; 20,它仍然是优化的。编辑:或者...更好的是,如果您包含string.h 并使用strlen,您也会保持优化。
  • 如果你使用了unsigned char,你的编译器理论上可以使用psadbw优化它,以获得8或16字节字符串块的水平总和。 (实际上它仍然是有符号字节的胜利,但需要进行一些预处理。)

标签: c string assembly optimization


【解决方案1】:

sizeof 在这里是错误的。它返回字符指针的大小而不是字符串的长度。

在您的情况下,它是一个 UB,当您在字符串文字范围之外读取时,编译器无法对其进行优化。这是一个clang错误而不是功能。

如果你做得好,gcc 也会优化它

int main(void)
{
    char const string[] = "hello";
    int hash = 0;
    for (unsigned char i=0; i < sizeof(string); ++i)
    {
        hash += string[i]; //reeaally simple hash :)
    }

    printf("%i", hash);
    return 0;
}

https://godbolt.org/z/YCCNCt

【讨论】:

  • 我同意这是问题所在,但它没有优化它仍然有点奇怪,因为 sizeof(char*) 应该是恒定的......
  • 编译器如何知道已知数据末尾的内容?
猜你喜欢
  • 1970-01-01
  • 2021-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-02
  • 2014-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多