التخزين المؤقت في C++: تسريع التطبيقات 10 مرات

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

التخزين المؤقت للبيانات في C++: عندما تعمل الذاكرة أسرع من القرص



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



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



لماذا التخزين المؤقت أمر لا بد منه لمطور C++؟



C++ هي لغة للأنظمة عالية التحميل. روبوتات التداول، محركات الألعاب، خوادم قواعد البيانات - في كل مكان تكون فيه كل ميكروثانية مهمة، يصبح التخزين المؤقت هو المنقذ. بدونه، ستواجه باستمرار مشاكل في الإدخال/الإخراج (I/O) أو الحسابات المتكررة.



الفوائد الرئيسية من تطبيق التخزين المؤقت:



  • تقليل زمن الاستجابة - قراءة البيانات من RAM أسرع بمقدار 100,000 مرة من HDD.
  • تقليل الحمل على قاعدة البيانات - عدد أقل من الاستعلامات = زمن انتقال أقل.
  • توفير الموارد الحاسوبية - لا حاجة لإعادة حساب الدوال الثقيلة.


ولكن هناك أيضًا عيوب: تسرب الذاكرة، البيانات القديمة (stale data)، وانخفاض الأداء عند التنفيذ غير الصحيح. لذلك دعونا نفهم الأمر بالترتيب.



استراتيجيات التخزين المؤقت الرئيسية في C++



يعتمد اختيار الاستراتيجية على نوع البيانات التي تخزنها مؤقتًا ومدى تكرار تغييرها. دعنا ننظر إلى الثلاث الأكثر شيوعًا.



1. LRU (الأقل استخدامًا مؤخرًا) - نستبعد ما لم يتم الوصول إليه لأطول فترة



هذه هي الطريقة الكلاسيكية. تخيل أن لديك صندوقًا يتسع لـ 10 كتب. عندما يأتي كتاب جديد والصندوق ممتلئ، تتخلص من الكتاب الذي لم يُقرأ لأطول فترة. LRU cache C++ يعمل بنفس الطريقة.



التنفيذ في C++ باستخدام std::list و std::unordered_map:



#include <iostream>#include <list>#include <unordered_map>#include <string>

template <typename K, typename V>class LRUCache {private: size_t capacity; std::list<std::pair<K, V>> items; std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> cache;

public: LRUCache(size_t cap) : capacity(cap) {}

V get(const K& key) { auto it = cache.find(key); if (it == cache.end()) { return V(); // إرجاع القيمة الافتراضية إذا لم يتم العثور على المفتاح } // نقل العنصر إلى بداية القائمة (الأحدث) items.splice(items.begin(), items, it->second); return it->second->second; }

void put(const K& key, const V& value) { auto it = cache.find(key); if (it != cache.end()) { // تحديث العنصر الموجود it->second->second = value; items.splice(items.begin(), items, it->second); return; }

if (cache.size() >= capacity) { // إزالة أقدم عنصر (آخر عنصر في القائمة) auto last = items.back(); cache.erase(last.first); items.pop_back(); }

// إدراج عنصر جديد في البداية items.emplace_front(key, value); cache[key] = items.begin(); }};

int main() { LRUCache<std::string, int> lru(3); lru.put("one", 1); lru.put("two", 2); lru.put("three", 3); lru.get("one"); // الوصول إلى "one" - يصبح الأحدث lru.put("four", 4); // سيتم استبعاد "two" لأنه لم يتم الوصول إليه لأطول فترة\

المدونات

توصيات الكتب