当前位置:  开发笔记 > 编程语言 > 正文

restrict关键字在C++中的含义是什么?

如何解决《restrict关键字在C++中的含义是什么?》经验,为你挑选了5个好方法。

我总是不确定,在C++中,restrict关键字是什么意思?

是否意味着赋予函数的两个或更多指针不重叠?还有什么意思?



1> Robert S. Ba..:

在他的论文" 内存优化"中,Christer Ericson说,虽然restrict它还不是C++标准的一部分,但它得到了许多编译器的支持,他建议在可用时使用它:

限制关键字

!1999年ANSI/ISO C标准的新成员

!尚未使用C++标准,但许多C++编译器都支持它

!只有提示,所以可能什么都不做,仍然符合要求

限制合格的指针(或参考)......

!...基本上是对编译器的承诺,对于指针的范围,指针的目标只能通过该指针(以及从中复制的指针)访问.

在支持它的C++编译器中,它应该与C中的行为相同.

有关详细信息,请参阅此SO帖子: C99'conctrict'关键字的实际用法?

花半个小时浏览Ericson的论文,这很有趣,值得花时间.

编辑

我还发现IBM的AIX C/C++编译器支持该__restrict__关键字.

g ++似乎也支持这个,因为下面的程序在g ++上完全编译:

#include 

int foo(int * __restrict__ a, int * __restrict__ b) {
    return *a + *b;
}

int main(void) {
    int a = 1, b = 1, c;

    c = foo(&a, &b);

    printf("c == %d\n", c);

    return 0;
}

我还发现了一篇关于使用的好文章restrict:

揭开限制关键词的神秘面纱

EDIT2

我碰到了一篇专门讨论在C++程序中使用restrict的文章:

加载命中存储和__restrict关键字

此外,Microsoft Visual C++ 还支持该__restrict关键字.


Memory Optimization论文链接已经死了,这里是他GDC演示中音频的链接.http://www.gdcvault.com/play/1022689/Memory

2> Ciro Santill..:

正如其他人所说,如果C++ 14中没有任何意义,那么让我们考虑一下__restrict__与C99相同的GCC扩展restrict.

C99

restrict说两个指针不能指向重叠的内存区域.最常见的用法是函数参数.

这限制了函数的调用方式,但允许更多的编译优化.

如果调用者不遵循restrict合同,则定义未定义的行为.

该C99 N1256草案 6.7.3/7 "类型的限定"说:

restrict限定符(如寄存器存储类)的预期用途是促进优化,并且从构成符合程序的所有预处理转换单元中删除限定符的所有实例不会改变其含义(即,可观察行为).

和6.7.3.1"限制的正式定义"给出了血淋淋的细节.

可能的优化

的维基百科例子是非常照明.

它清楚地显示了如何允许保存一个汇编指令.

没有限制:

void f(int *a, int *b, int *x) {
  *a += *x;
  *b += *x;
}

伪装配:

load R1 ? *x    ; Load the value of x pointer
load R2 ? *a    ; Load the value of a pointer
add R2 += R1    ; Perform Addition
set R2 ? *a     ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because a may be equal to x.
load R1 ? *x
load R2 ? *b
add R2 += R1
set R2 ? *b

有限制:

void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x);

伪装配:

load R1 ? *x
load R2 ? *a
add R2 += R1
set R2 ? *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; load R1 ? *x
load R2 ? *b
add R2 += R1
set R2 ? *b

海湾合作委员会真的这样做吗?

g++ 4.8 Linux x86-64:

g++ -g -std=gnu++98 -O0 -c main.cpp
objdump -S main.o

-O0,他们是一样的.

-O3:

void f(int *a, int *b, int *x) {
    *a += *x;
   0:   8b 02                   mov    (%rdx),%eax
   2:   01 07                   add    %eax,(%rdi)
    *b += *x;
   4:   8b 02                   mov    (%rdx),%eax
   6:   01 06                   add    %eax,(%rsi)  

void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x) {
    *a += *x;
  10:   8b 02                   mov    (%rdx),%eax
  12:   01 07                   add    %eax,(%rdi)
    *b += *x;
  14:   01 06                   add    %eax,(%rsi) 

对于没有经验的人,调用约定是:

rdi =第一个参数

rsi =第二个参数

rdx =第三个参数

GCC输出甚至比wiki文章更清晰:4条指令vs 3条指令.

数组

到目前为止,我们有单指令节省,但如果指针表示要循环的数组,一个常见的用例,那么可以保存一堆指令,如supercat和michael所述.

考虑例如:

void f(char *restrict p1, char *restrict p2, size_t size) {
     for (size_t i = 0; i < size; i++) {
         p1[i] = 4;
         p2[i] = 9;
     }
 }

因为restrict,智能编译器(或人类)可以优化它:

memset(p1, 4, size);
memset(p2, 9, size);

哪个可能更高效,因为它可能在一个体面的libc实现(如glibc)上进行程序集优化?在性能方面使用std :: memcpy()或std :: copy()会更好吗?,可能有SIMD指令.

没有,限制,这种优化无法完成,例如考虑:

char p1[4];
char *p2 = &p1[1];
f(p1, p2, 3);

然后for版本:

p1 == {4, 4, 4, 9}

memset版本使:

p1 == {4, 9, 9, 9}

海湾合作委员会真的这样做吗?

GCC 5.2.1.Linux x86-64 Ubuntu 15.10:

gcc -g -std=c99 -O0 -c main.c
objdump -dr main.o

-O0,两者都是一样的.

-O3:

限制:

3f0:   48 85 d2                test   %rdx,%rdx
3f3:   74 33                   je     428 
3f5:   55                      push   %rbp
3f6:   53                      push   %rbx
3f7:   48 89 f5                mov    %rsi,%rbp
3fa:   be 04 00 00 00          mov    $0x4,%esi
3ff:   48 89 d3                mov    %rdx,%rbx
402:   48 83 ec 08             sub    $0x8,%rsp
406:   e8 00 00 00 00          callq  40b 
                        407: R_X86_64_PC32      memset-0x4
40b:   48 83 c4 08             add    $0x8,%rsp
40f:   48 89 da                mov    %rbx,%rdx
412:   48 89 ef                mov    %rbp,%rdi
415:   5b                      pop    %rbx
416:   5d                      pop    %rbp
417:   be 09 00 00 00          mov    $0x9,%esi
41c:   e9 00 00 00 00          jmpq   421 
                        41d: R_X86_64_PC32      memset-0x4
421:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
428:   f3 c3                   repz retq

两个memset电话如预期.

没有限制:没有stdlib调用,只是一个16迭代宽的循环展开,我不打算在这里重现:-)

我没有耐心对它们进行基准测试,但我相信限制版本会更快.

严格别名规则

restrict关键字仅影响兼容类型的指针(例如两个int*),因为严格的别名规则表明,默认情况下,别名不兼容类型是未定义的行为,因此编译器可以假设它不会发生并优化掉.

请参阅:什么是严格别名规则?

它适用于参考?

根据海湾合作委员会的文件,它确实:https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.html语法:

int &__restrict__ rref

甚至还有this成员函数的版本:

void T::fn () __restrict__



3> dirkgently..:

没有.它被添加到C99标准中.


@dirkgently:尽管如此,为什么不呢?许多项目都与特定或非常少的编译器支持的特定非标准语言扩展相关联.想到了Linux内核和gcc.在项目的整个使用寿命期间,坚持使用特定的编译器,甚至是特定编译器的特定修订版并不罕见.并非每个程序都需要严格遵守.
@Robert S Barnes:C++标准不承认`restrict`为关键字.因此,我的答案是正确的.您所描述的是特定于实现的行为以及您不应该*真正依赖的行为.
@Rpbert S. Barnes:问题是c ++.不是MSVC,不是gcc,不是AIX.如果acidzombie24需要编译器特定的扩展,那么他应该说/标记为.
这不完全正确.显然它受到一些C++编译器的支持,有些人强烈建议它在可用时使用,请参阅下面的答案.
@Rpbert S. Barnes:我不可能再强调为什么你不应该依赖于特定于实现的行为.至于Linux和gcc - 想想,你会明白为什么它们不是你防守的好例子.我还没有看到即使是一个适度成功的软件在_single_编译器版本上运行它的生命周期.
@dirkgently Steam.
@dirkgently Linux,Word,Chrome

4> unwind..:

这是添加此关键字的原始提案.尽管如此指出,这是一个C99功能; 它与C++无关.


许多C++编译器支持`__restrict__`关键字,就我所知,它是完全相同的.

5> AnT..:

C++中没有这样的关键字.C++关键字列表可以在C++语言标准的2.11/1节中找到.restrict是C语言的C99版本中的关键字,而不是C++中的关键字.


@Robert:但是**在C++中没有这样的关键字**.个别编译器所做的是他们自己的业务,但它不是C++语言的一部分.
许多C++编译器支持`__restrict__`关键字,就我所知,它是完全相同的.
推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有