سؤال

أعمل حاليًا على برنامج تتبع الأشعة في لغة C# كمشروع هواية.أحاول تحقيق سرعة عرض مناسبة من خلال تنفيذ بعض الحيل من تطبيق c++ وواجهت مشكلة.

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

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

لقد جربت عدة أشياء لتثبيت العناوين في الذاكرة ولكن لا يبدو أن أيًا منها يدوم طوال عمر التطبيق بالكامل كما أحتاج.يبدو أن الكلمة الأساسية "الثابتة" تساعد فقط أثناء استدعاءات الطريقة الفردية ولا يمكن الإعلان عن المصفوفات "الثابتة" إلا على الأنواع البسيطة التي لا تكون العقدة كذلك.هل هناك طريقة جيدة للقيام بذلك أم أنني بعيد جدًا عن مسار الأشياء التي لم يكن المقصود من C# القيام بها.

راجع للشغل، التغيير إلى c++، رغم أنه ربما يكون الخيار الأفضل لبرنامج عالي الأداء، ليس خيارًا.

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

المحلول

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

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

شيء واحد أود أن أقوله من رسالتك هو أن استخدام البنيات قد لا يساعد في الأداء كما تأمل.تفشل لغة #C في تضمين أي استدعاءات أسلوب تتضمن بنيات، وعلى الرغم من أنهم قاموا بإصلاح ذلك في أحدث نسخة تجريبية لوقت التشغيل، إلا أن البنيات لا تؤدي أداءً جيدًا في كثير من الأحيان.

أنا شخصياً أود أن أقول إن حيل C++ مثل هذه لا تميل عمومًا إلى الانتقال بشكل جيد إلى C#.ربما يتعين عليك أن تتعلم كيف تتخلى عن الأمر قليلًا؛يمكن أن يكون هناك طرق أخرى أكثر دقة لتحسين الأداء؛)

نصائح أخرى

ما الذي يفعله مدير الذاكرة الثابتة لديك بالفعل؟ما لم يكن يفعل شيئًا غير آمن (P/Invoc، رمز غير آمن)، فإن السلوك الذي تراه هو خطأ في برنامجك، وليس بسبب سلوك CLR.

ثانيًا، ماذا تقصد بـ "المؤشر" فيما يتعلق بالروابط بين الهياكل؟هل تقصد حرفيًا مؤشر KdTree* غير الآمن؟لا تفعل ذلك.بدلاً من ذلك، استخدم فهرسًا في الصفيف.وبما أنني أتوقع أن يتم تخزين جميع العقد الخاصة بشجرة واحدة في نفس المصفوفة، فلن تحتاج إلى مرجع منفصل للمصفوفة.مجرد فهرس واحد سيفي بالغرض.

أخيرًا، إذا كان عليك حقًا استخدام مؤشرات KdTree*، فيجب على مدير الذاكرة الثابتة لديك تخصيص كتلة كبيرة باستخدام على سبيل المثال.Marshal.AllocHGlobal أو مصدر ذاكرة آخر غير مُدار؛يجب أن يتعامل مع هذه الكتلة الكبيرة كمصفوفة KdTree (أي.فهرسة نمط KdTree* C) و يجب أن يقوم بتخصيص العقد من هذه المصفوفة، عن طريق رفع مؤشر "مجاني".

إذا اضطررت إلى تغيير حجم هذه المصفوفة، فسوف تحتاج إلى تحديث جميع المؤشرات بالطبع.

الدرس الأساسي هنا هو أن المؤشرات غير الآمنة والذاكرة المُدارة تفعل ذلك لا امزج خارج الكتل "الثابتة"، والتي لها بالطبع تقارب إطار المكدس (أي.عندما تعود الدالة، يختفي السلوك المثبت).هناك طريقة لتثبيت كائنات عشوائية، مثل المصفوفة الخاصة بك، باستخدام GCHandle.Alloc(yourArray, GCHandleType.Pinned)، ولكنك بالتأكيد لا تريد السير في هذا الطريق.

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

اذا أنت حقًا تريد القيام بذلك، يمكنك استخدام الأسلوب GCHandle.Alloc لتحديد أنه يجب تثبيت المؤشر دون تحريره تلقائيًا في نهاية النطاق مثل العبارة الثابتة.

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

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

هل يعد تخزين زوج مرجع الصفيف والفهرس أمرًا محظورًا حقًا؟

ما الذي يفعله مدير الذاكرة الثابتة لديك بالفعل؟ما لم يكن يفعل شيئًا غير آمن (P/Invoc، رمز غير آمن)، فإن السلوك الذي تراه هو خطأ في برنامجك، وليس بسبب سلوك CLR.

كنت في الواقع أتحدث عن المؤشرات غير الآمنة.ما أردت كان شيئا من هذا القبيل Marshal.AllocHGlobal, ، على الرغم من أن عمر الخدمة يتجاوز استدعاء أسلوب واحد.عند التفكير، يبدو أن مجرد استخدام الفهرس هو الحل الصحيح لأنني ربما انشغلت كثيرًا بتقليد كود c++.

شيء واحد أود أن أقوله من رسالتك هو أن استخدام البنيات قد لا يساعد في الأداء كما تأمل.تفشل C# في تضمين أي استدعاءات أسلوب تتضمن بنيات، وعلى الرغم من أنهم قاموا بإصلاح ذلك في أحدث نسخة تجريبية لوقت التشغيل، إلا أن البنيات في كثير من الأحيان لا تؤدي أداءً جيدًا.

لقد بحثت في هذا الأمر قليلاً وأرى أنه قد تم إصلاحه في .NET 3.5SP1؛أفترض أن هذا ما كنت تشير إليه باعتباره الإصدار التجريبي من وقت التشغيل.في الواقع، أفهم الآن أن هذا التغيير أدى إلى مضاعفة سرعة العرض لدي.الآن، أصبحت الهياكل مضمنة بقوة، مما أدى إلى تحسين أدائها بشكل كبير على أنظمة X86 (كان أداء X64 أفضل للبنية مقدمًا).

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