هل توفر الكلمة الرئيسية تقييد فوائد كبيرة في مجلس التعاون الخليجي/G ++؟

StackOverflow https://stackoverflow.com/questions/1965487

سؤال

هل شاهد أي شخص أي أرقام/تحليل حول ما إذا كان استخدام 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. يبدو مثله يستطيع حفظ بعض زمن.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top