เป็นคนอื่น ๆ กล่าวว่าถ้าหมายถึงอะไรที่เป็นของ C ++ 14เพื่อให้พิจารณา__restrict__
ขยาย GCC ซึ่งไม่เหมือนกันเป็น restrict
C99
C99
restrict
บอกว่าตัวชี้สองตัวไม่สามารถชี้ไปที่ส่วนของหน่วยความจำที่ทับซ้อนกันได้ การใช้งานทั่วไปส่วนใหญ่มีไว้สำหรับอาร์กิวเมนต์ของฟังก์ชัน
สิ่งนี้ จำกัด วิธีการเรียกใช้ฟังก์ชัน แต่อนุญาตให้มีการปรับแต่งคอมไพล์ได้มากขึ้น
หากผู้โทรไม่ปฏิบัติตามrestrict
สัญญาให้ระบุพฤติกรรมที่ไม่ได้กำหนดไว้
C99 N1256 ร่าง 6.7.3 / 7 "ชนิดบ่น" พูดว่า:
การใช้งานที่ จำกัด ของ qualifier ที่ จำกัด (เช่นคลาสหน่วยเก็บข้อมูลรีจิสเตอร์) คือการโปรโมตการออปติไมซ์และการลบอินสแตนซ์ทั้งหมดของ qualifier จากหน่วยการแปลก่อนการประมวลผลทั้งหมดที่ประกอบด้วยโปรแกรมที่สอดคล้องกัน
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 x may point to a (a aliased by x) thus
; the value of x will change when the value of a
; changes.
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" is no longer needed.
load R2 ← *b
add R2 += R1
set R2 → *b
GCC ทำจริงหรือ
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 คำแนะนำกับ 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 ทำจริงหรือ
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 <fr+0x38>
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 <fr+0x1b>
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 <fr+0x31>
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*
) เพราะกฎระเบียบที่เข้มงวด aliasing บอกว่า aliasing ชนิดเข้ากันไม่ได้เป็นพฤติกรรมที่ไม่ได้กำหนดโดยค่าเริ่มต้นและเพื่อให้คอมไพเลอร์สามารถสันนิษฐานได้ว่ามันไม่ได้เกิดขึ้นและเพิ่มประสิทธิภาพออกไป
ดู: กฎนามแฝงที่เข้มงวดคืออะไร
มันใช้งานได้สำหรับการอ้างอิง?
ตามเอกสาร GCC มันทำ: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.htmlด้วยไวยากรณ์:
int &__restrict__ rref
มีแม้แต่รุ่นสำหรับthis
ฟังก์ชั่นสมาชิก:
void T::fn () __restrict__
restrict
เป็นคำหลัก c99 ใช่ Rpbert__restrict__
เอสบาร์นส์ฉันรู้ว่าคอมไพเลอร์ส่วนใหญ่สนับสนุน คุณจะทราบว่าสิ่งใดก็ตามที่มีการขีดเส้นใต้สองเท่าโดยนิยามการปรับใช้เฉพาะและไม่ใช่ C ++แต่เป็นเวอร์ชันเฉพาะของคอมไพเลอร์