سؤال

لدينا أجهزة Core2 (Dell T5400) مع XP64.

نلاحظ أنه عند تشغيل عمليات 32 بت ، يكون أداء memcpy في ترتيب 1.2gbyte/s ؛ومع ذلك ، يحقق memcpy في عملية 64 بت حوالي 2.2gbyte/s (أو 2.4gbyte/s مع memcpy المترجم Intel).على الرغم من أن التفاعل الأولي قد يكون مجرد توضيح ذلك بعيدًا بسبب السجلات الأوسع المتاحة في رمز 64 بت ، إلا أننا نلاحظ أن رمز تجميع SSE الذي يشبه Memcpy (والذي يجب أن يستخدم متاجر الحمل الواسعة 128 بت بغض النظر عن 32 /64 براعة من العملية) توضح حدود علوية مماثلة على النطاق الترددي للنسخ الذي يحققه.

سؤالي هو ، ما هو هذا الاختلاف في الواقع؟هل يجب أن تقفز عمليات 32 بت من خلال بعض الأطواق الإضافية لـ WOW64 للوصول إلى ذاكرة الوصول العشوائي؟هل هذا شيء يتعلق بـ tlbs أو prefecters أو ...ماذا ؟

شكرا على أي فكرة.

كما أثيرت على منتديات إنتل.

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

المحلول

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

وبلدي quess هو أنه ربما لا يكون لها أي علاقة مع 32 بت مقابل 64 بت في حد ذاته. تخميني هو أن روتين أسرع مكتبة كتب باستخدام SSE المتاجر غير والزمانية.

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

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

ومنذ memcpy لا تفعل أي حسابات، انها ملزمة دائما مدى السرعة التي يمكن قراءة وكتابة الذاكرة.

نصائح أخرى

وأعتقد أن يلي يمكن تفسير ذلك:

لنسخ البيانات من الذاكرة إلى تسجيل والعودة إلى الذاكرة، يمكنك القيام

mov eax, [address]
mov [address2], eax

وهذا ينقل 32 بت (4 بايت) من عنوان إلى address2. وينطبق نفس الشيء مع وضع 64 بت بت في 64

mov rax, [address]
mov [address2], rax

وهذا ينقل 64 بت، 2 بايت، من التصدي لaddress2. "موف" في حد ذاته، بغض النظر عن ما إذا كان 64 بت أو 32 بت و الكمون 0.5 والإنتاجية من 0.5 وفقا لمواصفات إنتل. الكمون هو كيف يأخذ العديد من الدورات على مدار الساعة تعليمات السفر عبر خط الانابيب والإنتاجية هي المدة التي CPU أن ينتظر قبل قبول نفس التعليمات مرة أخرى. كما ترون، فإنه يمكن القيام اثنين وسائل التحقق في كل دورة على مدار الساعة، ومع ذلك، فإن عليها أن تنتظر نصف دورة على مدار الساعة بين وسائل التحقق، وبالتالي فإنه يمكن على نحو فعال إلا تفعل وسائل التحقق واحد لكل دورة على مدار الساعة (أو أنا على خطأ هنا وإساءة تفسير المصطلحات؟ انظر PDF هنا لمزيد من التفاصيل).

وبطبيعة الحال لmov reg, mem يمكن أن تكون أطول من 0.5 دورات، تبعا إذا كانت البيانات في 1 أو ذاكرة التخزين المؤقت 2nd المستوى، أو لا في ذاكرة التخزين المؤقت على الإطلاق، ويحتاج إلى أن أمسك من الذاكرة. ومع ذلك، فإن الوقت الكمون من فوق يتجاهل هذه الحقيقة (كما يذكر PDF أنا مرتبطة أعلاه)، فإنه يتحمل كامل البيانات اللازمة لوسائل التحقق موجودة بالفعل (وإلا الكمون ستزيد بنسبة المدة التي تستغرقها لجلب البيانات من أينما كان الآن - وهذا قد يكون عدة دورات على مدار الساعة ومستقلة تماما عن الأمر يتم تنفيذه يقول PDF على الصفحة 482 / C-30)

.

ما هي مثيرة للاهتمام، ما إذا كانت وسائل التحقق هو 32 أو 64 بت لا تلعب أي دور. وهذا يعني أن ما لم يصبح عرض حزمة الذاكرة العامل المحدد، 64 بت وسائل التحقق هي على قدم المساواة بسرعة إلى 32 بت في وسائل التحقق، ونظرا لأنه يأخذ سوى نصف عدد من وسائل التحقق لنقل نفس الكمية من البيانات من A إلى B عند استخدام 64 بت، ويمكن للسرعة (نظريا) أن يكون ضعفي (حقيقة أنه ليس هو على الأرجح لأن الذاكرة لا غير محدود بسرعة).

حسنا، الآن كنت تعتقد عند استخدام سجلات SSE أكبر، يجب أن تحصل على سرعة أسرع، أليس كذلك؟ AFAIK سجلات XMM ليست 256، ولكن على نطاق واسع 128 بت، راجع للشغل ( إشارة في ويكيبيديا ). ومع ذلك، هل تعتبر الكمون والإنتاجية؟ أما البيانات التي تريد نقل هو محاذاة 128 بت أو لا. واعتمادا على ذلك، يمكنك إما نقلها باستخدام

movdqa xmm1, [address]
movdqa [address2], xmm1

وأو إذا لم الانحياز

movdqu xmm1, [address]
movdqu [address2], xmm1

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

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

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

لقد وصلت أخيرًا إلى الجزء السفلي من هذا (وكانت إجابة Die in Sente في الاتجاه الصحيح، شكرًا)

في ما يلي، يبلغ حجم dst وsrc 512 ميجابايت std::vector.أنا أستخدم برنامج التحويل البرمجي Intel 10.1.029 وCRT.

على 64 بت على حد سواء

memcpy(&dst[0],&src[0],dst.size())

و

memcpy(&dst[0],&src[0],N)

حيث تم الإعلان عن N مسبقًا const size_t N=512*(1<<20);يتصل

__intel_fast_memcpy

والتي يتكون الجزء الأكبر منها من:

  000000014004ED80  lea         rcx,[rcx+40h] 
  000000014004ED84  lea         rdx,[rdx+40h] 
  000000014004ED88  lea         r8,[r8-40h] 
  000000014004ED8C  prefetchnta [rdx+180h] 
  000000014004ED93  movdqu      xmm0,xmmword ptr [rdx-40h] 
  000000014004ED98  movdqu      xmm1,xmmword ptr [rdx-30h] 
  000000014004ED9D  cmp         r8,40h 
  000000014004EDA1  movntdq     xmmword ptr [rcx-40h],xmm0 
  000000014004EDA6  movntdq     xmmword ptr [rcx-30h],xmm1 
  000000014004EDAB  movdqu      xmm2,xmmword ptr [rdx-20h] 
  000000014004EDB0  movdqu      xmm3,xmmword ptr [rdx-10h] 
  000000014004EDB5  movntdq     xmmword ptr [rcx-20h],xmm2 
  000000014004EDBA  movntdq     xmmword ptr [rcx-10h],xmm3 
  000000014004EDBF  jge         000000014004ED80 

ويعمل بسرعة ~ 2200 ميجا بايت / ثانية.

ولكن على 32 بت

memcpy(&dst[0],&src[0],dst.size())

المكالمات

__intel_fast_memcpy

الجزء الأكبر منها يتكون من

  004447A0  sub         ecx,80h 
  004447A6  movdqa      xmm0,xmmword ptr [esi] 
  004447AA  movdqa      xmm1,xmmword ptr [esi+10h] 
  004447AF  movdqa      xmmword ptr [edx],xmm0 
  004447B3  movdqa      xmmword ptr [edx+10h],xmm1 
  004447B8  movdqa      xmm2,xmmword ptr [esi+20h] 
  004447BD  movdqa      xmm3,xmmword ptr [esi+30h] 
  004447C2  movdqa      xmmword ptr [edx+20h],xmm2 
  004447C7  movdqa      xmmword ptr [edx+30h],xmm3 
  004447CC  movdqa      xmm4,xmmword ptr [esi+40h] 
  004447D1  movdqa      xmm5,xmmword ptr [esi+50h] 
  004447D6  movdqa      xmmword ptr [edx+40h],xmm4 
  004447DB  movdqa      xmmword ptr [edx+50h],xmm5 
  004447E0  movdqa      xmm6,xmmword ptr [esi+60h] 
  004447E5  movdqa      xmm7,xmmword ptr [esi+70h] 
  004447EA  add         esi,80h 
  004447F0  movdqa      xmmword ptr [edx+60h],xmm6 
  004447F5  movdqa      xmmword ptr [edx+70h],xmm7 
  004447FA  add         edx,80h 
  00444800  cmp         ecx,80h 
  00444806  jge         004447A0

ويعمل بسرعة ~ 1350 ميجابايت/ثانية فقط.

لكن

memcpy(&dst[0],&src[0],N)

حيث تم الإعلان عن N مسبقًا const size_t N=512*(1<<20); يجمع (على 32 بت) إلى مكالمة مباشرة إلى ملف

__intel_VEC_memcpy

الجزء الأكبر منها يتكون من

  0043FF40  movdqa      xmm0,xmmword ptr [esi] 
  0043FF44  movdqa      xmm1,xmmword ptr [esi+10h] 
  0043FF49  movdqa      xmm2,xmmword ptr [esi+20h] 
  0043FF4E  movdqa      xmm3,xmmword ptr [esi+30h] 
  0043FF53  movntdq     xmmword ptr [edi],xmm0 
  0043FF57  movntdq     xmmword ptr [edi+10h],xmm1 
  0043FF5C  movntdq     xmmword ptr [edi+20h],xmm2 
  0043FF61  movntdq     xmmword ptr [edi+30h],xmm3 
  0043FF66  movdqa      xmm4,xmmword ptr [esi+40h] 
  0043FF6B  movdqa      xmm5,xmmword ptr [esi+50h] 
  0043FF70  movdqa      xmm6,xmmword ptr [esi+60h] 
  0043FF75  movdqa      xmm7,xmmword ptr [esi+70h] 
  0043FF7A  movntdq     xmmword ptr [edi+40h],xmm4 
  0043FF7F  movntdq     xmmword ptr [edi+50h],xmm5 
  0043FF84  movntdq     xmmword ptr [edi+60h],xmm6 
  0043FF89  movntdq     xmmword ptr [edi+70h],xmm7 
  0043FF8E  lea         esi,[esi+80h] 
  0043FF94  lea         edi,[edi+80h] 
  0043FF9A  dec         ecx  
  0043FF9B  jne         ___intel_VEC_memcpy+244h (43FF40h) 

ويعمل بسرعة ~ 2100 ميجابايت/ثانية (ويثبت أن 32 بت ليس محدودًا بطريقة أو بأخرى في عرض النطاق الترددي).

أسحب ادعائي أن كود SSE الذي يشبه memcpy يعاني من 1300 mbyte/الحد في 32 بت.ليس لدي الآن أي مشاكل في الحصول على> 2gbyte/s على 32 أو 64 بت ؛الحيلة (كما تلميح النتائج أعلاه) هي استخدام متاجر غير زملية ("دفق") (على سبيل المثال _mm_stream_ps جوهري).

يبدو غريبا بعض الشيء أن 32 بت "dst.size()"Memcpy لا يدعو في النهاية أسرع"movnt"الإصدار (إذا دخلت إلى memcpy ، فهناك كمية لا تصدق CPUID التحقق من المنطق والمنطق الإرشادي على سبيل المثال مقارنة عدد البايتات المراد نسخها بحجم ذاكرة التخزين المؤقت وما إلى ذلك قبل أن يذهب إلى أي مكان بالقرب من بياناتك الفعلية) ولكن على الأقل أفهم السلوك المرصود الآن (وليس Syswow64 أو H/W ذات الصلة).

وبلدي خارج على صفعة تخمين هو أن عمليات 64 بت تستخدم الأصلي حجم الذاكرة 64 بت المعالج، الذي يحسن استخدام الحافلة الذاكرة.

شكرا على التقييم الايجابي!أعتقد أنني أستطيع جزئيا شرح ما يحدث هنا.

إن استخدام المخازن غير المؤقتة لـ memcpy هو بالتأكيد الصيام لو أنت تقوم فقط بتوقيت مكالمة memcpy.

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

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

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

لذا فإن فكرة memcpy سيكون لها إصدارات متعددة تحت الغطاء.عندما يكون المخزن المؤقت للوجهة صغيرًا مقارنة بذاكرة التخزين المؤقت للمعالج، استخدم movdqa، وإلا، فإن المخزن المؤقت للوجهة كبير مقارنة بذاكرة التخزين المؤقت للمعالج، استخدم movntdq.يبدو أن هذا ما يحدث في مكتبة 32 بت.

وبطبيعة الحال، لا شيء من هذا له علاقة بالاختلافات بين 32 بت و64 بت.

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

وأنا لم يكن لديك إشارة أمام لي، لذلك أنا لست إيجابي للغاية على توقيت / تعليمات، ولكن أنا لا تزال تعطي النظرية. إذا كنت تفعل هذه الخطوة الذاكرة تحت وضع 32 بت، عليك أن تفعل شيئا مثل "movsd مندوب" التي تتحرك قيمة 32-بت واحد كل دورة على مدار الساعة. تحت وضع 64 بت، يمكنك القيام ب "movsq مندوب" التي لا واحد 64 بت تتحرك كل دورة على مدار الساعة. أن التعليمات غير متوفر لرمز 32 بت، لذلك كنت تقوم به 2 × مندوب movsd (في 1 دورة قطعة) لنصف سرعة التنفيذ.

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

وهنا مثال على روتين memcpy موجهة تحديدا للهندسة المعمارية 64 بت.

void uint8copy(void *dest, void *src, size_t n){
    uint64_t * ss = (uint64_t)src;
    uint64_t * dd = (uint64_t)dest;
    n = n * sizeof(uint8_t)/sizeof(uint64_t); 

    while(n--)
        *dd++ = *ss++;
}//end uint8copy()

وهذه المادة كاملة هنا: http://www.godlikemouse.com/2008/03/04/ تحسين-memcpy-الروتينية /

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