سؤال

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

ما يعطي؟ إن كلاهما صغير (3 عوامات للمتجه، 2 ناقلات راي)، لا يتم نسخها بشكل مفرط. أقوم بتمريرهم إلى طرق عند الحاجة بالطبع، ولكن هذا أمر لا مفر منه. إذن ما هي المزالق الشائعة التي تقتل الأداء عند استخدام الهياكل؟ أنا قد قرأت هذه مقالة MSDN التي تقول ما يلي:

عند تشغيل هذا المثال، سترى أن حلقة الهيكل هي أوامر من حيث الحجم بشكل أسرع. ومع ذلك، من المهم احذر استخدام Valuetypes عند التعامل معها مثل الكائنات. يضيف هذا مرفقا إضافيا من الملاكمة والعلوم العامة إلى البرنامج الخاص بك، ويمكن أن ينتهي بك الأمر إلى أن تكلفك أكثر مما لو كنت قد علقت بالكائنات! لمعرفة ذلك في العمل، قم بتعديل التعليمات البرمجية أعلاه لاستخدام مجموعة من FOOS و BARS. ستجد أن الأداء يساوي أكثر أو أقل.

ومع ذلك، يبدو الأمر قديما جدا (2001) وكال "وضعها في مجموعة أسباب الملاكمة / إلغاء الصندوق" ضربني غريب. هل هذا صحيح؟ ومع ذلك، قمت بحساب الأشعة الأساسية مسبقا ووضعها في صفيف، لذلك تناولت هذه المقالة وتحسب الشعاع الرئيسي عندما كنت بحاجة إليه ولم أضيفها أبدا إلى صفيف، لكنها لم تغير أي شيء: مع الطبقات، كان لا يزال 1.5x أسرع.

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

لذلك أساسا: أي نصائح، أشياء يجب مراعاتها وما يجب تجنبها؟

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

public static VectorStruct Add(VectorStruct v1, VectorStruct v2)
{
  return new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}

public static VectorStruct Add(ref VectorStruct v1, ref VectorStruct v2)
{
  return new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}

public static void Add(ref VectorStruct v1, ref VectorStruct v2, out VectorStruct v3)
{
  v3 = new VectorStruct(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}

لكل مني حصلت على تباين من طريقة القياسية التالية:

VectorStruct StructTest()
{
  Stopwatch sw = new Stopwatch();
  sw.Start();
  var v2 = new VectorStruct(0, 0, 0);
  for (int i = 0; i < 100000000; i++)
  {
    var v0 = new VectorStruct(i, i, i);
    var v1 = new VectorStruct(i, i, i);
    v2 = VectorStruct.Add(ref v0, ref v1);
  }
  sw.Stop();
  Console.WriteLine(sw.Elapsed.ToString());
  return v2; // To make sure v2 doesn't get optimized away because it's unused. 
}

كل شيء يبدو أن يؤدي متطابقة إلى حد كبير. هل من الممكن أن يحسنوا من قبل الجبهة إلى أي شيء هو الطريقة المثلى لتمرير هذا الصياغة؟

EDIT2: يجب أن أشير بالمناسبة التي تستخدم الهياكل في مشروع الاختبار الخاص بي يكون حوالي 50٪ أسرع من استخدام الفصل. لماذا هذا يختلف عن بلدي RayTracer لا أعرف.

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

المحلول

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

مع مرور المزيد من الذاكرة حولها، لا بد أن تسبب تخزين التخزين المؤقت سحق.

نصائح أخرى

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

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


بنية

يبدو مجموعة من القيم V ترميز بواسطة بنية (نوع القيمة) مثل هذه الذاكرة:

VVVV.

صف دراسي

تبدو مجموعة من القيم V ترميز بواسطة فئة (نوع المرجع) مثل هذا:

PPPP.

..v..v ... vv.

حيث P هي هذه المؤشرات، أو المراجع، التي تشير إلى القيم الفعلية V على كومة الكومة. تشير النقاط إلى أشياء أخرى قد تكون مقترنة على كومة الكومة. في حالة الأنواع المرجعية التي تحتاج إلى الرجوع إليها V عبر P Pointing P، في حالة أنواع القيمة، يمكنك الحصول على القيمة مباشرة عبر إزاحةها في الصفيف.

في توصيات متى تستخدم بنية تقول إنه لا ينبغي أن يكون أكبر من 16 بايت. متجه الخاص بك هو 12 بايت، وهو قريب من الحد. يحتوي RAY على ناقلات اثنين، مما وضعه في 24 بايت، وهو واضح على الحد الموصى به.

عند الحصول على بنية أكبر من 16 بايت، لم يعد من الممكن نسخها بكفاءة مع مجموعة واحدة من التعليمات، بدلا من استخدام حلقة. لذلك، من خلال تمرير حد "السحر" هذا، تقوم بالفعل بعمل أكبر بكثير عند تمرير الهيكل أكثر من عند تمرير مرجع إلى كائن. هذا هو السبب في أن الكود أسرع مع فئات EventHough هناك المزيد من النفقات العامة عند تخصيص الكائنات.

يمكن أن يكون المتجه أن يكون بنية، لكن الأشعة هي ببساطة كبيرة جدا للعمل بشكل جيد مثل الهيكل.

أي شيء مكتوب فيما يتعلق بالملاكمة / Unboxing قبل أن تؤخذ .NET Generics مع شيء من حبوب الملح. تتم إزالة أنواع المجموعة العامة الحاجة إلى الملاكمة وإلغاء تثبيت أنواع القيمة، مما يجعل استخدام الهياكل في هذه الحالات أكثر قيمة.

بالنسبة لتباطؤك المحدد - ربما نحتاج إلى رؤية بعض الكود.

أعتقد أن المفتاح يكمن في هذين البياناتين من مشاركتك:

يمكنك إنشاء ملايين منهم

و

أقوم بتمريرها إلى طرق عند الحاجة بالطبع

الآن ما لم تكن بنتك أقل من أو تساوي 4 بايت في الحجم (أو 8 بايت إذا كنت على نظام 64 بت)، فأنت نسخ أكثر بكثير على كل مكالمة طريقة بعد ذلك، إذا قمت ببساطة بمرور مرجع كائن.

أول ما أبحث عنه هو التأكد من أن لديك تنفذ صراحة على قدم المساواة و gethashcode. الفشل في القيام بذلك يعني أن تنفيذ وقت التشغيل لكل منها يقوم ببعض العمليات باهظة الثمن لمقارنة مثيلات التغذية (داخليا يستخدم التفكير لتحديد كل مجال من الحقول الخاصة، ثم يتحقق منهم للمساواة، ويسبب هذا قدرا كبيرا من التخصيص) وبعد

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

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

في حين أن الوظيفة متشابهة، فإن الهياكل عادة ما تكون أكثر فعالية من الفصول الدراسية. يجب عليك تحديد هيكل، بدلا من فصل، إذا كان النوع سيؤدي بشكل أفضل كوعي قيمة من نوع مرجعي.

على وجه التحديد، يجب أن تلبي أنواع الهيكل جميع هذه المعايير:

  • يمثل منطقيا قيمة واحدة
  • لديه حجم مثيل أقل من 16 بايت
  • لن تتغير بعد الإبداع
  • لن يتم وضعها إلى نوع مرجعي

أستخدم الهياكل أساسا لكائنات المعلمة، وإرجاع قطع متعددة من المعلومات من وظيفة، ... لا شيء آخر. لا أعرف ما إذا كان "صحيح" أو "خطأ"، لكن هذا ما أقوم به.

يستخدم جهاز Ray Tracer الخاص بي أيضا ناقلات الهياكل (وإن لم يكن الأشعة) وتغيير المتجهات إلى الفصل لا يكون له أي تأثير على الأداء. أنا حاليا باستخدام ثلاثة زوجي للمتجه لذلك قد يكون أكبر مما يجب أن يكون عليه. شيء واحد يجب ملاحظة ذلك، وقد يكون هذا واضحا ولكنه لم يكن بالنسبة لي، وهذا هو تشغيل البرنامج خارج Visual Studio. حتى إذا قمت بتعيينه على إصدار الإفراج المحسن، فيمكنك الحصول على زيادة سرعة ضخمة إذا قمت ببدء تشغيل EXE خارج مقابل. يجب أن تأخذ أي معيار يمكنك القيام بذلك في الاعتبار.

إذا كانت الهياكل صغيرة، وليس هناك الكثير من الوجود في وقت واحد، فينبغي أن تضعها على المكدس (طالما أن متغير محلي وليس عضوا في الفصل) وليس على كومة الكومة، وهذا يعني أن GC لا يوجد " يجب الاحتجاج من الاحتجاج ويجب أن يكون تخصيص الذاكرة / الكتب لحظية تقريبا.

عند تمرير الهيكل كمعلمة إلى الوظيفة، يتم نسخ الهيكل، والتي لا تعني فقط المزيد من المخصصات / الوصف (من المكدس، وهو أمر فظيع تقريبا، ولكن لا يزال يحتوي على النفقات العامة)، ولكن النفقات العامة في نقل البيانات بين النسخ 2 وبعد إذا مررت عبر المرجع، فهذه مشكلة لأنك تخبرها أين تقرأها، بدلا من نسخها.

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

يمكنك أيضا جعل الهياكل في كائنات لا تطل. لن تكون الفصول المخصصة قادرة على إنشاؤها

كما

Nullable<MyCustomClass> xxx = new Nullable<MyCustomClass>

حيث مع بنية غير قابلة للنظر

Nullable<MyCustomStruct> xxx = new Nullable<MyCustomStruct>

ولكن سوف تكون (من الواضح) فقدان جميع ميزات الميراث الخاصة بك

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