سؤال

لدي توقيع أسلوب C++ الذي يبدو كما يلي:

    static extern void ImageProcessing(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
        [MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
        int inYSize, int inXSize);

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

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

المحلول 3

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

وكان رمز التوقيت الذي كنت تستخدم هذا:

Ipp32s timer;
ippGetCpuFreqMhz(&timer);
Ipp64u globalStart = ippGetCpuClocks();
globalStart = ippGetCpuClocks() *2 - globalStart; //use this method to get rid of the overhead of getting clock ticks

      //do some stuff

Ipp64u globalEnd = ippGetCpuClocks(); 
globalEnd = ippGetCpuClocks() *2 - globalEnd;
std::cout << "total runtime: " << ((Ipp64f)globalEnd - (Ipp64f)globalStart)/((Ipp64f)timer *1000000.0f) << " seconds" << std::endl;

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

وهناك لا يزال يبدو أن تأخير وقت التشغيل، though-- رمز سيقدم تقريرا 0.24 الصورة مع هذا الرمز توقيت على، والآن التقارير توقيت تقريبا 0.35s، وهو ما يعني أن هناك حوالي تكلفة سرعة 50٪.

وتغيير رمز لهذه:

  static extern void ImageProcessing(
     IntPtr inImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
     IntPtr outImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
     int inYSize, int inXSize);

ودعا مثل:

        unsafe {
            fixed (ushort* inImagePtr = theInputImage.DataArray){
                fixed (ushort* outImagePtr = theResult){
                    ImageProcessing((IntPtr)inImagePtr,//theInputImage.DataArray,
                        (IntPtr)outImagePtr,//theResult,
                        ysize,
                        xsize);
                }
            }
        }

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

نصائح أخرى

نلقي نظرة على هذا المقال.على الرغم من أن التركيز ينصب على Compact Framework، إلا أن المبادئ العامة تنطبق على سطح المكتب أيضًا.الاقتباس ذو الصلة من قسم التحليل هو كما يلي:

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

يحرر:يستحق القراءة أيضا مقالة المدونة هذه حول أداء كود JITted - مرة أخرى، خاص بالتليف الكيسي، ولكنه لا يزال ذا صلة.يوجد ايضا مقال يغطي عمق مكدس الاستدعاءات وتأثيره على الأداء, ، على الرغم من أن هذا ربما يكون خاصًا بـ CF (لم يتم اختباره على سطح المكتب).

هل حاولت تبديل معلمتي الصفيف إلى IntPtr؟يكون PInvocation في أسرع حالاته على الإطلاق عندما تكون جميع الأنواع الموجودة في التوقيع التنظيمي قابلة للاختراق.هذا يعني أن Pinvoc يقتصر على مجرد memcpy للحصول على البيانات ذهابًا وإيابًا.

لقد وجدنا في فريقي أن الطريقة الأكثر أداءً لإدارة طبقة PInvoc الخاصة بنا هي

  1. ضمان أن كل شيء يجريه مارشال قابل للكسر
  2. ادفع الثمن لأنواع Marshal يدويًا مثل المصفوفات عن طريق معالجة فئة IntPtr على أساس الحاجة.هذا أمر تافه للغاية نظرًا لأن لدينا العديد من أساليب/فئات التغليف.

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

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