حول ذاكرات التخزين المؤقت في ميكروكنترولر ARM

صورةمرحبا!



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



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



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


الكتابة من خلال



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



بالطبع ، يبدو أن هذا يجب أن يكون له تأثير كبير على الأداء ، لكن STM نفسها في هذا المستند تقول أنه ليس:

Write-through: triggers a write to the memory as soon as the contents on the cache line are written to. This is safer for the data coherency, but it requires more bus accesses. In practice, the write to the memory is done in the background and has a little effect unless the same cache set is being accessed repeatedly and very quickly. It is always a tradeoff.
أي أننا افترضنا في البداية أنه نظرًا لأن الكتابة هي الذاكرة ، فإن الأداء في عمليات الكتابة سيكون تقريبًا كما هو بدون ذاكرة تخزين مؤقت على الإطلاق ، ويحدث الكسب الرئيسي بسبب القراءات المتكررة. ومع ذلك ، تدحض STM هذا ، وتقول أن البيانات الموجودة في الذاكرة "في الخلفية" ، لذا فإن أداء الكتابة هو نفسه تقريبًا في وضع "إعادة الكتابة". هذا ، على وجه الخصوص ، قد يعتمد على المخازن المؤقتة الداخلية لوحدة تحكم الذاكرة (FMC).



عيوب وضع "الكتابة من خلال":



  • يمكن أن يؤدي الوصول المتسلسل والسريع إلى نفس الذاكرة إلى تدهور الأداء. في وضع "إعادة الكتابة" ، ستكون عمليات الوصول المتكررة المتسلسلة إلى نفس الذاكرة ، على العكس من ذلك ، ميزة إضافية.
  • كما في حالة "إعادة الكتابة" ، ما زلت بحاجة إلى إبطال ذاكرة التخزين المؤقت بعد انتهاء عمليات DMA.
  • خطأ "تلف البيانات في تسلسل مخازن وتحميلات الكتابة" في بعض إصدارات Cortex-M7. تم توضيح ذلك من قبل أحد مطوري LVGL.


رد على الرسالة



كما ذكرنا سابقًا ، في هذا الوضع (على عكس "الكتابة من خلال") لا تدخل البيانات عمومًا الذاكرة عن طريق الكتابة ، ولكنها تذهب فقط إلى ذاكرة التخزين المؤقت. مثل "الكتابة من خلال" ، هذه الإستراتيجية لها "خياران فرعيان" - 1) "تخصيص الكتابة" ، 2) "عدم تخصيص الكتابة". سنتحدث عن هذه الخيارات أكثر.



اكتب تخصيص



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



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



MPU



قبل الانتقال إلى الجزء العملي ، نحتاج إلى معرفة كيفية تعيين معلمات منطقة الذاكرة. لتحديد وضع التخزين المؤقت (أو تعطيله) لمنطقة معينة من الذاكرة في بنية ARMv7-M ، يتم استخدام MPU (وحدة حماية الذاكرة).



تدعم وحدة التحكم MPU ضبط مناطق الذاكرة. على وجه التحديد في بنية ARMV7-M ، يمكن أن يكون هناك ما يصل إلى 16 منطقة. بالنسبة لهذه المناطق ، يمكنك تعيين: عنوان البداية والحجم وحقوق الوصول (القراءة / الكتابة / التنفيذ ، وما إلى ذلك) بشكل مستقل ، والسمات - TEX ، والتخزين المؤقت ، والتخزين المؤقت ، والقابل للمشاركة ، بالإضافة إلى المعلمات الأخرى. باستخدام مثل هذه الآلية ، على وجه الخصوص ، يمكنك تحقيق أي نوع من التخزين المؤقت لمنطقة معينة. على سبيل المثال ، يمكننا التخلص من الحاجة إلى استدعاء cache_clean / cache_invalidate ببساطة عن طريق تخصيص منطقة من الذاكرة لجميع عمليات DMA ووضع علامة على تلك الذاكرة على أنها غير قابلة للتخزين المؤقت.



نقطة مهمة يجب ملاحظتها عند العمل مع MPU:

يمكن تكوين كل من العنوان الأساسي والحجم والسمات لمنطقة ما ، مع القاعدة العامة التي تنص على محاذاة جميع المناطق بشكل طبيعي. يمكن ذكر ذلك على النحو التالي:

RegionBaseAddress [(N-1): 0] = 0 ، حيث N هو log2 (SizeofRegion_in_bytes)
بمعنى آخر ، يجب محاذاة عنوان البداية لمنطقة الذاكرة مع حجمها. إذا كان لديك ، على سبيل المثال ، منطقة 16 كيلو بايت ، فأنت بحاجة إلى محاذاتها بمقدار 16 كيلو بايت. إذا كانت مساحة الذاكرة 64 كيلو بايت ، فقم بمحاذاة 64 كيلو بايت. وهلم جرا. إذا لم يتم ذلك ، فيمكن لوحدة MPU تلقائيًا "اقتصاص" المنطقة إلى الحجم المقابل لعنوان البداية (تم اختباره عمليًا).



بالمناسبة ، هناك العديد من الأخطاء في STM32Cube. على سبيل المثال:



  MPU_InitStruct.BaseAddress = 0x20010000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;


يمكنك أن ترى أن عنوان البداية محاذي 64 كيلو بايت. ونريد أن يكون حجم المنطقة 256 كيلوبايت. في هذه الحالة ، سيتعين عليك إنشاء 3 مناطق: أول 64 كيلوبايت ، والثاني 128 كيلوبايت ، والثالث 64 كيلوبايت.



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



الطب الصيني التقليدي



كما يلي من الوثائق (القسم 2.3 Embedded SRAM) ، فإن أول 64 كيلو بايت من SRAM في STM32F7 غير قابل للتخزين المؤقت. في بنية ARMv7-M نفسها ، يقع SRAM في 0x20000000. يشير TCM أيضًا إلى SRAM ، ولكنه موجود في ناقل مختلف بالنسبة لبقية الذكريات (SRAM1 و SRAM2) ، ويقع "أقرب" إلى المعالج. لهذا السبب ، هذه الذاكرة سريعة جدًا ، في الواقع ، لها نفس سرعة ذاكرة التخزين المؤقت. ولهذا السبب ، لا حاجة إلى التخزين المؤقت ، ولا يمكن جعل هذه المنطقة قابلة للتخزين المؤقت. في الواقع ، الطب الصيني التقليدي هو نوع آخر من ذاكرة التخزين المؤقت.



مخبأ التعليمات



تجدر الإشارة إلى أن كل ما تمت مناقشته أعلاه يشير إلى ذاكرة التخزين المؤقت للبيانات (D-Cache). ولكن بالإضافة إلى ذاكرة التخزين المؤقت للبيانات ، يوفر ARMv7-M أيضًا ذاكرة تخزين مؤقت للتعليمات - ذاكرة التخزين المؤقت للتعليمات (I-Cache). يسمح لك I-Cache بنقل بعض التعليمات القابلة للتنفيذ (والتعليمات اللاحقة) إلى ذاكرة التخزين المؤقت ، والتي يمكن أن تسرع البرنامج بشكل كبير. خاصة في الحالات التي يكون فيها الرمز في ذاكرة أبطأ من FLASH ، على سبيل المثال ، QSPI.



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



في الوقت نفسه ، أود أن أشير إلى أن تشغيل I-Cache بسيط للغاية ولا يتطلب أي إجراءات إضافية من MPU ، على عكس D-Cache.



الاختبارات التركيبية



بعد مناقشة الجزء النظري ، دعنا ننتقل إلى الاختبارات لفهم أفضل للاختلاف ونطاق قابلية تطبيق نموذج معين. كما قلت أعلاه ، قم بتعطيل I-Cache واعمل فقط مع D-Cache. أنا أيضًا أقوم بالتجميع عن قصد مع -O0 حتى لا يتم تحسين الحلقات في الاختبارات. سنقوم باختبار ذاكرة SDRAM الخارجية. بمساعدة MPU ، قمت بتمييز منطقة 64 كيلوبايت ، وسوف نكشف السمات التي نحتاجها لهذه المنطقة.



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



يمكنك عرض أكواد الاختبار الكاملة هنا . تم إجراء جميع الاختبارات على اللوحة 32F769IDISCOVERY .



الذاكرة غير القابلة للتخزين المؤقت VS. رد على الرسالة



لنبدأ ببعض الاختبارات البسيطة جدًا.



نحن فقط نكتب إلى الذاكرة باستمرار.



    dst = (uint8_t *) DATA_ADDR;

    for (i = 0; i < ITERS * 8; i++) {
        for (j = 0; j < DATA_LEN; j++) {
            *dst = VALUE;
            dst++;
        }
        dst -= DATA_LEN;
    }


نكتب أيضًا إلى الذاكرة بالتسلسل ، لكن ليس بايت واحد في كل مرة ، لكن نوسع الحلقات قليلاً.



    for (i = 0; i < ITERS * BLOCKS * 8; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            *dst = VALUE;
            *dst = VALUE;
            *dst = VALUE;
            *dst = VALUE;
            dst++;
        }
        dst -= BLOCK_LEN;
    }


نكتب أيضًا إلى الذاكرة بالتتابع ، لكننا الآن سنضيف القراءة أيضًا.



    for (i = 0; i < ITERS * BLOCKS * 8; i++) {
        dst = (uint8_t *) DATA_ADDR;

        for (j = 0; j < BLOCK_LEN; j++) {
            val = VALUE;
            *dst = val;
            val = *dst;
            dst++;
        }
    }


إذا أجريت كل هذه الاختبارات الثلاثة ، فستعطي نفس النتيجة تمامًا ، بغض النظر عن الوضع الذي تختاره:



mode: nc, iters=100, data len=65536, addr=0x60100000
Test1 (Sequential write):
  0s 728ms
Test2 (Sequential write with 4 writes per one iteration):
  7s 43ms
Test3 (Sequential read/write):
  1s 216ms


وهذا أمر معقول ، SDRAM ليس بطيئًا ، خاصة عندما تفكر في المخازن المؤقتة الداخلية لـ FMC التي يتم توصيلها من خلالها. ومع ذلك ، كنت أتوقع اختلافًا طفيفًا في الأرقام ، لكن اتضح أنه لم يكن في هذه الاختبارات. حسنًا ، دعنا نفكر أكثر.



دعنا نحاول "إفساد" حياة SDRAM عن طريق المزج بين القراءة والكتابة. للقيام بذلك ، دعنا نوسع الحلقات ونضيف شيئًا شائعًا في الممارسة مثل زيادة عنصر المصفوفة:



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            // 16 lines
            arr[i]++;
            arr[i]++;
	***
            arr[i]++;
        }
    }


نتيجة:



  :   4s 743ms
Write-back:                     :   4s 187ms


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



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr[i + 0 ]++;
            ***
            arr[i + 3 ]++;
            arr[i + 4 ]++;
            arr[i + 100]++;
            arr[i + 6 ]++;
            arr[i + 7 ]++;
            ***
            arr[i + 15]++;
        }
    }


نتيجة:



  :   11s 371ms
Write-back:                     :   4s 551ms


الآن أصبح الاختلاف مع ذاكرة التخزين المؤقت أكثر من ملحوظ! وفوق ذلك ، نقدم فهرسًا ثانيًا من هذا القبيل:



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr[i + 0 ]++;
            ***
            arr[i + 4 ]++;
            arr[i + 100]++;
            arr[i + 6 ]++;
            ***
            arr[i + 9 ]++;
            arr[i + 200]++;
            arr[i + 11]++;
            arr[i + 12]++;
            ***
            arr[i + 15]++;
        }
    }


نتيجة:



  :   12s 62ms
Write-back:                     :   4s 551ms


نرى كيف نما الوقت للذاكرة غير المخزنة مؤقتًا بمقدار ثانية تقريبًا ، بينما بقيت ذاكرة التخزين المؤقت كما هي.



اكتب تخصيص VS. لا الكتابة تخصيص



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



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr[j + 0 ]  = VALUE;
            ***
            arr[j + 7 ]  = VALUE;
            arr[j + 8 ]  = arr[i % 1024 + (j % 256) * 128];
            arr[j + 9 ]  = VALUE;
            ***
            arr[j + 15 ]  = VALUE;
        }
    }


هنا ، تم تعيين 15 من أصل 16 سجلًا على ثابت VALUE ، بينما تتم القراءة من عناصر مختلفة (لا تتعلق بالكتابة) arr [i٪ 1024 + (j٪ 256) * 128]. اتضح أنه مع إستراتيجية عدم تخصيص الكتابة ، سيتم تحميل هذه العناصر فقط في ذاكرة التخزين المؤقت. سبب استخدام هذه الفهرسة (i٪ 1024 + (j٪ 256) * 128) هو "تدهور سرعة" FMC / SDRAM. نظرًا لأن الوصول إلى الذاكرة في عناوين مختلفة (غير متسلسلة) يمكن أن يؤثر بشكل كبير على سرعة العمل.



نتيجة:



Write-back                                           :   4s 720ms
Write-back no write allocate:               :   4s 888ms


أخيرًا ، حصلنا على فرق ، وإن لم يكن ملحوظًا ، لكنه مرئي بالفعل. هذا هو ، فرضيتنا تم تأكيدها.



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



في الاختبار التالي ، في حالة "اكتب تخصيص" ، سيتم ملء البيانات على القراءة والكتابة. لقد صنعت مصفوفة 64 كيلو بايت "arr2" ، لذلك سيتم مسح ذاكرة التخزين المؤقت لتبديل البيانات الجديدة. في حالة "عدم تخصيص الكتابة" ، قمت بإنشاء مصفوفة "arr" من 4096 بايت ، وسوف تدخل فقط في ذاكرة التخزين المؤقت ، مما يعني أن بيانات ذاكرة التخزين المؤقت لن يتم مسحها في الذاكرة. نتيجة لهذا ، سنحاول تحقيق فوز صغير على الأقل.



    arr = (uint8_t *) DATA_ADDR;
    arr2 = arr;

    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr2[i * BLOCK_LEN            ] = arr[j + 0 ];
            arr2[i * BLOCK_LEN + j*32 + 1 ] = arr[j + 1 ];
            arr2[i * BLOCK_LEN + j*64 + 2 ] = arr[j + 2 ];
            arr2[i * BLOCK_LEN + j*128 + 3] = arr[j + 3 ];
            arr2[i * BLOCK_LEN + j*32 + 4 ] = arr[j + 4 ];
            ***
            arr2[i * BLOCK_LEN + j*32 + 15] = arr[j + 15 ];
        }
    }


نتيجة:



Write-back                                           :   7s 601ms
Write-back no write allocate:               :   7s 599ms


يمكن ملاحظة أن وضع "إعادة الكتابة" "تخصيص الكتابة" يكون أسرع قليلاً. لكن الشيء الرئيسي هو أنه أسرع.



لم أحصل على عرض أفضل ، لكنني متأكد من وجود مواقف عملية يكون الفرق فيها ملموسًا بشكل أكبر. يمكن للقراء اقتراح خياراتهم الخاصة!



أمثلة عملية



دعنا ننتقل من الأمثلة الاصطناعية إلى الأمثلة الحقيقية.



بينغ



أحد أبسطها هو ping. من السهل البدء ، ويمكن عرض الوقت مباشرة على المضيف. تم بناء Embox مع تحسين -O2. سأعطي النتائج على الفور:



    :  ~0.246 c
Write-back                        :  ~0.140 c


أوبينكف



مثال آخر لمشكلة حقيقية أردنا تجربة النظام الفرعي لذاكرة التخزين المؤقت عليها هو OpenCV على STM32F7 . في تلك المقالة ، تم توضيح أنه من الممكن تمامًا الإطلاق ، لكن الأداء كان منخفضًا نوعًا ما. للتوضيح ، سنستخدم مثالًا قياسيًا يستخرج الحدود بناءً على مرشح Canny. دعونا نقيس وقت التشغيل مع أو بدون ذاكرات التخزين المؤقت (كلا من D-cache و I-cache).



   gettimeofday(&tv_start, NULL);

    cedge.create(image.size(), image.type());
    cvtColor(image, gray, COLOR_BGR2GRAY);

    blur(gray, edge, Size(3,3));
    Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
    cedge = Scalar::all(0);

    image.copyTo(cedge, edge);

    gettimeofday(&tv_cur, NULL);
    timersub(&tv_cur, &tv_start, &tv_cur);


بدون ذاكرة تخزين مؤقت:



> edges fruits.png 20 
Processing time 0s 926ms
Framebuffer: 800x480 32bpp
Image: 512x269; Threshold=20


مع ذاكرة التخزين المؤقت:



> edges fruits.png 20 
Processing time 0s 134ms
Framebuffer: 800x480 32bpp
Image: 512x269; Threshold=20


أي أن تسارع 926 مللي ثانية و 134 مللي ثانية يساوي 7 أضعاف تقريبًا.



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



لا ذاكرة مخبأة أو ذاكرة التخزين المؤقت ، ولكن مع ذاكرة التخزين المؤقت إبطال؟



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



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



خاتمة



لقد توصلنا قليلاً إلى الأنواع المختلفة من ذاكرات التخزين المؤقت في ARMv7m: إعادة الكتابة ، والكتابة ، بالإضافة إلى إعدادات "الكتابة المخصصة" و "عدم تخصيص الكتابة". لقد قمنا ببناء اختبارات تركيبية حاولنا من خلالها معرفة متى يكون أحد الأوضاع أفضل من الآخر ، كما درسنا أمثلة عملية باستخدام ping و OpenCV. في Embox ، نحن نعمل فقط على هذا الموضوع ، لذلك لا يزال النظام الفرعي المقابل قيد العمل. إن مزايا استخدام ذاكرات التخزين المؤقت ملحوظة بالتأكيد.



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



ملاحظة:



إذا كنت مهتمًا بموضوع برمجة النظام و OSDev ، فسيتم عقد مؤتمر OS Day غدًا ! هذا العام متصل بالإنترنت ، لذلك لا تفوت فرصة لمن يرغبون! سيعرض Embox غدًا في الساعة 12.00



All Articles