【问题标题】:what stops GCC __restrict__ qualifier from working是什么阻止了 GCC __restrict__ 限定符的工作
【发布时间】:2016-02-29 12:32:38
【问题描述】:

这是一些相当简单的代码,使用 -O2 (gcc 4.8.5) 编译:

unsigned char  * linebuf;
int yuyv_tojpegycbcr(unsigned char * buf, int w)
{
    int  col;
    unsigned char * restrict pix = buf;
    unsigned char * restrict line = linebuf;

    for(col = 0; col < w - 1; col +=2)
    {
            line[col*3] = pix[0];
            line[col*3 + 1] = pix[1];
            line[col*3 + 2] = pix[3];
            line[col*3 + 3] = pix[2];
            line[col*3 + 4] = pix[1];
            line[col*3 + 5] = pix[3];
            pix += 4;
    }
    return 0;
}

这是相应的程序集:

0000000000000000 <yuyv_tojpegycbcr>:
   0:   83 fe 01                cmp    $0x1,%esi
   3:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax        # a <yuyv_tojpegycbcr+0xa>
   a:   7e 4e                   jle    5a <yuyv_tojpegycbcr+0x5a>
   c:   83 ee 02                sub    $0x2,%esi
   f:   31 d2                   xor    %edx,%edx
  11:   d1 ee                   shr    %esi
  13:   48 8d 74 76 03          lea    0x3(%rsi,%rsi,2),%rsi
  18:   48 01 f6                add    %rsi,%rsi
  1b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  20:   0f b6 0f                movzbl (%rdi),%ecx
  23:   48 83 c2 06             add    $0x6,%rdx
  27:   48 83 c7 04             add    $0x4,%rdi
  2b:   48 83 c0 06             add    $0x6,%rax
  2f:   88 48 fa                mov    %cl,-0x6(%rax)
  32:   0f b6 4f fd             movzbl -0x3(%rdi),%ecx
  36:   88 48 fb                mov    %cl,-0x5(%rax)
  39:   0f b6 4f ff             movzbl -0x1(%rdi),%ecx
  3d:   88 48 fc                mov    %cl,-0x4(%rax)
  40:   0f b6 4f fe             movzbl -0x2(%rdi),%ecx
  44:   88 48 fd                mov    %cl,-0x3(%rax)
  47:   0f b6 4f fd             movzbl -0x3(%rdi),%ecx
  4b:   88 48 fe                mov    %cl,-0x2(%rax)
  4e:   0f b6 4f ff             movzbl -0x1(%rdi),%ecx
  52:   88 48 ff                mov    %cl,-0x1(%rax)
  55:   48 39 f2                cmp    %rsi,%rdx
  58:   75 c6                   jne    20 <yuyv_tojpegycbcr+0x20>
  5a:   31 c0                   xor    %eax,%eax
  5c:   c3                      retq   

在没有限制限定符的情况下编译时,输出是相同的: 大量混合加载和存储。一些值被加载了两次,看起来没有优化。如果 pixline 没有别名,我希望编译器足够聪明,除此之外,只加载一次 pix[1] 和 pix[3]。

您知道任何可能取消restrict 限定符的资格吗?

PS: 使用较新的 gcc (4.9.2),在另一个架构 (arm v7) 上,结果是相似的。这是一个测试脚本,用于比较生成的代码有无限制。

#!/bin/sh
gcc -c -o test.o -std=c99 -O2 yuyv_to_jpegycbcr.c
objdump -d test.o > test.S


gcc -c -o test2.o -O2 -D restrict='' yuyv_to_jpegycbcr.c
objdump -d test2.o > test2.S

【问题讨论】:

  • 有什么理由不使用标准的restrict 限定符?
  • 因为使用 std=c99 会破坏我的代码,可能是因为我没有设置有效的 feature_test_macros。我可以解决这个问题,但我认为这不会有所作为。
  • 你也应该期待矢量化(假设你正在编译的设备支持它)。
  • 好吧,至少通过预处理器运行代码并检查它们之后是否仍然存在......
  • 预处理后仍然存在限定符。

标签: c gcc


【解决方案1】:

限制函数参数而不是局部变量。

根据我的经验,大多数编译器(包括 GCC)仅在函数参数上指定时才使用限制。函数内对局部变量的所有使用都将被忽略。

我怀疑这与在函数级别而不是基本块级别进行的别名分析有关。但我没有证据支持这一点。此外,它可能因编译器和编译器版本而异。

无论哪种方式,这些东西都很难依赖。因此,如果性能很重要,您要么手动优化它,要么记得每次升级或更改编译器时重新访问它。

【讨论】:

  • 根据gcc.gnu.org/bugzilla/show_bug.cgi?id=60712 的评论,gcc 似乎只适用于函数参数的限制
  • 我通过块矩阵乘法看到了这一点。这有六个循环。如果我把最里面的三个循环放在一个带限制参数的静态函数中,那么代码的速度是我不声明函数的两倍。我看到 GCC (6.3) 和 Clang (4.0) 的效果相同。因此,编译器似乎完全按照您所说的那样忽略了带有限制的局部变量。我不知道 ICC。
  • 甚至有时当我声明一个单独的静态函数时,编译器会给出更糟糕的结果。有时我必须在单独的目标文件中声明内部函数的限制。所以你必须照你说的做:第一次检查程序集,每次升级或编译器更改。
  • @Zboson 我已经放弃了限制语义。太不协调了。在足够的内联层之后分崩离析。现在我手动进行这些优化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-03
  • 2012-01-17
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
相关资源
最近更新 更多