إنشاء مصفوفات عالية السرعة

This article is available in English too, check it out here.

نظرة خاطفة

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

مقدمة

كما تعرف فإن المسؤول عن تنفيذ الأكواد في بيئة الدوت نت هي خدمة CLR، وهي الخدمة المسؤولة عن تنفيذ الأوامر والأكواد وتحويها إلى أوامر مقروءة بلغة الآلة Machine Language كي يستطيع الجهاز تنفيذها. وهذه الخدمة تعمل على تنظيم التعامل مع الذاكرة من خلال الكود فتحجب عن البرنامج الوصول المباشر إلى الذاكرة وبهذا تحمي المستخدم من أي أخطاء قد يحدثها البرنامج والتي قد تسبب في فشل النظام بالكامل أو على الأقل فشل البرنامج أو أحد البرامج الأخرى. وهذا التقنية المسؤولة عن حماية الكود وموارد الجهاز تسمى الشفرة/الكود الآمن Safe Code.

على العكس من هذه التقنية Safe Code، فإن CLR تتيح أيضا للكود الوصول المباشر إلى الذاكرة ولكن في إطار محدود، فهي تطلب من الكود تحديد متطلباته والدوال أو العناصر التي تطلب هذه الامتيازات الخاصة. وهذه التقنية بالطبع تسمى Unsafe Code.

ولكن، كيف نستفيد من خدمة Unsafe Code، أو لماذا نحتاج إليها؟ ربما يكون لديك آلاف أو حتى الملايين من عناصر البيانات والتي تريد الوصول إليها، وحينئذ سيكون الوصول إليها بالطريقة المعروفة بطيئ جدا وربما يسبب توقف البرنامج. فحينها، تلجأ إلى تقنية Unsafe Code لتحصل على أعلى سرعة في معالجة هذه البيانات.

وأيضا، ربما تحتاج إلى التعامل مع تقنية أخرى غير تقنية الدوت نت (مثلا التعامل مع Windows API) وتطلب منك هذه التقنية تحديد مكان العنصر أو البيانات المطلوبة في الذاكرة، وحينها ستحتاج إلى استخدام تقنية Unsafe Code.

بما أن درسنا اليوم يفترض معرفتك الأولية بما هي Unsafe Code، فلن ندخل في مزيد من التفاصيل عنها. وسنبدأ مباشرة في شرح فكرتنا التي نريد تنفيذها اليوم.

ما نريد عمليه اليوم هو إنشاء مصفوفة Array ووضعها في الذاكرة والتعامل معها مباشرة بدون وسيط، أي بدون تدخل CLR، للحصول على أعلى سرعة في التنفيذ. وهذه المصفوفات تسمى Stack-based Arrays.

علاوة على ذلك، سوف نقوم بوضع هذه المصفوفة في ذاكرة Stack وليس ذاكرة الـ Heap كالعادة. وهذا لنضمن إزالة هذه المصفوفة من الذاكرة فور انتهاء الكود الذي يتعامل معها، وبهذا نضمن أفضل أداء للبرنامج.

القراء الذين لديهم خبرة سابقة في لغات مثل C أو C++ سيلاحظون أن الكود مشابه كثيرا لأكواد C و C++ من ناحية استخدام المؤشرات Pointers والتنقل داخل الذاكرة. أما إذا لم يكن لديك خبرة في استخدام المؤشرات أو التعامل مع الكود غير الآمن Unsafe Code فيفضل أن تقوم بقراءة سريعة في هذا الموضوع  أولا.

ملاحظة هامة: هذه الخدمة Unsafe Code غير متوفرة في لغة VB.NET.

إنشاء المصفوفة

أولا، قم بتفعيل خدمة Unsafe Code على مشروعك من خلال الذهاب إلى خصائص المشروع Project Properties، والانتقال إلى التبويب Build، وتفعيل Unsafe Code.

أما الخطوة الثانية فهي كتابة الكود الذي يقوم بإنشاء المصفوفة وإضافة بعض العناصر إليها:

// Methods that use unsafe code
// must be declared as unsafe
public unsafe static void CreateArray()
{
    int length = 10000;
    // Creating Int32 stack-based array
    // with 10K elements
    int* pArr = stackalloc int[length];
    // Setting the first element to 1
    *pArr = 1;
    // This code also sets the first element
    // but to 10
    pArr[0] = 10;
    // Setting the second element to 2
    *(pArr + 1) = 2;
    // The following line also sets
    // the second element
    pArr[1] = 20;
    // Retrieving stored values
    Console.WriteLine("First value: {0}", *pArr);
    Console.WriteLine("First value: {0}", pArr[0]);
    Console.WriteLine("Second value: {0}", *(pArr + 1));
    Console.WriteLine("Second value: {0}", pArr[1]);
    Console.WriteLine();
    // Prints:
    // First value: 10
    // First value: 10
    // Second value: 20
    // Second value: 20
    // Setting all values
    for (int idx = 0; idx < length; idx++)
    {
        pArr[idx] = idx + 1;
        // Also this works well
        (pArr + idx) = idx + 1;
    }
    // Retrieving all values
    for (int idx = 0; idx < length; idx++)
        Console.WriteLine("Value {0} = {1}", idx, pArr[idx]);
    // Prints:
    // Value 0 = 1
    // Value 1 = 2
    // ............
    // Value 8 = 9
    // Value 9 = 10
    // The array will be removed from memory
    // at this location because the scope which
    // it was declared on ends here
}

أولا، قمنا بتحديد أن دالتنا CreateArray() تقوم بالتعامل مع كود غير آمن Unsafe Code ولذلك استخدمنا الخاصية unsafe في تعريفها.

قمنا بتعريف المصفوفة بطريقة خاصة وهي مصفوفة من نوع Integer وتم تعريفها باستخدام الخاصية stackalloc لتحديد أنها سوف يتم وضعها في Stack، وقمنا بتحديد أننا نحتاج إلى 10000 عناصر فقط، ثم قمنا بوضع بيانات زائفة (لا يشترط ملئها بالبيانات.)

الآن قم بكتابة نفس الكود السابق ولكن باستخدام الكود الآمن Safe Code أي بالطريقة المعروفة لديك من تعريف المصفوفات.

ثم قم بتجربة الكودين وقس الزمن (ربما بأداة مثل System.Threading.Timer) ثم سجل النتائج.

نسخ العناصر

يمكنك تجربة جميع العمليات على المصفوفات التي تستهلك وقت طويل (مثل البحث والعكس ونحوها) باستخدام الـ Unsafe Code والمقارنة بين زمنها وزمن الطريقة العادية.

من هذه العملية عملية نسخ العناصر من مصفوفة إلى مصفوفة أخرى. لاحظ الكود التالي:

unsafe static void Copy(int[] src, int srcIdx,
    int[] dst, int dstIdx, int count)
{
    // Because normal arrays are heap-based
    // GC can move them from time to time.
    // Therefore, we use the fixed statement
    // to stick them into memory
    fixed (int* pSrc = src, pDst = dst)
    {
        // Getting a pointer to the first
        // element of the array
        int* pSrcIdx = &srcIdx;
        int* pDstIdx = &dstIdx;
        // Don't exceed array boundaries
        for (int counter = 0; counter < count; counter++)
        {
            // Copying....
            // Because Int32 is stack-based
            // it is copied not referenced
            pDst[dstIdx] = pSrc[srcIdx];
            // Moving the pointer to the
            // next element
            dstIdx++;
            srcIdx++;
        }
    }
}

خاتمة

بالطبع إلا أن تضطر إلى استخدام الـ Unsafe Code يجب عليك البعد عن استخدامه بالكامل. فإن أحد الأخطاء في التعامل مع الذاكرة ربما تتسبب فشل نظام التشغيل.

مواضيع مشابهة:

اخترنا لك:

أحدث المواضيع:

هل أعجبتك؟ شارك بها...