السلام عليكم
تتمحور هذه المقاله على الافضلية optimization في برمجتك لأي مشروع كان ,
بحيث تقدم لك المفاهيم العامة عن النصوص البرمجية و التباين بينهم من حيث الأفضل.
وكذلك ستسوق لك بحول الله بعض المصطلاحات العلمية و التقنية في هذا المجال .
إضافه إلى ذلك تزويد هذه المقاله بالنصوص البرمجية (VB.NET) كل ما امكن .
في كلمات أخرى تقدم لك هذه المقاله, مفاضلة نص برمجي عن أخر, وذكر سبب المفاضلة
إن أمكن ذلك للنسخة VS.NET 2003 لنبدأ الرحله .
ملاحظة هذه المقاله إشرُبت معظم محتوياتها من المقاله التالية
Performance Optimization in Visual Basic .NET
علاوة على ذلك من بعض الكتُب و صفحات الإنترنت .
1 – إتساع نوع البيانات data type width
يفضل دائماً و أبداً الـ Integer عند إستخدام البيانات الرقمية ثم يليه الـ Long < byte< short على هذا الترتيب
أما عند إستخدام الأعداد الكسرية فإن الـ Double هو الأفضل ثم يليه الـ Decimal < Single على هذا الترتيب
......................................................
السبب : كون النوع Integer و Double هما الأقرب من حيث محاكاتهما (تقارب حجمهم , حجم البيانات Length ) لحجم
أنظمة التشغيل و المعالجات على حدٍ سواء , و السبب بسيط جداً , كون النوع الأول يتم تخزينه في الذاكرة على اساس 32 bits
و بسبب أن معالجات اليوم processors على سرعة 32 bits , لذلك الـ Integer هو الافضل كعدد صحيح
بينما الثاني Double يتم تخزينه في الذاكرة على اساس 64 bits لكن كل العمليات الكسرية في المعالجات
تعمل على اساس Double لذلك هو الأنجع في المتغيرات الكسرية . ولو كان المتغير من نوع single على اساس 32 bits
بمعنى أخر byte الخيار الأسوء عندما يكون بديل لعمل الـ Integer ويكون الـ Integer قادر على ذلك العمل .
من الملاحظ هنا أنه كلما قل حجم النوع ( data type ) كل ما قلة أفضليته في تخزينه و إستيراده من الذاكرة
و في المستقبل القريب عندما تكون المعالجات ذوات الحجم 64 bits فإن الخيار الأفضل كعدد صحيح سيكون بلا شك هو Long .
للمزيد عن هذا المنطق راجع الـ Integer Data Type في الـ MSDN
ملاحظة : يمكنك تحسين أداء كل من short و byte بإسناد القيمة true للخاصية RemoveIntegerChecks
النصيحة : إستخدم المتغير الرقمي كعدد صحيح من نوع Integer كل ما أمكن عِوضاً عن البقية.
و استخدم المتغير الرقمي الكسري Double كل ما أمكن عوضاً عن المتغيرات التي تكون على نفس النمط.
2 – المتغيرات المرجعية و المتغيرات القيمية Reference Type And Value Type
كما هو معلوم للجميع أن المتغير الغير مرجعي Value Type يحتفظ بياناته على حِده في الذاكرة , غير قابل لمشاركة الأخرين
بسبب العزلة التي يفرضها هذا النوع لانه يقبع في منطقة تعرف بـ Stack .
بينما النوع الأخر Reference Type يحمل فقط رقم المرجع Pointer و بما أنه قابل للمشاركة لأكثر من متغير في قيمته
فإنه يجب أن يكون في منطقة في الذاكرة تعرف بـ Heap .
المنطقة Heap ثقيلة و بطيئة نسبةً للمنطقة Stack ولذلك لأسباب منها البحث عن المنطقة التي بها العنوان
ثم البحث عن المؤشر عليها كعناصر , إضافه إلى جامعت النفايات ( العناصر الغير مرغوب فيها) بالعنصر garbage collection
لذلك يُعُتبر المتغير الغير مرجعي أفضل من المتغير المرجعي .
يقول المبرمج Francesco Balena صاحب كتاب Programming Microsoft Visual Basic .NET ص 106
المتغير الغير مرجعي (القيمي) Value Type أسرع من المتغير المرجعي Reference Type لِسببين إثنين
1 – أنك لا تحتاج للعودة لصاحب المرجع (متغير) dereference لأخذ القيمة الحقيقية منه
2 – لا تحتاج للبحث عن موقع المتغير لِتحرير الذاكرة release memory في منطقة الـ Heap
بسبب أن المتغير الغير مرجعي يهدم ذاته بذاته destroyed بعد الخروج من الإجراء مباشرتاً .
النصيحة : يفضل دئماً و بداً المتغير الغير مرجعي Value Type كل ما أمكن بشرط أن تكون القيم صغيرة نسبياً
وينصح بإستخدام المتغيرات أو الممررات المرجعية عندما تكون القيم كبيرة أو عبارة عن قوالب كمثلاً تمرير نموذج أو صندوق الصور .
3 – تحويل قيمة المتغير القيمي إلى قيمة مرجعية و العكس Boxing And UnBoxing
هي عملية إلزامية على الـ CLR لتأديتها عندما تتعامل مع العناصر من نوع Value Type على أساس الـ Reference Type
عندما تقوم بتمرير قيمة من نوع Value Type إلى إجراء أو سلوك يحتوي على ممرر من نوع Object أو متغير مرجعي النوع
فإن القيمة التي مررتها يتم تحويلها إلى Reference Type ثم يتم اخذ مساحه لها في الـ heap في الذاكرة
ثم تؤخذ نسخه من ممررك كقيمة إلى تلك المنطقة في الذاكرة و من ثم يمرر عنوان تلك المنطقة كمؤشر Pointer
إلى ذلك السلوك ( العنوان يكون عادة عبارة عن أرقام كمكان لتلك المنطقة في الذاكرة)
هذه العملية تعرف بـ Boxing . إليك المثال التالي لعملية الـ Boxing
'Option Strict On Dim ValueType As Integer Dim RefType As Object RefType = CType(ValueType, Object)
و عندما يتم إسناد قيمة الـ Boxed Value الى متغير من النوع Value Type تعرف بـ Unboxing
بحيث يتم نقل القيمة من المنطقةHeap إلى المنطقة Stack
على إفتراض ان المتغير المستقبل لهذا القيمة هو متغير محلي local variable بهذا الشكل
ValueType = CType(RefType, Integer)
النصيحة : دئماً و أبداً أجعل هذا الخيار Option Strict On من ضمن كل مشروع تقوم بإنشاءه
فإنه سيتكفل عنك عناء البحث عن تحويل قيم المتغيرات المرجعية إلى متغيرات غير مرجعية
مثل هذا الكود تماماً
ValueType = RefType 'Option Strict Off
4 – الممرر المرجعي و الممرر القيمي ByRef & ByVal
إن ممررات الوظائف و الإجراءات عادةً لا تكون ذات أهميه قصوى, إلا ان هنالك بعض الامور التي يجب مراعاتها في ذلك
أولاً لابد أن نعرف إذا تم تمرير قيمة إلى ممرر من نوع ByRef فسيتم أخذ عنوان تلك القيمة , سواءً كانت القيمة الممررةً
من نوع Value أو Reference , أما إن كان الممرر من نوع ByVal فسيتم أخذ القيمة مباشرتاً حتى و إن كان
المُمرر متغير مرجعي. لذلك يفضل أن يكون الممرر من نوع ByVal
إذا كانت المتغيرات الممررة ذات أنواع صغيرة Small data type width مثل Integer أو Double أو غيرهما .
إما إذا كانت القيم الممررة عبارة عن structure مثلاً أو ذات بيانات كبيرة Large Data Type Width فإن
الممرر المرجعي هو الأفضل. إضافة إلى ذلك أن الممرر المرجعي يستطيع تغير قيمة متغير مؤشر للقيمة المُمررة
و الممرر القيمي كأفضلية يحمي تغير القيمة الممررة .
النصيحة : إذا كنت ترسل قيم صغيرة أو لا ترغب بأن يطرأ عليها تغيير فإن ByVal يضمن لك ذلك
إما إذا كنت ترسل قيم كبيرة أو مؤشر عليها لأكثر من متغير و ترغب في أن يحصل الكل على الناتج بعد التمرير فإن ByRef يضمن لك ذلك.
5 – وحدة القياس التمثيلية Units of Representation
عندما يتعلق الامر بوحدة القياس فإن الافضل هي الحسبة بالـ pixel مقارنتاً بـ inches أو centimeters
كون عائد قيمة الـ Pixel هو عدد صحيح , وهذا يعني أننا نستطيع تخزينها في متغير رقمي النوع Integer .
بينما inches يتطلب متغير كسري النوع fractional كمثلاً النوع double وهذا ليس بأفضل من النوع الرقمي الصحيح .
النصيحة : ومن الملاحظ هنا أن نستخدم العدد الصحيح في القياسات التمثيلية كل ما أمكن . عِوضاً عن المتغيرات الرقمية الأخرى .
6 – المصفوفات Arrays
دئماً حاول أن تتجنب المصفوفات ذات المقاييس الطويلة Rank
الـ Rank هو عدد مقياس المصفوفة مثلاً Dim RectArrays(7, 4, 7) As String الـ Rank يساوي 3
فكلما زاد مقياس المصفوفة , قلّت الكفاءة . هذا النوع من المصفوفات يعرف بـ rectangular المصفوفات المستطيلة
و يفضل إستخدام المصفوفات البارزة Jagged وهي التي تعرف بـالمصفوفات في مصفوفات Arrays of Arrays
بمعنى أخر يفترض أن يكون تعريف المصفوفات بهذا الشكل Dim jagArrays(2)() As String
دائماً تجنب تعريف المصفوفات المستطيلة والتي تكون بهذا الشكل Dim RectArrays(1,) As String
………………………………..
السبب
كون الـ CLR يعمل بشكل أفضل على المصوفات البارزة Jagged بحيث يوظفها بشكل أفضل من المصفوفات المستطيلة rectangular
بقدر يتجاوز الثلاثين نسبة مئوية.
و أكثر حد للـ Rank هو 32 فقط وعلى هذا المدى و المقياس يعتبر أكثر من ثلاثة شيء نادر !!!
للمزيد عن المصفوفات راجع هذا الرابط
وللعلم إذا كانت المصفوفة لديك تتغير من حيث الطول Length بشكل متكرر فإن الخيار الأفضل إن نستخدم ArrayList
فهي أفضل بكثير من إستخدام Redim مع Preserve و لكن العيب الوحيد في هذا النوع من المصفوفات هي أنها من نوع Object
وهذا يعني أنها على النمط Late binding اي الإرتباط المُتأخر .
النصيحة : حاول تجنب المصفوفات ذات المدى الطويل large Rank كل ما أمكن
و دائماً إستخدم المصفوفات البارزة Jagged عِوضاً عن المصفوفات المستطيلة rectangular
7 – عناصر التجميع و عناصر المصفوفات Object Collections and Object Arrays
التباين بين المصفوفات و المجموعات , المصفوفة هي الخيار الافضل كعنصر يحتوي على أكثر من قيمة
إذا كنت تبحث عن القيم عن طريق ترقيمها في المصفوفة كـ Index فإن المصفوفة هي الخيار الافضل
إذا كنت تبحث عن القيم عن طريق مفاتيحها keys فإن المصفوفة لا تدعم ذلك بل المجموعة هي الخيار الافضل collection
يفضل دائماً المجموعات , حينما يتعلق الامر بالاضافه و الحذف و غير ذلك , بسبب أن المصفوفات لا تدعم هذا بشكل مباشر.
بالنسبة للمصفوفات إذا كنت تضيف و تحذف من اخر المصفوفه فاستخدم المصفوفات Jagged مع السلوك Redim
اما إذا كنت تضيف و تحذف في اي مكان في المصفوفة فاستخدم المصفوفة ArrayList
النصيحة : إستخدم المصفوفة Jagged كل ما أمكن , ثم Collection ثم ArrayList
و إبتعد عن المصفوفة Rectangle كل ما أمكن .
8 – الإرتباط المُتأخر و الارتباط المبكر Late Bound And Early Bound
كل المتغيرات التي تكون من نوع Object هي متغيرات مرجعية Reference Type
على أفضليتها في أنها تقبل أي قيمة إلا أنه توصف بـ Late Bound وهو الارتباط المُتأخر
يحدث ذلك إذا لم يتم تعريف المتغير على أنه قالب معين specific Class
و يعرف على انه متغير من نوع Object مثال ذلك
Dim myVar As Object ‘Late Bound Dim myVar As Form1 ‘Early Bound
السبب
تعود أفضلية الارتباط السريع بإن المترجم يقوم ببعض التحسينات Management على المتغير أثناء التشغيل
أما الارتباط المتأخر فإن المترجم مجبر على التحقق من نوع المتغير مع عناصرهِ أو قيمه
إضافه إلى ذلك يكون غير مقروء unreadable
وصعب التعديل , كما أن مشروعك قد يكون معرض لظهور الأخطاء أثناء التشغيل Run Time Error
النصيحة : دائماً تجنب التعريف الغير مباشر كل ما أمكن , ودئماً إستخدم التعريف المباشر أو المبكر كل ما أمكن
9 – فارق السرعة بين المتغيرات و الخصائص و الثوابت Variables & Properties & Constants
المتغير أسرع من الخاصية
بسبب أن الخاصية تحتاج إلى نداء كل من السلوك Get أو Set .
و المتغير الثابت أسرع من المتغير الكلاسيكي كون قيمة المتغير الثابت تجمع وتترجم في النصوص البرمجية بشكل تلقائي ولا يحتاج
إلى جلب قيمته في الذاكرة باستثناء الثوابت من نوع Date و Decimal .
النصيحة: إذا كانت لديك قيم ثابته لا تتغير (مثل اسم قاعدة البيانات) فالمتغير الثابت هو الافضل.
إذا كانت لديك قيم تنتهي بإنتهاء الإجراء Routine فإن المتغير الكلاسيكي ( العادي) هو الأفضل
إما إذا كنت تريد أن تمنع الوصول إلى قيمة المتغير مباشرتاً و مع إختبار تلك القيمة فإن الخاصية هي الوحيدة التي تضمن لك ذلك .
10 – تنصيب الخيارات Options Settings
خيار التصريح Option Explicit On يضمن لك تعريف المتغيرات قبل إستخدامها
لان أي متغير يتم إستخدامه بدون تعريف مسبق سيكون من نوع Object هذا إذا كان الـ Option Explicit Off
للتأكد من أنه على القيمة On من القائمة solution explorer أختر المشروع بالزر الأيمن project ثم خصائص properties
ثم إذهب إلى Build شاهد الصورة للإيضاح .
خيار الحزم Option Strict بحيث يمنعك من مايعرف بـ implicit narrowing
وهي تحويل المتغيرات بدون إستخدام المحولات Ctype أو Cint أو غيرها هذه الطريقة تعرف بـ explicit conversion
أو تحويل قيمة عدد كسري مثلاً إلى عدد صحيح فهذا غير مقبول
السبب إن العدد الكسري لان يتم إسناده في العدد الصحيح إلا بعد إزالة الكسر .
خيار المقارنة بالعد الثنائي Option Compare Binary
هذا الخيار معين ومحدد للتحقق من قيمتين كقيم نصية و فقاً للترتيبها Sorting على اساس العد الثنائي
هذا النوع من المقارنة يعتبر أفضل من غيره كونه لا يلتفت إلى التدقيق الحساس Case Sensitively ولا إلى ترتيب
حروف اللغة وفقاً لترتيبها في كل من الجدول ASCI أو Unicode .
النصيحة : دائماً إجعل خيار التصريح Option Explicit On لضمان إستخدام متغيرات معرفه مسبقاً
و دائماً إجعل خيار الحزم Option Strict لعدم فقدان شيء من قيم المتغيرات
وكذلك إجعل المقارنة ثنائية كافتراض Option Compare Binary لإجل أداء أفضل و سرعة أكثر في المقارنة ,
ربما الصورة أكثر إيضاحً مني.
معلومات سريعة و مختصرة.
11 – العنصرين StreamReader و StreamWriter هما الانجع و الافضل للملفات النصية Text files txt
لانها تعطيك تحكم أكثر من غيرها على الملفات النصية , وهي مهيئة على ANSI Format بينما
الـ TextReader و TextWriter مهيئة على Unicode Format .
12 – العنصر FileStream يقدم أفضل الميزات للتعامل مع الملفات
13 – في العمليات الحسابية القيم الصحيحة أفضل من القيم الكسرية
14 – إذا كنت متأكد أن القيم الرقمية الصحيحة لديك لن تظهر أخطاء أثناء العمليات الحسابية
فقم بإزالة Remove integer overflow checks شاهد الصورة التالية
هذا الخيار يزيد سرعة معالجة العمليات الحسابية للقيم الرقمية الصحيحة
بحاولي 30 إلى 40 في المائة كنسبة مئوية كما يقول المبرمج Balena
و للعلم هذا الخيار ينطبق فقط على المشروع الحالي وليس على كل المشاريع .
15 – لزيادة سرعة تشغيل المشروع قم بتمكين الخيار Enable Optimization بهذا الشكل
16 – إن إستخدم معامل القسمة \ أفضل بعشر مرات من إستخدام معامل باقي القسمة /
17 – عملية الإسناد مع ضمان القيمة الحالية تكون بشكل أفضل بـ a+=1 كونها عمليه واحدة فقط
أما a = a + 1 هنا عمليتان و إن كانت مقروءة لكن الافضل هي الاولى.
18 – عملية التجميع النصي Concatenation تكون بشكل أفضل باستخدام المعامل & بدلاً من +
بسبب أن الاخر يقوم بالتدقيق على نوع القيم مما يقلل الأداء, وبسبب أن المعامل & مصمم أصلاً للقيم النصية , لذلك هو الخيار الافضل.
و الافضل من كلاِ المعاملين هو إستخدام السلوك Append للعنصر StringBuilder راجع كتاب Gotachas للمبرمج Venka في العنوان
GOTCHA #5 String concatenation is expensive
19 – يفضل دائماً التحقق من القيم المنطقية مباشرتاً دون إستخدام معاملٍ ما , كونه ليس ضروري . شاهد المثال
If BoolVariable = True Then ' Unnecessary specification of True. ' ... End If If BoolVariable Then ' More compact. ' ... End If
20 – يفضل دائماً عند إستخدام الشروط المتعددة أن نستخدم المعاملات AndAlso و OrElse فهما أفضل من غيرهم
بسبب أن AndAlso لا يمرر القيمة كلها بل الجزء الاول إذا كان False عُلم أن الشرط غير متحقق فيخرج من الشرط مباشرتاً
أما OrElse فإنه لا يمرر القيمة الثانية إذا نجحت القيمة الأول (لانه تحصيل حاصل ) وبهذا يكونان أسرع من منطق دفعه واحده
والتحقق من الجميع حتى ولو نجحت الأول في Or أو فشلت الأولى في And , فهما بلاشك أنهما الخيار الأفضل في التحقق من الشروط.
هذه الفكرة أشبه بما يعرف بـ Nested If Statement بحيث الـ إذا فشل الشرط الأول لا يتم التح













