ما هي السرعة النسبية للنقطة العائمة إضافة مقابل النقطة العائمة تتضاعف

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

سؤال

منذ عقد أو شهرين، كان من المفيد كتابة رمز رقمي لتجنب استخدام الممرضات والتقسيم واستخدام الإضافة والطرح بدلا من ذلك. مثال جيد يستخدم إلى الأمام الاختلافات لتقييم منحنى متعدد الحدود بدلا من حساب متعدد الحدود مباشرة.

هل هذا لا يزال هذا هو الحال، أو تقدم بتصميم هياكل الكمبيوتر الحديثة إلى النقطة التي *، / لم تعد بأكبر عدة مرات أبطأ من +، -؟

لتكون محددة، أنا مهتم برمز C / C ++ مجمعة تعمل على رقائق X86 النموذجية الحديثة مع أجهزة نقطة عائمة واسعة النطاق، وليس صغيرا صغيرا في محاولة للقيام FP في البرنامج. أدرك أن خطوط الأنابيب وغيرها من التحسينات المعمارية دون تخصيص دورة معينة، ولكن ما زلت أحب الحصول على حدس مفيد.

هل كانت مفيدة؟

المحلول

كما يعتمد على مزيج التعليمات. سيكون لدى المعالج الخاص بك العديد من وحدات حساب يقف في أي وقت، وستحصل على أقصى إنتاجية إذا تم إملاء كل منهم طوال الوقت. لذا، فإن تنفيذ حلقة من Mul هو بأسرع سرعة تنفيذ حلقة أو يضيف - ولكن الشيء نفسه لا يحمل إذا أصبح التعبير أكثر تعقيدا.

على سبيل المثال، خذ هذه الحلقة:

for(int j=0;j<NUMITER;j++) {
  for(int i=1;i<NUMEL;i++) {
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ;
  }
}

بالنسبة للرقم = 10 ^ 7، Numel = 10 ^ 2، تتم تهيئة كلا من صفيفات تهيئة بأرقام إيجابية صغيرة (NAN أبطأ بكثير)، ويستغرق هذا 6.0 ثانية باستخدام الزوجي على بروك 64 بت. إذا استبدلت الحلقة

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ;

يستغرق الأمر سوى 1.7 ثانية ... لذلك لأننا "تجاوزنا" الإضافات، وكانت الأزود مجانية في الأساس؛ وانخفاض الإضافات ساعد. انها تحصل على مربكة أكثر:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ;

- نفس التوزيع Mul / إضافة، ولكن الآن يتم إضافة ثابتة بدلا من مضروبة في - يستغرق 3.7 ثانية. من المرجح أن يحسن المعالج الخاص بك لأداء الحسابات العددية النموذجية بشكل أكثر كفاءة؛ لذلك فإن Dot-Product مثل مبالغ الصلارات والمبالغ الكبيرة حول جيدة كما يحصل؛ مضمد الثوابت غير شائع تقريبا، لذلك هذا أبطأ ...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/

يستغرق مرة أخرى 1.7 ثانية.

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/

(نفس الحلقة الأولية، ولكن دون إضافة ثابتة مكلفة: 2.1 ثانية)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/

(معظمها من الأجرة، ولكن إضافة واحدة: 1.9 ثانية)

لذلك، أساسا؛ من الصعب القول الذي هو أسرع، ولكن إذا كنت ترغب في تجنب الاختناقات، فمن الأهم من ذلك هو الحصول على مزيج عاقل، وتجنب NAN أو INF، وتجنب إضافة ثوابت. مهما فعلت، تأكد من اختبارك، واختبار إعدادات التحويل البرمجي المختلفة، حيث يمكن أن تحدث تغييرات صغيرة في كثير من الأحيان الفرق.

بعض الحالات الأخرى:

bla *= someval; // someval very near 1.0; takes 2.1 seconds
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86

نصائح أخرى

من الناحية النظرية المعلومات هنا:

Intel®64 و IA-32 - دليل المراجع الأمثل لتحسين الدليل المرجعي، التذييل ج تعليمات التعليمية والإنتاجية

لكل معالج، فهي قائمة، فإن الكمون على FMUL قريب جدا من FDD أو FDIV. في بعض المعالجات القديمة، FDIV هو 2-3 وقت أبطأ من ذلك، بينما على المعالجات الأحدث، فإنه هو نفسه من الخبراء.

تحفظات:

  1. تقول الوثيقة التي ربطتها بالفعل أنك لا تستطيع الاعتماد على هذه الأرقام في الحياة الحقيقية لأن المعالج سيفعل ما يرغب في جعل الأمور أسرع إذا كان ذلك صحيحا.

  2. هناك فرصة جيدة سيقرر برنامج التحويل البرمجي استخدام أحد مجموعات التعليمات الأحدث العديدة التي تحتوي على نقطة مضرة / تقسيم عائمة.

  3. هذا هو وثيقة معقدة فقط المقرر أن تقرأ عن طريق محول البرمجة، وقد أكون قد أخطأت. وكأنني لست واضحا لماذا رقم تشمئط FDIV مفقود تماما لبعض وحدات المعالجة المركزية.

أفضل طريقة للإجابة على هذا السؤال هي كتابة معيار / ملف تعريف للمعالجة التي تحتاج إليها. يجب استخدام تجريبي على النظري عندما يكون ذلك ممكنا. خاصة عندما يسهل الوصول إليها.

إذا كنت تعرف بالفعل تطبيقات مختلفة من الرياضيات، فأنت بحاجة إلى القيام به، فيمكنك كتابة بعض القليل من تحويل التعليمات البرمجية المختلفة للرياضيات ومعرفة أين قمم أداءك. سيسمح ذلك بالمعالج / المحول البرمجي لإنشاء تدفقات تنفيذ مختلفة لملء خطوط أنابيب المعالج وتعطيك إجابة ملموسة لإجابتك.

إذا كنت مهتما على وجه التحديد أداء إرشادات DIV / MUL / ADD / SUB ومع ذلك، تحتاج إلى التأكد من أنك تبقي وحدات التنفيذ المتعدد مشغولة للحصول على فكرة جيدة عن الأداء، وهو نظام قادر على ذلك.

قم أيضا بعمل شيء من هذا القبيل من شأنه أن يسمح لك بمقارنة الأداء في اختلافات متعددة من المعالج من خلال تشغيل نفس البرنامج ببساطة، ويمكن أن يسمح لك أيضا بالعمل في اختلافات اللوحة الأم.

يحرر:

الهندسة المعمارية الأساسية ل A + - متطابقة. لذلك يأخذون منطقيا نفس الوقت لحساب. * من ناحية أخرى، تتطلب طبقات متعددة، شيدت عادة من "المبات الكامل" لإكمال عملية واحدة. هذا الدجاج الذي يمكن إصداره في الوقت الذي يمكن إصداره على خط الأنابيب كل دورة سيكون لها زون أعلى من دائرة إضافة / طرح. عادة ما يتم تنفيذ FP / تشغيل باستخدام طريقة تقريبية تتقارب بشكل متكرر نحو الإجابة الصحيحة مع مرور الوقت. يتم تنفيذ هذه الأنواع من التقريبات عادة عبر الضرب. لذلك بالنسبة إلى النقطة العائمة، يمكنك عموما أن تستغرق الانقسام وقتا أطول لأنه غير عملي ل "drull" المضاعف (وهو بالفعل دائرة كبيرة في الذات وعن الذات) في خط أنابيب من العديد من الدوائر المضاعفة. ومع ذلك، يتم قياس أداء نظام معين من خلال الاختبار.

لا يمكنني العثور على مرجع نهائي، لكن التجربة الواسعة تخبرني أن الضرب العائم في الوقت الحاضر في الوقت الحاضر تقريبا بنفس السرعة مثل الإضافة والطرح، في حين أن القسم ليس (ولكن ليس "مرات عديدة" أبطأ، إما). يمكنك الحصول على الحدس الذي تريده فقط من خلال تشغيل تجاربك الخاصة - تذكر أن توليد الأرقام العشوائية (الملايين منها) مقدما، وقراءتها قبل البدء في توقيت، واستخدام عدادات أداء وحدة المعالجة المركزية (مع عدم وجود عملية أخرى تعمل، كما بقدر ما يمكنك إيقافها من) للقياس الدقيق!

الفرق السرعة من * / vs + - يعتمد على بنية المعالج الخاص بك. بشكل عام ومع X86 على وجه الخصوص أصبح اختلاف السرعة أقل مع المعالجات الحديثة. * يجب أن يكون قريبا من +، عندما تكون في شك: مجرد تجربة. إذا كانت لديك مشكلة صعبة حقا مع الكثير من عمليات FP تنظر في استخدام GPU الخاص بك (GeForce، ...) التي تعمل كمعالج متجه.

ربما يكون هناك اختلاف بسيط للغاية في الوقت المناسب بين الضرب والإضافة. القسم من ناحية أخرى لا يزال أبطأ بشكل كبير ثم الضرب بسبب طبيعته العودية. يجب مراعاة تعليمات SSE العادية الحديثة X86 عند القيام بعملية نقطة عائمة بدلا من ذلك باستخدام FPU.Though يجب أن يمنحك مترجم C / C ++ جيد خيار استخدام SSE بدلا من FPU.

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