
ما مدى سرعة تحليل Kotlin وما أهمية ذلك؟ JavaCC أو ANTLR؟ هل رموز مصدر JetBrains مناسبة؟
قارن ، تخيل وتساءل.
د
JetBrains يصعب سحبها ، ANTLR هو الضجيج ولكن بطيئة بشكل غير متوقع ، و JavaCC من السابق لأوانه شطبه.تحليل ملف Kotlin بسيط مع ثلاثة تطبيقات مختلفة:
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
---|
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
يوم مشمس بخير ...
قررت بناء مترجم في GLSL من بعض اللغات المريحة. كانت الفكرة هي برمجة التظليل مباشرةً في الفكرة والحصول على دعم IDE "مجاني" - اختبارات بناء الجملة وتصحيح الأخطاء والوحدة. اتضح
حقا مريحة للغاية .
منذ ذلك الحين ، ظلت فكرة استخدام Kotlin - يمكنك استخدام اسم vec3 فيه ، فهي أكثر صرامة وأكثر ملاءمة في IDE. بالإضافة إلى ذلك ، هو الضجيج. على الرغم من أنه من وجهة نظر مديري الداخلي ، هذه كلها أسباب غير كافية ، فقد عادت الفكرة مرات عديدة حتى قررت التخلص منها ببساطة عن طريق تنفيذها.
لماذا لا جافا؟ لا يوجد حمل زائد للمشغل ، لذلك سيكون بناء جملة الحساب المتجه مختلفًا كثيرًا عما اعتدت رؤيته في تطوير اللعبةالطائرات النفاثة
قام اللاعبين من JetBrains بتحميل رمز برنامج التحويل البرمجي الخاص بهم
إلى github . كيفية استخدامها ، يمكنك نظرة خاطفة
هنا وهنا .
في البداية كنت أستخدم المحلل اللغوي مع المحلل ، لأن الترجمة إلى لغة أخرى تحتاج إلى معرفة نوع المتغير بدون تحديد نوع
val x = vec3()
بشكل صريح. هنا نوع القارئ واضح ، لكن في AST ، ليس من السهل الحصول على هذه المعلومات ، خاصةً عندما يكون هناك متغير آخر على اليمين أو مكالمة دالة.
هنا شعرت بخيبة أمل. يستغرق الإطلاق الأول للمحلل اللغوي على ملف بدائي 3 ثوان (ثلاثة ثوان).
Kotlin JetBrains parser
first call elapsed : 3254.482ms
min time in next 10 calls: 70.071ms
min time in next 100 calls: 29.973ms
min time in next 1000 calls: 16.655ms
Whole time for 1111 calls: 40.888756 seconds
مثل هذا الوقت لديه المضايقات الواضحة التالية:
- لأنه يتطلب ثلاث ثوانٍ لبدء لعبة أو تطبيق.
- أثناء التطوير ، استخدم عبء تظليل ساخن وشاهد النتيجة فورًا بعد تغيير الكود.
- غالبًا ما أعد تشغيل التطبيق ويسرني أن يبدأ بسرعة كافية (ثانية أو اثنتين).
بالإضافة إلى ثلاث ثوان لتسخين المحلل - وهذا أمر غير مقبول. بالطبع ، أصبح من الواضح على الفور أنه أثناء المكالمات اللاحقة ، ينخفض وقت التحليل إلى 50ms وحتى 20ms ، مما يزيل (تقريبًا) الإزعاج رقم 2 من التعبير. لكن اثنين آخرين لا تذهب إلى أي مكان. بالإضافة إلى ذلك ، 50ms لكل ملف زائد 2500ms لكل 50 ملف (تظليل واحد هو 1-2 الملفات). ماذا لو كان Android؟ (هنا نتحدث فقط عن الوقت.)
الجدير بالذكر هو عمل مجنون من JIT. تحليل الوقت لملف بسيط يسقط من 70ms إلى 16ms. مما يعني ، أولاً ، أن JIT نفسها تستهلك الموارد ، وثانياً ، النتيجة على JVM مختلفة يمكن أن تكون مختلفة جداً.في محاولة لمعرفة من أين جاءت هذه الأرقام ، كان هناك خيار - استخدم المحلل اللغوي دون محلل. بعد كل شيء ، أنا فقط بحاجة لترتيب الأنواع ويمكن القيام بذلك بسهولة نسبية ، في حين يقوم محلل JetBrains بشيء أكثر تعقيدًا ويجمع المزيد من المعلومات. ثم ينخفض وقت بدء التشغيل بمقدار النصف (ولكن لا يزال ما يقرب من ثانية ونصف مناسبًا) ، ويعد وقت المكالمات اللاحقة أكثر إثارة للاهتمام بالفعل - من 8ms في العشرة الأوائل إلى 0.9 م في مكان ما بالآلاف.
Kotlin JetBrains parser (without analyzer)
()
first call elapsed : 1423.731ms
min time in next 10 calls: 8.275ms
min time in next 100 calls: 2.323ms
min time in next 1000 calls: 0.974ms
Whole time for 1111 calls: 3.6884801 seconds
()
first call elapsed : 1423.731ms
min time in next 10 calls: 8.275ms
min time in next 100 calls: 2.323ms
min time in next 1000 calls: 0.974ms
Whole time for 1111 calls: 3.6884801 seconds
اضطررت لجمع مثل هذه الأرقام فقط. وقت الإطلاق الأول مهم عند تحميل التظليل الأول. إنه أمر بالغ الأهمية ، لأنه هنا لا يمكنك صرف انتباه المستخدم أثناء تحميل التظليل في الخلفية ، إنه ينتظر فقط. من المهم حدوث انخفاض في وقت التشغيل لرؤية الديناميكيات نفسها ، وكيفية عمل JIT ، ومدى فعالية تحميل التظليل على تطبيق دافئ.
السبب الرئيسي للنظر في المقام الأول في محلل JetBrains هو الرغبة في استخدام المصنف الخاص بهم. ولكن منذ أن أصبح الرفض هو الخيار الذي تمت مناقشته ، يمكنك محاولة استخدام محلل آخر. بالإضافة إلى ذلك ، من غير المحتمل أن تكون JetBrains أصغر كثيرًا ، وأقل تطلبًا على البيئة ، مع دعم وتضمين الكود في المشروع.
ANTLR
لم يكن هناك محلل على JavaCC ، ولكن على الضجيج ANTLR ، كما هو متوقع ، هناك (
واحد ،
اثنان ).
ولكن ما كان غير متوقع هو السرعة. نفس 3s للتحميل (المكالمة الأولى) و 140 مللي ثانية رائعة للمكالمات اللاحقة. هنا ، لا يستمر الإطلاق الأول فقط لفترة طويلة بشكل غير مريح ، ولكن بعد ذلك لا يتم تصحيح الموقف. يبدو أن اللاعبين من JetBrains قاموا ببعض السحر من خلال السماح لـ JIT بتحسين الكود الخاص بهم بهذه الطريقة. لأنه لم يتم تحسين ANTLR في كل وقت.
Kotlin ANTLR parser
()
first call elapsed : 3705.101ms
min time in next 10 calls: 139.596ms
min time in next 100 calls: 138.279ms
min time in next 1000 calls: 137.20099ms
Whole time for 1111 calls: 161.90619 seconds
()
first call elapsed : 3705.101ms
min time in next 10 calls: 139.596ms
min time in next 100 calls: 138.279ms
min time in next 1000 calls: 137.20099ms
Whole time for 1111 calls: 161.90619 seconds
Javacc
بشكل عام ، نحن مندهشون لرفض خدمات ANTLR. تحليل لا يجب أن يكون هذا الوقت الطويل! لا توجد غموض فلكي في قواعد Kotlin ، وقد راجعت ذلك في ملفات فارغة عملياً. لذا ، فقد حان الوقت للكشف عن JavaCC القديم ، ولف عن سواعدك ، ولا يزال "يفعل ذلك بنفسك وكيف".
هذه المرة تبين أن الأرقام كانت متوقعة ، على الرغم من المقارنة بالبدائل - ممتعة بشكل غير متوقع.
Kotlin JavaCC parser
()
first call elapsed : 19.024ms
min time in next 10 calls: 1.952ms
min time in next 100 calls: 0.379ms
min time in next 1000 calls: 0.114ms
Whole time for 1111 calls: 0.38707677 seconds
()
first call elapsed : 19.024ms
min time in next 10 calls: 1.952ms
min time in next 100 calls: 0.379ms
min time in next 1000 calls: 0.114ms
Whole time for 1111 calls: 0.38707677 seconds
الايجابيات المفاجئة للمحلل JavaCC الخاص بكبالطبع ، بدلاً من كتابة المحلل اللغوي الخاص بك ، أود استخدام حل جاهز. لكن الموجودة منها لها عيوب كبيرة:
- الأداء (توقف مؤقت عند قراءة تظليل جديد غير مقبول ، وكذلك ثلاث ثوان من الاحماء في البداية)
- وقت تشغيل كبير في kotlin ، لست متأكدًا مما إذا كان من الممكن تعبئة المحلل اللغوي مع استخدامه في المنتج النهائي
- بالمناسبة ، في الحل الحالي مع Groovy نفس المشكلة - يمتد وقت التشغيل
بينما يكون محلل JavaCC الناتج هو
+ سرعة ممتازة سواء في البداية أو في العملية
+ مجرد فئات قليلة من المحلل اللغوي نفسه
الاستنتاجات
JetBrains يصعب سحبها ، ANTLR هو الضجيج ولكن بطيئة بشكل غير متوقع ، و JavaCC من السابق لأوانه شطبه.
تحليل ملف Kotlin بسيط مع ثلاثة تطبيقات مختلفة:
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
---|
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 | 1000 () JetBrains 3254 16,6 35.3 JetBrains (w/o analyzer) 1423 0,9 35.3 ANTLR 3705 137,2 1.4 JavaCC 19 0,1 0.8 |
في مرحلة ما ، قررت أن أنظر إلى حجم الجرة بكل التبعيات. تعد JetBrains رائعة كما هو متوقع ،
لكن وقت تشغيل ANTLR يذهل بحجمها .
استكمال: في البداية ، كتبت 15 ميغابايت ، ولكن كما هو مقترح في التعليقات ، إذا قمت بتوصيل وقت التشغيل antlr4 بدلاً من antlr4 ، سينخفض الحجم إلى القيمة المتوقعة. على الرغم من أن محلل JavaCC نفسه لا يزال أصغر بعشر مرات من ANTLR (إذا قمت بإزالة كل الكود على الإطلاق ، باستثناء المحللون أنفسهم).حجم الجرة على هذا النحو مهم ، بالطبع ، للهواتف المحمولة. ولكنه مهم أيضًا لسطح المكتب ، لأنه في الواقع ، يعني مقدار التعليمات البرمجية الإضافية التي يمكن أن تحتوي على أخطاء ، والتي يجب على IDE فهرستها ، والتي تؤثر تمامًا على سرعة التحميل الأول وسرعة الإحماء. بالإضافة إلى ذلك ، بالنسبة للرمز المركب ، هناك أمل ضئيل في الترجمة إلى لغة أخرى.
أنا لا أحثك على حساب عدد الكيلوبايت وأقدر وقت وراحة المبرمج ، ولكن لا يزال الأمر يستحق التفكير في المدخرات ، لأن هذه هي الطريقة التي تصبح بها المشاريع خرقاء ويصعب الحفاظ عليها.
بعض الكلمات عن ANTLR و JavaCCميزة خطيرة من ANTLR هو فصل القواعد النحوية والرمز. سيكون من الجيد إذا لم يكن عليها أن تدفع ثمناً غالياً. نعم ، وهذا مهم فقط بالنسبة إلى "مطوري القواعد النحوية" ، وبالنسبة للمنتجات النهائية ، فإن هذا ليس مهمًا جدًا ، لأنه حتى القواعد النحوية الحالية ستظل بحاجة إلى الانتهاء من كتابة التعليمات البرمجية الخاصة بك. بالإضافة إلى ذلك ، إذا قمنا بتوفير المال وأخذنا قواعد "طرف ثالث" - فقد يكون الأمر غير مريح ، وسيظل بحاجة إلى أن يتم فهمه تمامًا ، وسيحول الشجرة لنفسها. بشكل عام ، JavaCC ، بالطبع ، يمزج الذباب والشرطيس ، لكن هل هذا مهم حقًا وهل هو سيء جدًا؟
ميزة أخرى من ANTLR هي العديد من المنصات المستهدفة. ولكن هنا يمكنك النظر من الجانب الآخر - الكود من أسفل JavaCC بسيط للغاية. ولها بسيطة جدا ... البث! مباشرة مع الكود المخصص - على الأقل في C # ، على الأقل في JS.
PS
كل الشفرة هنا
github.com/kravchik/yastنتيجة التحليل هي شجرة مبنية على YastNode (هذه فئة بسيطة جدًا ، في الواقع - خريطة مع طرق ملائمة ومعرف). لكن YastNode ليست في الواقع "عقدة كروية في فراغ". هذا هو الفصل الذي أستخدمه بنشاط ، وبناءً عليه ، قمت بجمع العديد من الأدوات - مصنِّع ، عدة مترجمين ومُحسِّن / محسّن داخلي.
لا يحتوي محلل JavaCC بعد على جميع القواعد ، حيث تبقى 10 في المائة ، لكن لا يبدو أنها يمكن أن تؤثر على الأداء - لقد راجعت السرعة عند إضافة القواعد ، ولم تتغير بشكل ملحوظ. بالإضافة إلى ذلك ، لقد فعلت بالفعل أكثر بكثير مما كنت بحاجة وحاول فقط مشاركة النتيجة غير المتوقعة الموجودة في هذه العملية.