هل توفر الكلمة الرئيسية تقييد فوائد كبيرة في مجلس التعاون الخليجي/G ++؟
-
21-09-2019 - |
سؤال
هل شاهد أي شخص أي أرقام/تحليل حول ما إذا كان استخدام C/C ++ أم لا restrict
توفر الكلمة الرئيسية في GCC/G ++ الفعلي أي زيادة كبيرة في الأداء في الواقع (وليس فقط من الناحية النظرية)؟
لقد قرأت العديد من المقالات التي توصي / استخلاص استخدامها ، لكنني لم أواجه أي أرقام حقيقية تظهر عملياً أي من الجانبين.
تعديل
وأنا أعلم ذلك restrict
ليس جزءًا رسميًا من C ++ ، لكنه مدعوم من قبل بعض المترجمين وقرأت ورقة بواسطة Christer Ericson الذي يوصي بقوة استخدامه.
المحلول
الكلمة الرئيسية تقييد الفرق.
لقد رأيت تحسينات في العامل 2 وأكثر في بعض المواقف (معالجة الصور). معظم الوقت لا يكون الفرق كبيرًا. حوالي 10٪.
فيما يلي مثال صغير يوضح الفرق. لقد كتبت تحويل مصفوفة 4x4 أساسيًا للغاية * كاختبار. لاحظ أنه يجب علي إجبار الوظيفة على عدم إثارة. بخلاف ذلك ، تكتشف GCC أنه لا توجد أي مؤشرات مستعملة في الرمز القياسي الخاص بي ولن تحدث تقييد فرقًا بسبب الإطار.
كان بإمكاني نقل وظيفة التحويل إلى ملف مختلف أيضًا.
#include <math.h>
#ifdef USE_RESTRICT
#else
#define __restrict
#endif
void transform (float * __restrict dest, float * __restrict src,
float * __restrict matrix, int n) __attribute__ ((noinline));
void transform (float * __restrict dest, float * __restrict src,
float * __restrict matrix, int n)
{
int i;
// simple transform loop.
// written with aliasing in mind. dest, src and matrix
// are potentially aliasing, so the compiler is forced to reload
// the values of matrix and src for each iteration.
for (i=0; i<n; i++)
{
dest[0] = src[0] * matrix[0] + src[1] * matrix[1] +
src[2] * matrix[2] + src[3] * matrix[3];
dest[1] = src[0] * matrix[4] + src[1] * matrix[5] +
src[2] * matrix[6] + src[3] * matrix[7];
dest[2] = src[0] * matrix[8] + src[1] * matrix[9] +
src[2] * matrix[10] + src[3] * matrix[11];
dest[3] = src[0] * matrix[12] + src[1] * matrix[13] +
src[2] * matrix[14] + src[3] * matrix[15];
src += 4;
dest += 4;
}
}
float srcdata[4*10000];
float dstdata[4*10000];
int main (int argc, char**args)
{
int i,j;
float matrix[16];
// init all source-data, so we don't get NANs
for (i=0; i<16; i++) matrix[i] = 1;
for (i=0; i<4*10000; i++) srcdata[i] = i;
// do a bunch of tests for benchmarking.
for (j=0; j<10000; j++)
transform (dstdata, srcdata, matrix, 10000);
}
النتائج: (على ثنائي الأساسي 2 جيجاهرتز)
nils@doofnase:~$ gcc -O3 test.c
nils@doofnase:~$ time ./a.out
real 0m2.517s
user 0m2.516s
sys 0m0.004s
nils@doofnase:~$ gcc -O3 -DUSE_RESTRICT test.c
nils@doofnase:~$ time ./a.out
real 0m2.034s
user 0m2.028s
sys 0m0.000s
على الإبهام 20 ٪ تنفيذ أسرع ، على الذي - التي النظام.
لإظهار مقدار ما يعتمد على الهندسة المعمارية ، تركت نفس الكود يعمل على وحدة المعالجة المركزية المضمنة في قشرة القشرة-A8 (تعديل عدد الحلقة قليلاً لأنني لا أريد الانتظار لفترة طويلة):
root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp test.c
root@beagleboard:~# time ./a.out
real 0m 7.64s
user 0m 7.62s
sys 0m 0.00s
root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -DUSE_RESTRICT test.c
root@beagleboard:~# time ./a.out
real 0m 7.00s
user 0m 6.98s
sys 0m 0.00s
هنا الفرق هو 9 ٪ فقط (نفس المترجم راجع للشغل.)
نصائح أخرى
هل توفر الكلمة الرئيسية تقييد فوائد كبيرة في مجلس التعاون الخليجي / G ++؟
هو - هي يستطيع قلل من عدد الإرشادات كما هو موضح في المثال أدناه ، لذا استخدمه كلما كان ذلك ممكنًا.
GCC 4.8 Linux X86-64 Exmample
إدخال:
void f(int *a, int *b, int *x) {
*a += *x;
*b += *x;
}
void fr(int *restrict a, int *restrict b, int *restrict x) {
*a += *x;
*b += *x;
}
ترجمة وتفكك:
gcc -g -std=c99 -O0 -c main.c
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
= المعلمة الثالثة
استنتاج: 3 تعليمات بدلا من 4.
بالطبع ، التعليمات يمكن أن يكون لها اختلافات مختلفة, ، لكن هذا يعطي فكرة جيدة.
لماذا تمكنت مجلس التعاون الخليجي من تحسين ذلك؟
تم أخذ الرمز أعلاه من مثال ويكيبيديا الذي جداً إضاءة.
التجميع الزائف ل f
:
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
إلى عن على fr
:
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
هل هو حقا أي أسرع؟
ermmm ... ليس لهذا الاختبار البسيط:
.text
.global _start
_start:
mov $0x10000000, %rbx
mov $x, %rdx
mov $x, %rdi
mov $x, %rsi
loop:
# START of interesting block
mov (%rdx),%eax
add %eax,(%rdi)
mov (%rdx),%eax # Comment out this line.
add %eax,(%rsi)
# END ------------------------
dec %rbx
cmp $0, %rbx
jnz loop
mov $60, %rax
mov $0, %rdi
syscall
.data
x:
.int 0
وثم:
as -o a.o a.S && ld a.o && time ./a.out
على Ubuntu 14.04 AMD64 CPU Intel I5-3210M.
أعترف أنني ما زلت لا أفهم وحدات المعالجة المركزية الحديثة. اسمحوا لي أن أعرف إذا كنت:
- وجدت عيب في طريقتي
- وجدت حالة اختبار التجميع حيث تصبح أسرع بكثير
- فهم لماذا لم يكن هناك فرق
المقالة إزالة الغموض عن الكلمة الرئيسية تقييد يشير إلى الورقة لماذا يعد الاسم المستعار المحدد للمبرمج فكرة سيئة (PDF) الذي يقول أنه لا يساعد بشكل عام ويوفر قياسات لدعم هذا الأمر.
لاحظ أن مجمعات C ++ التي تسمح restrict
قد لا تزال الكلمة الرئيسية تجاهلها. هذا هو الحال على سبيل المثال هنا.
اختبرت هذه C-Program. بدون restrict
استغرق الأمر 12.640 ثانية لإكمال ، مع restrict
12.516. يبدو مثله يستطيع حفظ بعض زمن.