هل يستحق التحويل من Python إلى Nim للأداء؟

Nim هو مزيج من بناء جملة Python وأداء C







قبل بضعة أسابيع كنت أتصفح GitHub ووجدت مستودعًا غريبًا: تمت كتابة المشروع بالكامل في Nim . لم أقابله من قبل ، وهذه المرة قررت أن أعرف نوع الحيوان الذي كان عليه.



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



فيما يلي استنتاجاتي:



  • هذه اللغة شائعة بالفعل بين دائرة ضيقة من الناس.
  • ربما يجب أن يكون الأمر كذلك.


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



كود في الاستوديو!



كمثال ، قررت أن أكتب شيئًا أكثر تعقيدًا من مرحبًا ، العالم في نيم:







يبدو أنه ليس شيئًا غير ضروري ، أليس كذلك؟ يبدو الأمر بسيطًا للغاية بحيث يمكنك بسهولة معرفة ما يفعله ، حتى لو لم تسمع عن Nim من قبل. (سيخرج البرنامج: "num: 5 i: 5")



لذا ، دعنا نحلل ما يبدو مألوفًا لنا من مكان ما.



إعلان متغير



هذا مألوف بشكل مؤلم لمطوري JavaScript. بينما تستخدم بعض اللغات var وبعضها يستخدم let ، تسمح لك JS و Nim باستخدام كليهما عند التصريح عن المتغيرات. ومع ذلك ، من المهم ملاحظة أنها تعمل بشكل مختلف في Nim عن JS. ولكن أكثر عن ذلك لاحقا.



كتل



للدلالة على كتلة جديدة في نيم ، نستخدم نقطتين متبوعين بخط مسافة بادئة. كل شيء يشبه في بايثون.



الكلمات الدالة



تبدو كلتا الحلقات وعبارة if وكأنهما جزء من كود Python. في الواقع ، كل شيء بدءًا من السطر 5 وما بعده هو كود Python (بافتراض أن لدينا وظيفة صدى محددة).



لذا ، نعم ، يمكن أيضًا استخدام العديد من الكلمات الأساسية وعوامل تشغيل Python في Nim: لا ، ولا ، أو ، وما إلى ذلك.



أي أننا حتى الآن لا نرى شيئًا مميزًا في Nim: أسوأ إصدار من Python (من حيث

التركيب اللغوي) ، مع مراعاة حقيقة أنك بحاجة إلى استخدام let أو var للإعلان عن المتغيرات.



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



حسنا، الآن لمحادثة أخرى. دعونا التحقق من ذلك.



تجربة أداء







قبل أن نتعمق في بناء جملة Nim (خاصة الجزء المكتوب بشكل ثابت الذي لم نره حتى الآن) ، دعنا نحاول تقييم أدائه. للقيام بذلك ، كتبت تطبيقًا ساذجًا لحساب رقم فيبوناتشي التاسع في Nim و Python و C.



لإبقاء الأمور عادلة ، قمت بتوحيد التنفيذ بناءً على حل Leetcode (الخيار 1) وحاولت الالتزام به بدقة قدر الإمكان في جميع اللغات الثلاث.



يمكنك بالطبع تذكيرني بـ LRU Cache . لكن مهمتي في الوقت الحالي هي استخدام نهج قياسي ، وعدم محاولة تحسين الحسابات. لذلك اخترت تطبيقًا ساذجًا.


فيما يلي نتائج حساب رقم فيبوناتشي الأربعين:







نعم ، بالمعنى الدقيق للكلمة ، لا يمكن تسمية التجربة بأنها نظيفة ، لكن هذا يرتبط بنتائج المتحمسين الآخرين الذين أجروا اختبارات أكثر جدية [1] [2] [3] .



كل الكود الذي كتبته لهذه المقالة متاح على GitHub ، بما في ذلك إرشادات حول كيفية إجراء هذه التجربة.



فلماذا نيم أسرع بكثير من بايثون؟



حسنًا ، أود أن أقول إن هناك سببين رئيسيين:



  1. نيم هي لغة مترجمة وبايثون هي لغة مفسرة (المزيد عن ذلك هنا ). هذا يعني أنه يتم إنجاز المزيد من العمل عند تشغيل برنامج Python لأنه يجب تفسير البرنامج قبل تشغيله. هذا عادة ما يجعل اللغة أبطأ.
  2. نيم مكتوب بشكل ثابت. على الرغم من عدم وجود إعلان عن النوع في المثال الذي عرضته سابقًا ، سنرى لاحقًا أنها بالفعل لغة مكتوبة بشكل ثابت. في حالة لغة Python ، التي يتم كتابتها ديناميكيًا ، فإن المترجم الفوري لديه الكثير من العمل الذي يتعين عليه القيام به لتحديد الأنواع والتعامل معها بشكل مناسب. كما أنه يقلل من الأداء.


تنمو سرعة العمل - تنخفض سرعة التشفير



إليك ما تقوله مستندات Python عن اللغات المفسرة:

« / , , ».


هذا تعميم جيد للمفاضلة بين Python و C ، على سبيل المثال. أي شيء يمكنك القيام به في Python يمكنك القيام به في لغة C ، ولكن برنامج C الخاص بك سيعمل بشكل أسرع عدة مرات.



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



لذلك ، إذا كانت Python في أحد طرفي الطيف وكانت C في الطرف الآخر ، فإن Nim يحاول الوصول إلى مكان ما في المنتصف. إنه أسرع بكثير من Python ، ولكن ليس من الصعب برمجته مثل C.



دعونا نلقي نظرة على تطبيقنا لحساب أرقام فيبوناتشي.



من عند:



#include <stdio.h>
int fibonacci(int n) {
    if (n <= 1) {
        return n;
    } 
    return fibonacci(n-1) + fibonacci(n-2);
}

int main(void) {
    printf("%i", fibonacci(40));
}


بايثون:



def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(40))


نيم:



proc fibonacci(n: int): int = 
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

echo(fibonacci(40))


وعلى الرغم من نيم يستخدم علامة "=" في بناء الجملة الداخلي (وظيفة)، بشكل عام انه من الاسهل بكثير لكتابة رمز من في C.



ربما هذا هو حقا مقايضة تستحق؟ أصعب قليلاً في الكتابة من بايثون ، لكنها تعمل أسرع بعشر مرات. يمكنني التعايش معها



تركيب نيم



import strformat

#    https://nim-lang.org/

type
  Person = object
    name: string
    age: Natural #      

let people = [
  Person(name: "John", age: 45),
  Person(name: "Kate", age: 30)
]

for person in people:

  echo(fmt"{person.name} is {person.age} years old")


سأشير فقط إلى الميزات الرئيسية.



المتغيرات



نستخدم var أو let أو const للإعلان عن المتغيرات.



يعمل var و const بنفس الطريقة كما في JavaScript ، ولكن لنفترض أن قصة مختلفة.



يختلف اسم JavaScript عن var من حيث النطاق ، ويشير Nim let إلى متغير لا يمكن أن تتغير قيمته بعد التهيئة. يبدو لي مثل Swift.



لكن أليس هذا هو نفسه ثابت؟ - أنت تسأل.



لا. في نيم ، يكون الفرق بين const و let كما يلي:

بالنسبة لـ const ، يجب أن يكون المترجم قادرًا على تحديد القيمة في وقت الترجمة ، بينما يمكن تحديدها في وقت التشغيل.


مثال من الوثائق:



const input = readLine(stdin) # Error: constant expression expected
let input = readLine(stdin)   #  


بدلاً من ذلك ، يمكن التصريح عن المتغيرات وتهيئتها على النحو التالي:



var
   a = 1
   b = 2
   c = 3
   x, y = 10 #   x  y   10


المهام



تسمى الوظائف في نيم الإجراءات:



proc procedureName(parameterName: parameterType):returnType =
   return returnVar


بالنظر إلى أن اللغة تشبه لغة بايثون من نواح كثيرة ، فإن الإجراءات تبدو غريبة بعض الشيء عندما تراها لأول مرة.



من الواضح أن استخدام "=" بدلاً من "{" أو ":" كل شيء أفضل قليلاً عند كتابة الإجراء في سطر واحد:



proc hello(s: string) = echo s


يمكنك أيضًا الحصول على نتيجة الوظيفة:



proc toString(x: int): string =
   result =
       if x < 0: “negative”
       elif x > 0: “positive”
       else: “zero”


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



يمكنك أيضًا زيادة الإجراءات:




proc toString(x: int): string =   
    result =     
        if x < 0: "negative"     
        elif x > 0: "positive"     
        else: "zero"  
proc toString(x: bool): string =   
    result =     
        if x: "yep"     
        else: "nope"
echo toString(true) #  "yep"
echo toString(5) #  "positive"


الشروط والدورات



لها علاقة كبيرة ببايثون.



# if true:

# while true:

# for num in nums:


إلى كرر أكثر من قائمة، على سبيل المثال، بدلا من مجموعة ()، يمكنك استخدام countup (بداية، والانتهاء) ، أو العد التنازلي (البدء، والانتهاء) . يمكنك القيام بذلك بشكل أسهل واستخدامه لأني في البداية .. إنهاء



مدخلات ومخرجات المستخدم



let input = readLine(stdin)
echo input


بالمقارنة مع Python ، فإن readLine (stdin) يعادل الإدخال () و echo يكافئ الطباعة.



يمكن استخدام echo مع أو بدون أقواس.



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



ميزات اضافية



البرمجة الشيئية



Nim ليست لغة موجهة للكائنات ، لكنها توفر الحد الأدنى من الدعم للعمل مع الكائنات . بالطبع ، هو بعيد عن دروس بايثون.



وحدات الماكرو



يدعم Nim وحدات الماكرو و metaprogramming ، ويبدو أن المطورين يركزون كثيرًا على هذا. هذا هو موضوع القسم الخاص به من سلسلة الدروس الثلاثة.



مثال صغير:



import macros  macro myMacro(arg: static[int]): untyped =  
   echo arg

myMacro(1 + 2 * 3)


أنواع البيانات الأساسية



string, char, bool, int, uint  float.


يمكنك أيضًا استخدام هذه الأنواع:



int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64


بالإضافة إلى ذلك ، فإن الجمل في Nim هي أنواع قابلة للتغيير ، على عكس Python.



تعليقات



على عكس بايثون ، يستخدم Nim الحرف "#" مع "[" و "]" للتعليقات متعددة الأسطر.



# a comment#[
a
multi
line
comment
]#


ترجمة JavaScript



يمكن لـ Nim ترجمة كودها إلى JavaScript. لست متأكدًا مما إذا كان الكثير من الأشخاص يأتون لاستخدام هذا. ولكن هناك مثال على لعبة متصفح Snake مكتوبة بلغة Nim.



التكرارات



تكرارات Nim تشبه مولدات Python:



iterator countup(a, b: int): int =
   var res = a
   while res <= b:
       yield res
       inc(res)


حساسية حالة الأحرف

والشرطة السفلية Nim هي فقط حساسة لحالة الأحرف للحرف الأول.



أي أنه يميز بين HelloWorld و helloWorld ، لكن ليس helloWorld و helloworld و hello_world. لذلك ، سيعمل الإجراء التالي دون مشاكل ، على سبيل المثال:



proc my_func(s: string) =
   echo myFunc("hello")


ما مدى شعبية نيم؟







لدى Nim ما يقرب من 10000 نجمة على GitHub. هذه إضافة واضحة. ومع ذلك ، حاولت تقدير شعبية اللغة من مصادر أخرى ، وبالطبع ليست بهذا الارتفاع.



على سبيل المثال ، لم يتم ذكر Nim حتى في استطلاع 2020 Stack Overflow . لم أتمكن من العثور على وظائف مطور Nim على LinkedIn (حتى مع الجغرافيا العالمية) ، وأدى البحث عن علامة [nim-lang] على Stack Overflow إلى إرجاع 349 سؤالاً فقط (مقارنة بـ 1500000 لـ Python أو 270،000 لـ Swift)



. وبالتالي ، سيكون من العدل الافتراض أن معظم المطورين لم يستخدموها ، ولم يسمع الكثيرون بلغة نيم.



استبدال بايثون؟



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



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



أيضًا ، لا تنسَ لغة Go. أنا متأكد من أن العديد منكم قد فكر في هذا أثناء قراءتك ، وهو محق في ذلك. على الرغم من حقيقة أن بناء جملة Nim أقرب إلى بناء جملة Python ، إلا أنه من حيث الأداء يتنافس بدقة مع لغات مثل "C ++ المبسطة".

لقد اختبرت أداء Go مرة واحدة. على وجه الخصوص ، بالنسبة لـ Fibonacci (40) ، عملت بسرعة مثل C.


لكن لا يزال: هل يستطيع نيم منافسة بايثون؟ أنا أشك في ذلك كثيرا. نحن نشهد اتجاهاً نحو زيادة أداء الكمبيوتر وتبسيط البرمجة. وكما أشرت ، حتى لو كان Nim يقدم مقايضة جيدة في بناء الجملة / الأداء ، لا أعتقد أنه يكفي التغلب على Python النقية والمتعددة الاستخدامات.

لقد تحدثت مع أحد مطوري Nim Core. يعتقد أن نيم هو أكثر ملاءمة لأولئك الذين يهاجرون من C ++ من الثعابين.


هل يستطيع نيم منافسة Go؟ ربما (إذا "سمحت" Google بذلك). لغة نيم قوية مثل جو. علاوة على ذلك ، يتمتع Nim بدعم أفضل لميزات C / C ++ ، بما في ذلك وحدات الماكرو والتحميل الزائد.



لكن المزيد عن ذلك في وقت ما في المرة القادمة.






إعلان



خوادم Epic هي خوادم افتراضية ميسورة التكلفة مع معالجات من AMD ، يصل تردد وحدة المعالجة المركزية إلى 3.4 جيجا هرتز. سيسمح لك الحد الأقصى من التكوين بالخروج إلى أقصى حد - 128 نواة لوحدة المعالجة المركزية و 512 جيجابايت من ذاكرة الوصول العشوائي و 4000 جيجابايت من NVMe اسرع للطلب!






All Articles