Design: الرجاء دعم التسميات التعسفية و Gotos.

تم إنشاؤها على ٨ سبتمبر ٢٠١٦  ·  159تعليقات  ·  مصدر: WebAssembly/design

أود أن أشير إلى أنني لم أشارك في جهود تجميع الويب ،
وأنا لا أحتفظ بأي مجمعات كبيرة أو مستخدمة على نطاق واسع (أنا فقط
لغة لعبة العش ، ومساهمات طفيفة في خلفية مترجم QBE ، و
التدريب على فريق المترجم في IBM) ، لكن انتهى بي الأمر بالحصول على القليل من التشوش ، و
تم تشجيعه على المشاركة على نطاق أوسع.

لذا ، في حين أنني غير مرتاح بعض الشيء للقفز واقتراح تغييرات كبيرة
إلى مشروع لم أعمل فيه ... هنا يذهب:

شكاوي:

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

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

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

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

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

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

Polyfilling:

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

إذا افترضنا وجود LLVM (أو QBE) مثل بناء الجملة لتجميع الويب ، إذن بعض التعليمات البرمجية
يشبه:

int f(int x) {
    if (x == 42)
        return 123;
    else
        return 666;
}

يمكن تجميعها إلى:

 func @f(%x : i32) {
    %1 = test %x 42
jmp %1 iftrue iffalse

 L0:
    %r =i 123
jmp LRet
 L1:
    %r =i 666
jmp LRet
 Lret:
    ret %r
 }

يمكن تعبئة هذا في Javascript الذي يبدو مثل:

function f(x) {
    var __label = L0;
    var __ret;

    while (__label != LRet) {
        switch (__label) {
        case L0:
            var _v1 = (x == 42)
            if (_v1) {__lablel = L1;} else {label = L2;}
            break;
        case L1:
            __ret = 123
            __label = LRet
            break;
        case L2;
            __ret = 666
            __label = LRet
            break;
        default:
            assert(false);
            break;
    }
}

هل هي قبيحة؟ بلى. هل يهم؟ نأمل ، إذا انطلق تجميع الويب ،
ليس لوقت طويل.

وإن لم يكن:

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

ولكن سيكون من الرائع ألا يكون لدينا كلا الجانبين من إنشاء الكود
حول التنسيق المحدد.

control flow

التعليق الأكثر فائدة

سيحصل إصدار Go 1.11 القادم على دعم تجريبي لـ WebAssembly. سيتضمن ذلك الدعم الكامل لجميع ميزات Go ، بما في ذلك goroutines والقنوات وما إلى ذلك. ومع ذلك ، فإن أداء WebAssembly الذي تم إنشاؤه ليس جيدًا حاليًا.

هذا يرجع بشكل أساسي إلى تعليمات goto المفقودة. بدون تعليمات geto ، كان علينا اللجوء إلى استخدام حلقة toplevel والقفز في كل وظيفة. استخدام خوارزمية إعادة التشغيل ليس خيارًا بالنسبة لنا ، لأنه عند التبديل بين goroutines نحتاج إلى أن نكون قادرين على استئناف التنفيذ في نقاط مختلفة من الوظيفة. لا يمكن أن يساعد relooper في ذلك ، فقط تعليمات goto يمكن.

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

ال 159 كومينتر

تم تحسين oridb Wasm إلى حد ما حتى يتمكن المستهلك من التحويل بسرعة إلى نموذج SSA ، والبنية تساعد هنا في أنماط الكود الشائعة ، وبالتالي فإن الهيكل ليس بالضرورة عبئًا على المستهلك. أنا لا أتفق مع تأكيدك أن "كلا الجانبين من إنشاء الكود يعملان حول التنسيق المحدد". Wasm يدور حول المستهلك النحيف والسريع ، وإذا كان لديك بعض المقترحات لجعله أقل نحافة وأسرع ، فقد يكون ذلك بناءً.

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

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

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

الهيكل يساعد هنا لأنماط التعليمات البرمجية الشائعة

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

يمكن التعبير عن الكتل التي يمكن طلبها في DAG في كتل وفروع wasm ، مثل المثال الخاص بك.

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

مرة أخرى ، حجتي ليست أن الحلقات والكتل تجعل الأمور مستحيلة ؛ كل ما يمكنهم فعله هو أبسط وأسهل بالنسبة للآلة للكتابة باستخدام ملصقات goto و goto_if والتعسفية وغير المنظمة.

ربما ألق نظرة على برنامج ثنائي والذي قد يقوم بالكثير من العمل لخلفية المترجم الخاص بك.

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

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

ما زلت غير مقتنع بأن الحلقات لها أي فوائد - أي شيء يمكن تمثيله بحلقة يمكن تمثيله بـ goto والتسمية ، وهناك تحويلات سريعة ومعروفة إلى SSA من قوائم التعليمات الثابتة.

بقدر ما يذهب CPS ، لا أعتقد أنه يجب أن يكون هناك دعم صريح - إنه شائع في دوائر FP لأنه من السهل إلى حد ما التحويل إلى التجميع مباشرة ، ويعطي مزايا مماثلة لـ SSA من حيث التفكير (http: // mlton.org/pipermail/mlton/2003- يناير / 023054.html) ؛ مرة أخرى ، أنا لست خبيرًا في ذلك ، ولكن مما أتذكره ، يتم تخفيض استمرارية الاستدعاء إلى ملصق ، بضع مرات ، ثم الانتقال.

oridb "هناك تحويلات سريعة ومعروفة إلى SSA من قوائم التعليمات الثابتة"

سيكون من المثير للاهتمام معرفة كيفية مقارنتها بأجهزة فك ترميز ASM SSA ، هذا هو السؤال المهم؟

يستفيد Wasm من مجموعة القيم في الوقت الحالي ، وستزول بعض فوائد ذلك بدون البنية ، مما يضر بأداء وحدة فك التشفير. بدون مكدس القيم ، سيكون لفك تشفير SSA المزيد من العمل أيضًا ، لقد جربت رمزًا أساسيًا للتسجيل وكان فك التشفير أبطأ (لست متأكدًا من مدى أهمية ذلك).

هل ستحتفظ بقيم مكدس ، أم تستخدم تصميمًا قائمًا على السجل؟ إذا تم الاحتفاظ بمكدس القيم ، فربما يصبح استنساخًا لـ CIL ، وربما يمكن مقارنة أداء wasm بـ CIL ، فهل قام أي شخص بالتحقق من ذلك بالفعل؟

هل ستحتفظ بقيم مكدس ، أم تستخدم تصميمًا قائمًا على السجل؟

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

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

سيكون من المثير للاهتمام معرفة كيفية مقارنتها بأجهزة فك ترميز ASM SSA ، هذا هو السؤال المهم؟

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

شكرا على أسئلتك واهتماماتك.

من الجدير بالذكر أن العديد من مصممي ومنفذي
WebAssembly لديه خلفيات في JITs الصناعية عالية الأداء ، ليس فقط
لـ JavaScript (V8 و SpiderMonkey و Chakra و JavaScriptCore) ، ولكن أيضًا بتنسيق
LLVM والمجمعين الآخرين. أنا شخصيا قمت بتنفيذ اثنين من JITs لجافا
يمكن أن نشهد أنا و bytecode على أن آلة مكدس تحتوي على عناصر الانتقال غير المقيدة
يقدم بعض التعقيد في فك التشفير والتحقق وبناء ملف
مترجم IR. في الواقع ، هناك العديد من الأنماط التي يمكن التعبير عنها في Java
bytecode الذي سيتسبب في حدوث JITs عالية الأداء ، بما في ذلك C1 و C2 في
HotSpot ببساطة للتخلي عن الكود وإبعاده عن الكود ليتم تشغيله فقط في
مترجم. في المقابل ، فإن إنشاء مترجم IR من شيء مثل
AST من JavaScript أو لغة أخرى هو شيء قمت به أيضًا. ال
الهيكل الإضافي لـ AST يجعل بعض هذا العمل أبسط بكثير.

يبسط تصميم تدفق التحكم في WebAssembly المستهلكين من خلال
تمكين تحقق سريع ، بسيط ، سهل ، تحويل واحد إلى نموذج SSA
(حتى الرسم البياني IR) ، JITs ذات المرور الفردي الفعال ، و (مع postorder و
مكدس) تفسير بسيط نسبيًا في المكان. منظم
التحكم يجعل الرسوم البيانية للتحكم غير القابل للاختزال مستحيلة ، مما يلغي
فئة كاملة من حالات الزاوية السيئة لأجهزة فك التشفير والمترجمات. كذلك
يهيئ بشكل جيد المسرح لمعالجة الاستثناءات في WASM bytecode ، والتي من أجلها V8
تقوم بالفعل بتطوير نموذج أولي بالتنسيق مع الإنتاج
التنفيذ.

لقد أجرينا الكثير من المناقشات الداخلية بين الأعضاء حول هذا الأمر بالذات
الموضوع ، لأنه بالنسبة إلى الرمز الثانوي ، هو الشيء الأكثر اختلافًا عنه
أهداف أخرى على مستوى الآلة. ومع ذلك ، لا يختلف الأمر عن الاستهداف
لغة مصدر مثل JavaScript (وهو ما يفعله العديد من المترجمين هذه الأيام) و
لا يتطلب سوى إعادة تنظيم طفيفة للكتل لتحقيق الهيكل. هناك
هي خوارزميات معروفة للقيام بذلك ، والأدوات. نود أن نقدم بعض
توجيه أفضل لهؤلاء المنتجين مع البدء بـ CFG التعسفي إلى
توصيل هذا بشكل أفضل. للغات التي تستهدف WASM مباشرة من AST
(وهو في الواقع شيء يفعله الآن V8 لرمز asm.js - مباشرة
ترجمة JavaScript AST إلى WASM bytecode) ، لا توجد إعادة هيكلة
خطوة ضرورية. نتوقع أن يكون هذا هو الحال بالنسبة للعديد من أدوات اللغة
عبر الطيف الذي لا يحتوي على IRs المتطورة بالداخل.

يوم الخميس ، 8 سبتمبر 2016 ، الساعة 9:53 صباحًا ، بريد برنشتاين الإخطارات @github.com
كتب:

هل ستحتفظ بقيم مكدس ، أم تستخدم تصميمًا قائمًا على السجل؟

ليس لدي في الواقع أي مشاعر قوية في هذا الصدد. كنت أتخيل
سيكون ضغط التشفير أحد أكبر الاهتمامات ؛ مثلك
المذكورة ، الأداء شيء آخر.

سيكون من المثير للاهتمام معرفة كيفية مقارنتها بأجهزة فك التشفير ASM SSA ، ذلك
هو السؤال المهم؟

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

-
أنت تتلقى هذا لأنك مشترك في هذا الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/WebAssembly/design/issues/796#issuecomment -245521009 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/ALnq1Iz1nn4--NL32R9ev0JPKfEnDyvqks5qn77cgaJpZM4J3ofA
.

شكرًا لـ titzer ، كنت أقوم بتطوير شك في أن بنية Wasm كان لها هدف يتجاوز مجرد التشابه مع asm.js. أتساءل على الرغم من ذلك: Java bytecode (و CIL) لا يصممان CFGs أو مكدس القيمة مباشرة ، يجب أن يستنتجهما JIT. ولكن في Wasm (خاصة إذا تمت إضافة توقيعات الكتلة) ، يمكن لـ JIT بسهولة معرفة ما يحدث مع مكدس القيمة وتدفق التحكم ، لذلك أتساءل ، ما إذا كانت CFGs (أو تدفق التحكم غير القابل للاختزال على وجه التحديد) قد تم تصميمها بشكل صريح مثل الحلقات والكتل ، هل يمكن أن يتجنب ذلك معظم حالات الركن السيئة التي تفكر فيها؟

هناك هذا التحسين الأنيق الذي يستخدمه المترجمون الفوريون والذي يعتمد على تدفق التحكم غير القابل للاختزال لتحسين التنبؤ بالفروع ...

تضمين التغريدة

أود أن أجادل في أن التنسيق المستند إلى الانتقال المسطح سيكون أكثر فائدة بكثير من تنسيق
هدف لمطوري المترجم

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

قد يكون من المفيد التفكير في WebAssembly باعتباره شيئًا محسّنًا لتستهلكه المتصفحات (كما أشار titzer ). ربما لا ينبغي على معظم المترجمين إنشاء WebAssembly مباشرةً ، ولكن بدلاً من ذلك ، يستخدمون أداة مثل Binaryen ، حتى يتمكنوا من إصدار ملفات gotos ، والحصول على مجموعة من التحسينات مجانًا ، ولا يحتاجون إلى التفكير في تفاصيل التنسيق الثنائي منخفضة المستوى لـ WebAssembly (بدلاً من ذلك) تقوم بإصدار IR باستخدام واجهة برمجة تطبيقات بسيطة).

بخصوص polyfilling بنمط while-switch الذي ذكرته: في emscripten بدأنا بهذه الطريقة قبل أن نطور طريقة "relooper" لإعادة إنشاء الحلقات. نمط التبديل while-switch أبطأ بنحو 4x في المتوسط ​​(ولكن في بعض الحالات أقل أو أكثر بشكل ملحوظ ، على سبيل المثال ، تكون الحلقات الصغيرة أكثر حساسية). أتفق معك في أنه من الناحية النظرية ، يمكن أن تؤدي تحسينات خيوط المعالجة السريعة إلى تسريع ذلك ، لكن الأداء سيكون أقل قابلية للتنبؤ لأن بعض الأجهزة الافتراضية ستفعل ذلك بشكل أفضل من غيرها. كما أنه أكبر بشكل ملحوظ من حيث حجم الكود.

قد يكون من المفيد التفكير في WebAssembly باعتباره شيئًا محسّنًا لتستهلكه المتصفحات (كما أشار titzer ). ربما لا ينبغي على معظم المترجمين إنشاء WebAssembly مباشرة ، ولكن بدلاً من ذلك ، يجب استخدام أداة مثل Binaryen ...

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

ولكن إذا كنت تقوم بالتحسين لاستهلاك مستعرض الويب ، فلماذا لا تعرف ببساطة تجميع الويب على أنه SSA ، والذي يبدو لي أكثر انسجامًا مع ما أتوقعه ، وبذل جهد أقل "للتحويل" إلى SSA؟

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

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

oridb راجع تعليق titzer "إن تصميم بنيات التحكم في تدفق WebAssembly يبسط المستهلكين من خلال تمكين تحقق سريع وبسيط وسهل وتحويل واحد إلى نموذج SSA ..." - يمكن أن يولد _ تم التحقق منه _ SSA في مسار واحد. حتى إذا استخدمت wasm SSA للتشفير ، فسيظل لديها عبء التحقق من ذلك ، من حساب بنية المسيطر التي تكون سهلة مع قيود تدفق التحكم في wasm.

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

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

الهدف من wasm هو نقل عبء التحسين خارج مستهلك وقت التشغيل. هناك مقاومة قوية لإضافة التعقيد في برنامج التحويل البرمجي لوقت التشغيل ، حيث إنه يزيد من سطح الهجوم. يتمثل الكثير من تحديات التصميم في التساؤل عما يمكن فعله لتبسيط برنامج التحويل البرمجي لوقت التشغيل دون الإضرار بالأداء ، والكثير من الجدل!

حسنًا ، ربما فات الأوان الآن ، لكني أود أن أتساءل عن فكرة أن خوارزمية إعادة التشغيل ، أو المتغيرات منها ، يمكن أن تنتج نتائج "جيدة بما فيه الكفاية" في جميع الحالات. من الواضح أنها تستطيع في معظم الحالات ، نظرًا لأن معظم التعليمات البرمجية المصدر لا تحتوي على تدفق تحكم غير قابل للاختزال لتبدأ به ، فإن التحسينات لا تجعل الأشياء غالبًا كثيرة الشعر ، وإذا حدث ذلك ، على سبيل المثال كجزء من دمج الكتل المكررة ، فمن المحتمل أن يتم تدريسها لا. لكن ماذا عن الحالات المرضية؟ على سبيل المثال ، ماذا لو كان لديك coroutine قام المترجم بتحويله إلى وظيفة عادية بهيكل مثل pseudo-C:

void transformed_coroutine(struct autogenerated_context_struct *ctx) {
    int arg1, arg2; // function args
    int var1, var2, var3, …; // all vars used by the function
    switch (ctx->current_label) { // restore state
    case 0:
        // initial state, load function args caller supplied and proceed to start
        arg1 = ctx->arg1;
        arg2 = ctx->arg2;
        break;
    case 1: 
        // restore all vars which are live at label 1, then jump there
        var2 = ctx->var2; 
        var3 = ctx->var3;
        goto resume_1;
    [more cases…]
    }

    [main body goes here...]
    [somewhere deep in nested control flow:]
        // originally a yield/await/etc.
        ctx->var2 = var2;
        ctx->var3 = var3;
        ctx->current_label = 1;
        return;
        resume_1:
        // continue on
}

لذلك ، يكون لديك تدفق تحكم طبيعي في الغالب ، ولكن مع وجود بعض الأشياء التي تم توجيهها في منتصفه. هذه هي الطريقة التي تعمل بها coroutines LLVM تقريبًا.

لا أعتقد أن هناك طريقة جيدة لإعادة طرح شيء من هذا القبيل ، إذا كان تدفق التحكم "الطبيعي" معقدًا بدرجة كافية. (قد يكون خاطئًا.) إما أن تقوم بنسخ أجزاء ضخمة من الوظيفة ، وربما تحتاج إلى نسخة منفصلة لكل نقطة إنتاجية ، أو تقوم بتحويل كل شيء إلى مفتاح عملاق ، والذي وفقًا لـ kripken أبطأ 4 مرات من إعادة التشغيل على الكود النموذجي ( التي ربما تكون في حد ذاتها أبطأ إلى حد ما من عدم الحاجة إلى إعادة التشغيل على الإطلاق).

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

ربما يكون القيام بهذا النوع من التحول فكرة سيئة لتبدأ بها ، لأنه بعد ذلك لا شيء يسيطر على أي شيء ، لذا لا تستطيع التحسينات المستندة إلى SSA فعل الكثير ... ربما يكون من الأفضل إجراؤها على مستوى التجميع ، ربما يجب أن تحصل في النهاية على دعم coroutine الأصلي بدلاً من ذلك؟ لكن يمكن للمجمع إجراء معظم التحسينات قبل إجراء التحويل ، ويبدو أن مصممي LLVM coroutines على الأقل لم يروا حاجة ملحة لتأخير التحول حتى إنشاء الكود. من ناحية أخرى ، نظرًا لوجود قدر لا بأس به من التنوع في الدلالات الدقيقة التي يريدها الأشخاص من coroutines (مثل تكرار coroutines المعلقة ، والقدرة على فحص "إطارات المكدس" لـ GC) ، عندما يتعلق الأمر بتصميم رمز ثانوي محمول (بدلاً من رمز ثانوي محمول) مترجم) ، يكون أكثر مرونة لدعم الكود المحول بالفعل بشكل صحيح من جعل الجهاز الظاهري يقوم بالتحويل.

على أي حال ، الكوروتينات هي مجرد مثال واحد. مثال آخر يمكنني التفكير فيه هو تطبيق VM-within-a-VM. في حين أن الميزة الأكثر شيوعًا في JITs هي المخارج الجانبية ، والتي لا تتطلب الانتقال ، إلا أن هناك مواقف تتطلب إدخالات جانبية - مرة أخرى ، تتطلب الانتقال إلى منتصف الحلقات وما شابه. قد يكون المترجمون الفوريون الآخرون هم المترجمون الفوريون المحسّنون: لا يعني ذلك أن المترجمين الفوريين الذين يستهدفون الوسم يمكن أن يتطابقوا حقًا مع تلك التي تستهدف الكود الأصلي ، والتي يمكنها على الأقل تحسين الأداء باستخدام gotos المحسوبة ، ويمكن أن تنغمس في التجميع للمزيد ... ولكن جزءًا من الدافع وراء gotos المحسوبة هو الاستفادة بشكل أفضل متنبئ الفرع من خلال إعطاء كل حالة تعليمات القفز الخاصة بها ، لذلك قد تتمكن من تكرار بعض التأثير من خلال وجود مفتاح منفصل بعد كل معالج رمز تشغيل ، حيث ستكون جميع الحالات مجرد الانتقال. أو لديك على الأقل إذا أو اثنتين للتحقق من الإرشادات المحددة التي تأتي عادةً بعد التعليمات الحالية. هناك بعض الحالات الخاصة لهذا النمط التي يمكن تمثيلها من خلال تدفق تحكم منظم ، ولكن ليس الحالة العامة. وما إلى ذلك وهلم جرا…

بالتأكيد هناك طريقة ما للسماح بتدفق التحكم التعسفي دون جعل الجهاز الظاهري يقوم بالكثير من العمل. قد يتم كسر فكرة رجل القش: يمكن أن يكون لديك مخطط يسمح بالانتقال إلى النطاقات الفرعية ، ولكن فقط إذا كان عدد النطاقات التي يتعين عليك إدخالها أقل من الحد المحدد بواسطة الكتلة الهدف. سيكون الحد افتراضيًا إلى 0 (لا توجد قفزات من النطاقات الأصلية) ، مما يحافظ على الدلالات الحالية ، ولا يمكن أن يكون حد الكتلة أكبر من حد الكتلة الأصلية + 1 (من السهل التحقق). وسيغير VM منهج هيمنته من "X يهيمن على Y إذا كان أحد الوالدين لـ Y" إلى "X يهيمن على Y إذا كان أحد الوالدين لـ Y بمسافة أكبر من حد قفزة الطفل Y". (هذا تقريب متحفظ ، وليس مضمونًا لتمثيل مجموعة المسيطر الدقيق ، ولكن الأمر نفسه ينطبق على الاستدلال الحالي - من الممكن أن تهيمن كتلة داخلية على النصف السفلي من الجزء الخارجي.) نظرًا لأن الكود فقط مع تدفق تحكم غير قابل للاختزال سيحتاج إلى تحديد حد ، فلن يزيد حجم الكود في الحالة الشائعة.

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

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

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

يعد CFG جزءًا فقط من هذا التصميم ، والآخر هو تدفق البيانات ، وهناك مجموعة من القيم والكتل يمكن أيضًا تنظيمها لفك مجموعة القيم التي يمكن أن تنقل النطاق المباشر إلى المستهلك بشكل مفيد مما يوفر العمل الذي يتم تحويله إلى SSA .

من الممكن تمديد wasm ليكون ترميز SSA (أضف pick ، واسمح للكتل بإرجاع قيم متعددة ، ولديك قيم منبثقة لإدخالات الحلقة) ، لذلك من المثير للاهتمام أن القيود المطلوبة لفك تشفير SSA الفعال قد لا تكون ضرورية (لأن يمكن أن يكون بالفعل مشفر SSA)! يؤدي هذا إلى لغة وظيفية (قد تحتوي على ترميز بأسلوب مكدس لتحقيق الكفاءة).

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

(func f1 (arg1)
  (let ((c1 10)) ; Some values up the stack.
    (blocks ((b1 (a1 a2 a3)
                   ... (br b3)
               (br b2 (+ a1 a2 a3 arg1 c1)))
             (b2 (a1)
                 ... (br b1 ...))
             (b3 ()
                 ...))
   .. regular structured wasm ..
   (br b2 ...)
   ....
   (br b3)
    ...
   ))

ولكن هل ستتعامل متصفحات الويب مع هذا الكفاءة داخليًا؟

هل سيتعرف شخص ما بخلفية آلة المكدس على نمط الكود ويكون قادرًا على مطابقته بترميز مكدس؟

هناك بعض المناقشات الشيقة حول الحلقات غير القابلة للاختزال هنا http://bboissin.appspot.com/static/upload/bboissin-thesis-2010-09-22.pdf

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

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

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

للسجل، أنا بقوة معoridb وcomex بشأن هذه المسألة.
أعتقد أن هذه قضية حرجة يجب معالجتها قبل فوات الأوان.

نظرًا لطبيعة WebAssembly ، من المرجح أن تستمر أي أخطاء ترتكبها الآن لعقود قادمة (انظر إلى Javascript!). هذا هو السبب في أن القضية حرجة للغاية. تجنب دعم الانتقال الآن لأي سبب كان (على سبيل المثال لتسهيل التحسين ، وهو - بصراحة تامة - تأثير تنفيذ معين على شيء عام ، وبصراحة ، أعتقد أنه كسول) ، وستنتهي مشاكل على المدى الطويل.

يمكنني بالفعل رؤية تطبيقات WebAssembly المستقبلية (أو الحالية ، ولكن في المستقبل) التي تحاول التعرف على حالة خاصة على أنماط while / switch المعتادة لتنفيذ الملصقات من أجل التعامل معها بشكل صحيح. هذا اختراق.

WebAssembly هو سجلا نظيفا، وحتى الآن هو الوقت المناسب لتجنب الخارقة القذرة (أو بالأحرى، ومتطلبات بالنسبة لهم).

darkuranium :

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

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

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

نظرًا لطبيعة WebAssembly ، من المرجح أن تستمر أي أخطاء ترتكبها الآن لعقود قادمة (انظر إلى Javascript!). هذا هو السبب في أن القضية حرجة للغاية. تجنب دعم الانتقال الآن لأي سبب كان (على سبيل المثال لتسهيل التحسين ، وهو - بصراحة تامة - تأثير تنفيذ معين على شيء عام ، وبصراحة ، أعتقد أنه كسول) ، وستنتهي مشاكل على المدى الطويل.

لذلك سأسمي خدعتك: أعتقد أن وجود الحافز الذي تظهره ، وعدم الخروج بمقترح وتنفيذ كما أشرت أعلاه ، هو كسول تمامًا.

أنا صفيق بالطبع. ضع في اعتبارك أن لدينا أشخاصًا يطرقون أبوابنا للخيوط ، و GC ، و SIMD ، وما إلى ذلك - يقدمون جميعًا حججًا عاطفية ومعقولة حول سبب أهمية ميزتهم - سيكون من الرائع أن تتمكن من مساعدتنا في معالجة إحدى هذه المشكلات. هناك أشخاص يفعلون ذلك من أجل الميزات الأخرى التي أذكرها. لا شيء مقابل goto حتى الآن. يرجى التعرف على إرشادات المساهمة الخاصة بهذه المجموعة والانضمام إلى المرح.

وإلا أعتقد أن goto ميزة مستقبلية رائعة. أنا شخصياً من المحتمل أن أتعامل مع الآخرين أولاً ، مثل إنشاء كود JIT. هذا هو اهتمامي الشخصي بعد GC والخيوط.

مرحبا. أنا في منتصف كتابة ترجمة من webassembly إلى IR والعودة إلى webassembly ، وقد أجريت مناقشة حول هذا الموضوع مع الأشخاص.

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

if (x) goto inside_loop;
// banana
while(y) {
    // things
    inside_loop:
    // do things
}

سينتج مترجمي برنامج EBB ما يلي:

entry:
    cjump x, inside_loop
    // banana
    jump loop

loop:
    cjump y, exit
    // things
    jump inside_loop

inside_loop:
    // do things
    jump loop
exit:
    return

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

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

<inside_loop, if(x)>
    // banana
<loop °>
<exit if(y)>
    // things
</inside_loop, if(x)>
    // do things
</loop ↑>
</exit>

بعد ذلك ، تحتاج إلى بناء كومة من هؤلاء. أي واحد يذهب إلى القاع؟ إنها إما "الحلقة الداخلية" أو أنها "الحلقة". لا يمكننا القيام بذلك ، لذا يتعين علينا قطع المكدس ونسخ الأشياء حولها:

if
    // do things
else
    // banana
end
loop
  br out
    // things
    // do things
end

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

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

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

ولكن على سبيل المثال ، قد يعمل ما يلي بنفس الجودة:

    cjump x, label(1)
    // banana
0: label
    cjump y, label(2)
    // things
1: label
    // do things
    jump label(0)
2: label
    // exit as usual, picking the values from the top of the stack.

ستكون الأرقام ضمنية ، أي .. عندما يرى المترجم "تسمية" ، فإنه يعلم أنه يبدأ كتلة ممتدة جديدة ويعطيه رقم فهرس جديد ، يبدأ في الزيادة من الصفر.

لإنتاج مكدس ثابت ، يمكنك تتبع عدد العناصر الموجودة في المكدس عندما تصادف قفزة في الملصق. إذا كان هناك مكدس غير متناسق بعد القفز إلى الملصق ، فإن البرنامج غير صالح.

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

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

أشعر وكأنني محطم الآن. فقط أعد قراءة مواصفات WebAssembly واكتشف أن تدفق التحكم غير القابل للاختزال قد تم استبعاده عن قصد من MVP ، ربما لسبب اضطر emscripten إلى حل المشكلة في الأيام الأولى.

تم شرح الحل الخاص بكيفية التعامل مع تدفق التحكم غير القابل للاختزال في WebAssembly في الورقة "Emscripten: مترجم LLVM إلى JavaScript". يعيد relooper تنظيم البرنامج مثل هذا:

_b_ = bool(x)
_b_ == 0 if
  // banana
end
block loop
  _b_ if
    // do things
    _b_ = 0
  else
    y br_if 2
    // things
    _b_ = 1
  end
  br 0
end end

كان السبب المنطقي هو أن تدفق التحكم المنظم يساعد في قراءة تفريغ كود المصدر ، وأعتقد أنه يُعتقد أنه يساعد تطبيقات polyfill.

من المحتمل أن يتكيف الأشخاص المترجمون من Webassembly للتعامل مع تدفق التحكم المنهار وفصله.

لذا:

  • كما ذكرنا ، أصبح WebAssembly الآن مستقرًا ، لذا فقد فات الوقت لأي إعادة كتابة كاملة لكيفية التعبير عن تدفق التحكم.

    • من ناحية ، هذا أمر مؤسف ، لأنه لم يختبر أحد فعليًا ما إذا كان الترميز المستند إلى SSA بشكل مباشر أكثر يمكن أن يحقق نفس الاكتناز مثل التصميم الحالي.

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

  • القابلية للتخفيض ليست مفيدة بشكل خاص.

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

    • لقد راجعت تطبيقات WebAssembly الحالية في JavaScriptCore و V8 و SpiderMonkey ، ويبدو أنها جميعًا تتبع هذا النمط. (يعتبر V8 أكثر تعقيدًا - نوع من تمثيل "بحر العقد" بدلاً من الكتل الأساسية - ولكنه أيضًا يرمي الهيكل المتداخل بعيدًا.)

    • استثناء : يمكن أن يكون تحليل الحلقة مفيدًا ، وتمرير جميع هذه التطبيقات الثلاثة عبر المعلومات إلى IR حول الكتل الأساسية التي تمثل بداية الحلقات. (قارن بـ LLVM التي ، بصفتها واجهة خلفية "ثقيلة الوزن" مصممة لتجميع AOT ، تتخلص منها وتعيد حسابها في الواجهة الخلفية. وهذا أكثر قوة ، حيث يمكنه العثور على أشياء لا تشبه الحلقات في الكود المصدري ولكنها تفعل بعد مجموعة من التحسينات ، ولكن بشكل أبطأ.)

    • يعمل تحليل الحلقة على "الحلقات الطبيعية" ، والتي تمنع الفروع في منتصف الحلقة التي لا تمر عبر رأس الحلقة.

    • يجب أن يستمر WebAssembly في ضمان أن تكون كتل loop حلقات طبيعية.

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

    • يجعل تدفق التحكم غير القابل للاختزال من الصعب ترجمة WebAssembly إلى JavaScript (polyfilling) ، حيث يتعين على المترجم تشغيل خوارزمية إعادة التشغيل نفسها.

    • لكن WebAssembly يتخذ بالفعل قرارات متعددة تضيف مقدارًا كبيرًا من وقت التشغيل إلى أي نهج ترجمة إلى JS (بما في ذلك دعم الوصول إلى الذاكرة غير المحاذاة وتعويض عمليات الوصول خارج الحدود) ، مما يشير إلى أنه لا يعتبر مهمًا للغاية.

    • بالمقارنة مع ذلك ، فإن جعل المترجم أكثر تعقيدًا ليس بالأمر الكبير.

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

  • المعلومات الرئيسية اللازمة لبناء تمثيل SSA (والتي ، حسب التصميم ، يجب أن تكون ممكنة في مسار واحد) هي شجرة المسيطر .

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

    • block :



      • يسيطر BB السابق على BB الذي يبدأ بالكتلة. *


      • BB الذي يلي end يهيمن عليه BB الذي يبدأ الكتلة ، ولكن ليس بواسطة BB قبل end (لأنه سيتم تخطيه إذا كان هناك br خارج ).



    • loop :



      • يسيطر BB السابق على BB الذي يبدأ بالكتلة.


      • يهيمن BB بعد end من قبل BB قبل end (نظرًا لأنه لا يمكنك الوصول إلى التعليمات بعد end إلا من خلال تنفيذ end ).



    • if :



      • كل من جانب if والجانب الآخر و BB بعد end كلها تخضع لسيطرة BB قبل if .



    • br ، return ، unreachable :



      • (لا يمكن الوصول إلى BB مباشرة بعد br أو return أو unreachable .)



    • br_if ، br_table :



      • BB قبل br_if / br_table يهيمن على واحد بعده.



    • والجدير بالذكر أن هذا مجرد تقدير. لا يمكن أن ينتج إيجابيات خاطئة (قول أ يسيطر على ب في حين أنه لا يفعل ذلك في الواقع) لأنه يقول ذلك فقط عندما لا توجد طريقة للوصول إلى ب دون المرور من خلال أ ، عن طريق البناء. ولكن يمكن أن ينتج عنه سلبيات خاطئة (القول إن A لا يهيمن على B في حين أنه يفعل ذلك بالفعل) ، ولا أعتقد أن خوارزمية المرور الفردي يمكنها اكتشاف ذلك (قد يكون خاطئًا).

    • مثال سلبي كاذب:

      ""

      حظر $ الخارجي

      عقدة

      br $ الخارجي ؛؛ منذ كسر هذا دون قيد أو شرط ، فإنه يسيطر سرا على نهاية BB

      نهاية

      نهاية

    • لكن هذا جيد ، AFAIK.



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


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



    • على أي حال ، فكر في كيفية عمل تعليمات goto من حيث شجرة المسيطر. افترض أن أ يسيطر على ب ، الذي يهيمن على ج.

    • لا يمكننا القفز من A إلى C لأن ذلك سيتخطى B (انتهاكًا لافتراض الهيمنة). بعبارة أخرى ، لا يمكننا القفز إلى أحفاد غير مباشرين. (وفي نهاية منتج النظام الثنائي ، إذا قاموا بحساب شجرة المسيطر الحقيقية ، فلن تكون هناك مثل هذه القفزة أبدًا.)

    • يمكننا القفز بأمان من A إلى B ، لكن الانتقال إلى سليل مباشر ليس مفيدًا. إنه مكافئ أساسًا لبيان if أو switch ، وهو ما يمكننا فعله بالفعل (باستخدام التعليمات if إذا كان هناك اختبار ثنائي فقط ، أو br_table إذا كان هناك عدة).

    • ومن الأمور الآمنة والأكثر إثارة للاهتمام أيضًا القفز إلى الأخ أو شقيق الجد. إذا قفزنا إلى أخوتنا ، فقد حافظنا على ضمان أن والدنا يهيمن على أخوتنا ، لأننا يجب أن نكون قد أعدمنا والدنا بالفعل للوصول إلى هنا (لأنه يهيمن علينا أيضًا). وبالمثل بالنسبة للأسلاف.

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

  • بناءً على ذلك ، إليك اقتراح مبتذل:

    • تعليمات واحدة جديدة من نوع الكتلة:
    • تسميات resulttype N instr * end
    • يجب أن تكون هناك إرشادات فورية من N للأطفال ، حيث يعني "الطفل المباشر" إما تعليمات من نوع الكتلة ( loop ، block ، أو labels ) وكل شيء يصل إلى المقابل end ، أو تعليمة واحدة غير مجمعة (والتي يجب ألا تؤثر على المكدس).
    • بدلاً من إنشاء ملصق واحد مثل إرشادات نوع الكتلة الأخرى ، ينشئ labels تسميات N + 1: N يشير إلى N الفرعية ، ويشير أحدهم إلى نهاية الكتلة labels . في كل من الأطفال ، تشير مؤشرات التسمية من 0 إلى N-1 إلى الأطفال ، بالترتيب ، ويشير مؤشر التسمية N إلى النهاية.

    بمعنى آخر ، إذا كان لديك
    loop ;; outer labels 3 block ;; child 0 br X end nop ;; child 1 nop ;; child 2 end end

    اعتمادًا على X ، يشير br إلى:

    | X | الهدف |
    | ---------- | ------ |
    | 0 | نهاية ال block |
    | 1 | الطفل 0 (بداية block ) |
    | 2 | الطفل 1 (nop) |
    | 3 | الطفل 2 (nop) |
    | 4 | نهاية labels |
    | 5 | بداية الحلقة الخارجية |

    • يبدأ التنفيذ من الطفل الأول.

    • إذا وصل الإعدام إلى نهاية أحد الأطفال ، فإنه يستمر إلى التالي. إذا وصل إلى نهاية الطفل الأخير ، فإنه يعود إلى الطفل الأول. (هذا من أجل التناظر ، لأن ترتيب الأطفال لا يُقصد به أن يكون مهمًا).

    • يؤدي التفرع إلى أحد الأطفال إلى فك حزمة المعامل حتى عمقها في بداية labels .

    • وكذلك الحال بالنسبة للتفرع حتى النهاية ، ولكن إذا كانت فارغة ، فإن التفرع إلى النهاية يؤدي إلى ظهور مُعامل ويدفعه بعد فك اللف ، على غرار block .

    • الهيمنة: الكتلة الأساسية قبل تعليمات labels تهيمن على كل طفل ، بالإضافة إلى BB بعد نهاية labels . الأطفال لا يسيطرون على بعضهم البعض أو النهاية.

    • ملاحظات التصميم:

    • تم تحديد N مقدمًا بحيث يمكن التحقق من صحة الرمز في مسار واحد. سيكون من الغريب الوصول إلى نهاية الكتلة labels ، لمعرفة عدد الأطفال ، قبل معرفة أهداف المؤشرات الموجودة فيها.

    • لست متأكدًا مما إذا كان يجب أن تكون هناك طريقة في النهاية لتمرير القيم على مكدس المعامل بين التسميات ، ولكن عن طريق القياس مع عدم القدرة على تمرير القيم إلى block أو loop ، يمكن أن يكون غير مدعوم للبدء مع.

سيكون من الرائع حقًا إذا كان من الممكن القفز في حلقة ، أليس كذلك؟ IIUC ، إذا تم حساب هذه الحالة ، فلن تكون هناك حاجة أبدًا للحلقة السيئة + br_table combo ...

تحرير: أوه ، يمكنك عمل حلقات بدون loop خلال القفز لأعلى في labels . لا أصدق أنني فاتني ذلك.

qwertie إذا كانت الحلقة المعينة ليست حلقة طبيعية ، يجب أن يعبر المترجم الذي يستهدف labels بدلاً من loop . يجب ألا يكون من الضروري أبدًا إضافة مفتاح للتعبير عن تدفق التحكم ، إذا كان هذا هو ما تشير إليه. (بعد كل شيء ، في أسوأ الأحوال ، يمكنك فقط استخدام كتلة عملاقة واحدة labels مع تسمية لكل كتلة أساسية في الوظيفة. هذا لا يسمح للمجمع بمعرفة الهيمنة والحلقات الطبيعية ، لذلك قد تفوتك التحسينات. ولكن labels مطلوب فقط في الحالات التي لا تنطبق فيها هذه التحسينات.)

هيكل الحلقة المتداخلة ، الشيء الذي تضمنه قابلية الاختزال ، يتم إلقاؤه إلى حد كبير في البداية. [...] لقد راجعت تطبيقات WebAssembly الحالية في JavaScriptCore و V8 و SpiderMonkey ، ويبدو أنها جميعًا تتبع هذا النمط.

ليس تمامًا: على الأقل في SM ، فإن مخطط الأشعة تحت الحمراء ليس رسمًا بيانيًا عامًا بالكامل ؛ نحن نفترض بعض ثوابت الرسم البياني التي تنشأ من مصدر منظم (JS أو wasm) وغالبًا ما نبسط و / أو نحسن الخوارزميات. قد يتطلب دعم CFG العام بالكامل إما تدقيق / تغيير العديد من التمريرات في خط الأنابيب لعدم تحمل هذه المتغيرات (إما عن طريق تعميمها أو تشاؤمها في حالة عدم الاختزال) أو تكرار تقسيم العقدة مقدمًا لجعل الرسم البياني قابلًا للاختزال. هذا بالتأكيد ممكن بالطبع ، لكن ليس صحيحًا أن هذا مجرد مسألة كون كونه عنق الزجاجة مصطنعًا.

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

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

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

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

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

بالنسبة لي هذا هو الأخير. يتوقع اقتراحي أن يستمر المنتجون في تشغيل خوارزمية من نوع إعادة التشغيل لحفظ عمل الواجهة الخلفية لتحديد العناصر المسيطرة والحلقات الطبيعية ، والتراجع إلى labels فقط عند الضرورة. ومع ذلك ، فإن هذا من شأنه أن يجعل المنتجين أبسط. إذا كان لتدفق التحكم غير القابل للاختزال عقوبة كبيرة ، فيجب على المنتج المثالي أن يعمل بجد لتجنب ذلك ، باستخدام الأساليب التجريبية لتحديد ما إذا كان من الأفضل تكرار الكود ، أو الحد الأدنى من التكرار الذي يمكن أن ينجح ، وما إلى ذلك. up loop ، هذا ليس ضروريًا حقًا ، أو على الأقل ليس ضروريًا أكثر مما سيكون عليه مع الواجهة الخلفية لشفرة الجهاز العادية (التي لها تحسينات الحلقة الخاصة بها).

يجب أن أجمع المزيد من البيانات حول مدى شيوع تدفق التحكم غير القابل للاختزال عمليًا ...

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

أنا منفتح على الإقناع بأن إيماني غير صحيح.

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

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

بالتناوب ، قد يكون اقتراحي بالفعل بمثابة مبدأ بدائي لائق للمترجمين الفوريين. إذا جمعت بين labels br_table ، فستحصل على القدرة على توجيه جدول القفز مباشرة إلى نقاط عشوائية في الوظيفة ، والتي لا تختلف كثيرًا عن geto المحسوب. (على عكس مفتاح C ، الذي يوجه في البداية على الأقل تدفق التحكم إلى نقاط داخل كتلة المحول ؛ إذا كانت جميع الحالات كلها الانتقال ، يجب أن يكون المترجم قادرًا على تحسين القفزة الإضافية بعيدًا ، ولكنه قد يندمج أيضًا مع "فائض" متعدد قم بتبديل العبارات إلى واحدة ، مما يؤدي إلى تدمير فائدة وجود قفزة منفصلة بعد كل معالج تعليمات.) لست متأكدًا من المشكلة المتعلقة بتخصيص السجل ، على الرغم من ...

comex أعتقد أنه يمكن للمرء ببساطة إيقاف تشغيل تمريرات التحسين بالكامل على مستوى الوظيفة في وجود تدفق تحكم غير قابل للاختزال (على الرغم من الحاجة إلى إنشاء SSA و regalloc وعدد قليل من الآخرين وبالتالي يتطلب العمل) ، لكنني كنت أفترض أننا أراد بالفعل إنشاء رمز جودة للوظائف ذات تدفق تحكم غير قابل للاختزال ويتضمن ذلك مراجعة كل خوارزمية كانت تفترض سابقًا رسمًا بيانيًا منظمًا.

>

هيكل الحلقة المتداخلة ، الشيء الذي تضمنه قابلية الاختزال ، هو
إلى حد كبير في البداية. [...] راجعت التيار
تطبيقات WebAssembly في JavaScriptCore و V8 و SpiderMonkey و
يبدو أنهم جميعًا يتبعون هذا النمط.

ليس تمامًا: على الأقل في SM ، فإن مخطط الأشعة تحت الحمراء ليس رسمًا بيانيًا عامًا بالكامل ؛ نحن
افترض بعض ثوابت الرسم البياني التي تتبع من أن يتم إنشاؤها من
مصدر منظم (JS أو wasm) وغالبًا ما يبسط و / أو يحسن ملف
الخوارزميات.

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

كما يقول lukewagner ، من المحتمل أن تكون حالة الاستخدام الرئيسية للتحكم غير القابل للاختزال
"رمز مترابط" للمترجمين الفوريين المحسنين. من الصعب القول مدى صلة هؤلاء
هي لمجال Wasm ، وما إذا كان غيابه هو الأكبر في الواقع
عنق الزجاجة.

بعد مناقشة تدفق التحكم غير القابل للاختزال مع عدد من الأشخاص
البحث عن الـ IRs للمترجم ، ربما يكون الحل "الأنظف" هو الإضافة
فكرة الكتل العودية المتبادلة. هذا من شأنه أن يتناسب مع واسم
هيكل التحكم بشكل جيد.

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

من الذاكرة ، وربما خطأ ، SPEC2006 لديه حلقة واحدة غير قابلة للاختزال في 401.bzip2 وهذا كل شيء. إنه نادر جدًا في الممارسة.

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

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

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

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

الأفضل،
بن

يوم الخميس ، 20 أبريل 2017 الساعة 5:20 صباحًا ، جاكوب ستوكلوند أولسن <
[email protected]> كتب:

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

من الذاكرة ، وربما الخطأ ، يحتوي SPEC2006 على حلقة واحدة غير قابلة للاختزال في
401.bzip2 وهذا كل شيء. إنه نادر جدًا في الممارسة.

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

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/WebAssembly/design/issues/796#issuecomment-295352983 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/ALnq1K99AR5YaQuNOIFIckLLSIZbmbd0ks5rxkJQgaJpZM4J3ofA
.

أستطيع أيضًا أن أقول أنه إذا تمت إضافة بنيات غير قابلة للاختزال إليها
WebAssembly ، فلن يعملوا في TurboFan (V8's Opt الأمثل JIT) ، لذلك هكذا
قد تنتهي وظائف إما أن يتم تفسيرها (بطيئة للغاية) أو يتم تفسيرها
تم تجميعها بواسطة مترجم أساسي (أبطأ إلى حد ما) ، لأننا على الأرجح لن نفعل ذلك
استثمر الجهد في ترقية TurboFan لدعم تدفق التحكم غير القابل للاختزال.
وهذا يعني أن وظائف ذات تدفق تحكم غير قابل للاختزال في WebAssembly
ربما ينتهي الأمر بأداء أسوأ بكثير.

بالطبع ، هناك خيار آخر لمحرك WebAssembly في V8 لتشغيل ملف
relooper لتغذية الرسوم البيانية القابلة للاختزال TurboFan ، ولكن هذا من شأنه أن يجعل التجميع
(وبدء التشغيل أسوأ). يجب أن يظل إعادة التوطين إجراء غير متصل بالشبكة في بلدي
الرأي ، وإلا فإننا سننتهي بتكاليف لا مفر منها للمحرك.

الأفضل،
بن

في يوم الإثنين 1 مايو 2017 الساعة 12:48 ظهرًا ، كتب بن إل تيتزر [email protected] :

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

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

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

الأفضل،
بن

يوم الخميس ، 20 أبريل 2017 الساعة 5:20 صباحًا ، جاكوب ستوكلوند أولسن <
[email protected]> كتب:

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

من الذاكرة ، وربما يكون خاطئًا ، يحتوي SPEC2006 على حلقة واحدة غير قابلة للاختزال
في 401.bzip2 وهذا كل شيء. إنه نادر جدًا في الممارسة.

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

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/WebAssembly/design/issues/796#issuecomment-295352983 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/ALnq1K99AR5YaQuNOIFIckLLSIZbmbd0ks5rxkJQgaJpZM4J3ofA
.

توجد طرق ثابتة للتحقق الزمني الخطي من تدفق التحكم غير القابل للاختزال. أحد الأمثلة البارزة هو JVM: باستخدام خرائط المكدس ، فإنه يحتوي على التحقق من الوقت الخطي. يحتوي WebAssembly بالفعل على توقيعات حظر على كل بنية تشبه الكتلة. مع معلومات الكتابة الصريحة في كل نقطة يتم فيها دمج مسارات تدفق تحكم متعددة ، ليس من الضروري استخدام خوارزميات النقطة الثابتة.

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

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

تنهد ... قصدت الرد مبكرا ولكن الحياة أعاقت الطريق.

لقد كنت أتصفح بعض محركات JS وأعتقد أنه يجب علي إضعاف ادعائي حول تدفق التحكم غير القابل للاختزال "العمل فقط". ما زلت لا أعتقد أنه سيكون من الصعب أن نجعلها تعمل ، ولكن هناك بعض التركيبات التي سيكون من الصعب تكييفها بطريقة قد تستفيد بالفعل من ...

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

داخل الواجهة الخلفية ، تعامل مع كتلة labels كما لو كانت حلقة + تبديل حتى الدقيقة الأخيرة. بمعنى آخر ، عندما ترى كتلة labels ، فإنك تتعامل معها كرأس حلقة مع حافة خارجية تشير إلى كل ملصق ، وعندما ترى branch يستهدف تسمية ، فإنك تنشئ ميزة لافتا إلى labels رأس، وليس التسمية الهدف الفعلي - التي يجب تخزينها على حدة في مكان ما. لا حاجة لإنشاء متغير فعلي لتخزين التسمية المستهدفة ، مثل حلقة حقيقية + مفتاح ما يجب القيام به ؛ يجب أن يكون كافيًا لتخزين القيمة في بعض مجالات تعليمات الفرع ، أو إنشاء تعليمات تحكم منفصلة لهذا الغرض. بعد ذلك ، يمكن للتحسينات والجدولة وحتى تسجيل التخصيص أن تتظاهر بوجود قفزتين. ولكن عندما يحين الوقت لإنشاء تعليمات قفزة أصلية فعليًا ، يمكنك التحقق من هذا الحقل وإنشاء قفزة مباشرة إلى التسمية الهدف.

قد تكون هناك مشكلات في ، على سبيل المثال ، أي تحسين يقوم بدمج / حذف الفروع ، ولكن يجب أن يكون من السهل جدًا تجنب ذلك ؛ تعتمد التفاصيل على تصميم المحرك.

بمعنى ما ، اقتراحي مكافئ لـ @ titzer "تحسين الانتقال السريع المحلي البسيط". أقترح جعل تدفق التحكم "الأصلي" غير القابل للاختزال يبدو وكأنه حلقة + مفتاح ، لكن البديل سيكون تحديد حلقة + مفاتيح فعلية - أي نمط @ titzer حيث يحدث تخصيص ثابت لمتغير محلي ، ثم فرع إلى فرع شرطي يقوم على الفور بالتبديل على هذا المتغير المحلي "- وإضافة بيانات وصفية تسمح بإزالة الفرع غير المباشر في وقت متأخر من خط الأنابيب. إذا أصبح هذا التحسين في كل مكان ، فقد يكون بديلاً مناسبًا لتعليمات صريحة.

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

a:
  control = 1;
  goto x;
b:
  control = 2;
  goto x;
...
x:
  // use control

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

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

بالنسبة للتمثيل ، أيهما أفضل: صريح (تعليمات labels أو ما شابه ذلك) أم ضمنيًا (أمثلية حلقة حقيقية + مفاتيح باتباع نمط معين)؟

فوائد ضمنية:

  • يحافظ على المواصفات ضعيفة.

  • قد يعمل بالفعل مع الحلقة الحالية + رمز التبديل. لكني لم ألقي نظرة على الأشياء التي يولدها برنامج Binaryen لمعرفة ما إذا كان يتبع نمطًا صارمًا بدرجة كافية.

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

عيوب ضمنية:

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

  • ينشئ "جرفًا أمثلية" ، والذي من المفترض عمومًا أن يتجنبه WebAssembly مقارنة بـ JS. لإعادة الفرز ، فإن النمط الأساسي المطلوب تحسينه هو "حيث يحدث تخصيص ثابت لمتغير محلي ، ثم فرع إلى فرع شرطي يقوم على الفور بتبديل ذلك المتغير المحلي". ولكن ماذا لو ، على سبيل المثال ، هناك بعض الإرشادات الأخرى بينهما ، أو أن المهمة لا تستخدم في الواقع تعليمات wasm const ولكن مجرد شيء يُعرف بالثابت بسبب التحسينات؟ قد تكون بعض المحركات أكثر ليبرالية من غيرها فيما يتعرفون عليه على أنه هذا النمط ، ولكن بعد ذلك سيكون للكود الذي يستفيد من ذلك (بقصد أو بغير قصد) أداء مختلف تمامًا بين المتصفحات. وجود تشفير أكثر وضوحًا يحدد التوقعات بشكل أكثر وضوحًا.

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

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

...

بالعودة إلى مسألة دعم التدفق غير القابل للاختزال محليًا - ما هي العقبات ، ومقدار الفائدة - إليك بعض الأمثلة المحددة من IonMonkey لممرات التحسين التي يجب تعديلها لدعمها:

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

-> لذلك يجب أن يكون هناك بعض علامات الحلقة للكتل labels . في هذه الحالة ، أعتقد أن وضع علامة على كتلة labels بأكملها على أنها حلقة "ستعمل فقط" (بدون تمييز التسميات الفردية بشكل خاص) ، لأن التحليل غير دقيق للغاية بحيث لا يهتم بتدفق التحكم داخل الحلقة.

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

-> Messier لأنه يفترض (أ) ظهور أسلاف الكتل الأساسية الفردية دائمًا قبلها باستثناء الحلقات المدعومة ، و (ب) يمكن أن تحتوي الحلقة على دعم واحد فقط. هناك طرق مختلفة لإصلاح هذا الأمر ، ولكن من المحتمل أن يتطلب الأمر معالجة صريحة لـ labels ، ولكي تظل الخوارزمية خطية ، من المحتمل أن تعمل بشكل فظ في هذه الحالة ، مثل AliasAnalysis العادي

BacktrackingAllocator.cpp: سلوك مشابه لتخصيص السجل: يقوم بتمرير عكسي خطي من خلال قائمة التعليمات ويفترض أن جميع استخدامات التعليمات ستظهر بعد (أي أن تتم معالجتها من قبل) تعريفها ، إلا عند مواجهة حلقة مدعومة: السجلات التي هي عش في بداية الحلقة ، ببساطة ابق على قيد الحياة من خلال الحلقة بأكملها.

-> يجب معاملة كل تسمية كرأس حلقة ، ولكن يجب أن تمتد الحيوية لتشمل مجموعة الملصقات بأكملها. ليس من الصعب التنفيذ ، ولكن مرة أخرى ، لن تكون النتيجة أفضل من نهج الاختراق. أعتقد.

comex هناك اعتبار آخر هنا وهو مقدار ما يتوقع أن تقوم به محركات wasm. على سبيل المثال ، ذكرت تحليل Ion AliasAnalysis أعلاه ، ولكن الجانب الآخر من القصة هو أن تحليل الاسم المستعار ليس مهمًا لرمز WebAssembly ، على الأقل في الوقت الحالي أثناء استخدام معظم البرامج للذاكرة الخطية.

قد تتطلب خوارزمية Ion BacktrackingAllocator.cpp الحيوية بعض العمل ، لكنها لن تكون باهظة. يتعامل معظم Ion بالفعل مع أشكال مختلفة من تدفق التحكم غير القابل للاختزال ، حيث يمكن لـ OSR إنشاء إدخالات متعددة في حلقات.

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

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

أنا مهتم باستخدام WebAssembly كخلفية qemu على iOS ، حيث WebKit (والرابط الديناميكي ، ولكنه يتحقق من توقيع الكود) هو البرنامج الوحيد المسموح به لتمييز الذاكرة على أنها قابلة للتنفيذ. يفترض كودجين Qemu أن عبارات goto ستكون جزءًا من أي معالج يجب ترميزه من أجله ، مما يجعل WebAssembly الخلفية شبه مستحيلة بدون إضافة goto.

tbodt - هل ستتمكن من استخدام أداة إعادة تشغيل Binaryen؟ هذا يتيح لك إنشاء ما هو أساسًا Wasm-with-goto ثم تحويله إلى تدفق تحكم منظم لـ Wasm.

eholk يبدو أنه سيكون أبطأ بكثير من الترجمة المباشرة لرمز الآلة إلى wasm.

tbodt يؤدي استخدام Binaryen إلى إضافة IR إضافي في الطريق ، نعم ، لكن لا ينبغي أن يكون أبطأ كثيرًا ، على ما أعتقد ، لقد تم تحسينه لسرعة الترجمة. وقد يكون لها أيضًا فوائد أخرى غير التعامل مع gotos وما إلى ذلك ، حيث يمكنك اختياريًا تشغيل مُحسِّن Binaryen ، والذي قد يفعل أشياء لا يفعلها مُحسِّن qemu (أشياء خاصة بـ wasm).

في الواقع ، سأكون مهتمًا جدًا بالتعاون معك في ذلك ، إذا كنت تريد :) أعتقد أن نقل Qemu إلى wasm سيكون مفيدًا للغاية.

لذا ، في التفكير الثاني ، لن تساعد الأشياء الأساسية كثيرًا حقًا. يولد كودجين Qemu كود الكتل الأساسية عند تشغيلها لأول مرة. إذا قفزت كتلة إلى كتلة لم يتم إنشاؤها بعد ، فإنها تقوم بإنشاء الكتلة وتصحيح الكتلة السابقة مع الانتقال إلى الكتلة التالية. تحميل التعليمات البرمجية الديناميكية وتصحيح الوظائف الحالية ليست من الأشياء التي يمكن القيام بها في Webassembly ، على حد علمي.

kripken سأكون مهتمًا بالتعاون ، ما هو أفضل مكان للدردشة معك؟

لا يمكنك تصحيح الوظائف الحالية مباشرةً ، ولكن يمكنك استخدام call_indirect و WebAssembly.Table لكود jit. بالنسبة لأي كتلة أساسية لم يتم إنشاؤها ، يمكنك الاتصال بـ JavaScript ، وإنشاء وحدة WebAssembly والمثيل بشكل متزامن ، واستخراج الوظيفة المصدرة وكتابتها على الفهرس في الجدول. ستستخدم المكالمات المستقبلية بعد ذلك الوظيفة التي تم إنشاؤها.

لست متأكدًا من أن أي شخص قد جرب هذا حتى الآن ، لذلك من المحتمل أن يكون هناك العديد من الجوانب الصعبة.

يمكن أن ينجح ذلك إذا تم تنفيذ المكالمات الخلفية. وإلا فإن المكدس سوف يفيض بسرعة كبيرة.

التحدي الآخر هو تخصيص مساحة في الجدول الافتراضي. كيف يمكنك تعيين عنوان إلى فهرس الجدول؟

خيار آخر هو تجديد وظيفة wasm على كل كتلة أساسية جديدة. هذا يعني أن عددًا من عمليات إعادة التحويل البرمجي يساوي عدد الكتل المستخدمة ، لكنني أراهن أنها الطريقة الوحيدة لتشغيل الشفرة بسرعة بعد تجميعها (خاصة الحلقات الداخلية) ، ولا يلزم أن تكون كاملة إعادة التحويل البرمجي ، يمكننا إعادة استخدام Binaryen IR لكل كتلة موجودة ، وإضافة IR للكتلة الجديدة ، وتشغيل أداة إعادة التشغيل عليها جميعًا.

(ولكن ربما يمكننا جعل qemu يجمع الوظيفة بأكملها مقدمًا بدلاً من الكسول؟)

tbodt للتعاون في القيام بذلك مع Binaryen ، يتمثل أحد الخيارات في إنشاء ريبو مع عملك (ويمكن استخدام المشكلات هناك وما إلى ذلك) ، والآخر هو فتح مشكلة محددة في Binaryen for qemu.

لا يمكننا جعل qemu يجمع دالة كاملة في وقت واحد ، لأن qemu ليس لديه مفهوم "الوظيفة".

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

سؤال جانبي. من وجهة نظري ، يجب أن تكون لغة WebAssembly التي تستهدف اللغة قادرة على توفير وظيفة تعاودية متبادلة فعالة. لتصوير فائدتها ، أدعوك لقراءة: http://sharp-gamedev.blogspot.com/2011/08/forgotten-control-flow-construct.html

على وجه الخصوص ، يبدو أن الحاجة التي عبرت عنها Cheery يتم تناولها من خلال وظيفة عودية متبادلة.

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

davidgrenier ، جميع الوظائف في وحدة Wasm متكررة بشكل متبادل. هل يمكنك توضيح ما تعتبره غير فعال بشأنها؟ هل تشير فقط إلى عدم وجود مكالمات الذيل أو أي شيء آخر؟

مكالمات الذيل العامة قادمة. التكرار الذيل (متبادل أو غير ذلك) سيكون حالة خاصة لذلك.

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

يعد Goto مفيدًا جدًا لإنشاء التعليمات البرمجية من الرسوم البيانية في البرمجة المرئية. ربما لا تحظى البرمجة المرئية الآن بشعبية كبيرة ولكن في المستقبل يمكنها جذب المزيد من الأشخاص وأعتقد أن wasm يجب أن يكون جاهزًا لذلك. المزيد حول إنشاء الكود من الرسوم البيانية و goto: http://drakon-editor.sourceforge.net/generation.html

سيحصل إصدار Go 1.11 القادم على دعم تجريبي لـ WebAssembly. سيتضمن ذلك الدعم الكامل لجميع ميزات Go ، بما في ذلك goroutines والقنوات وما إلى ذلك. ومع ذلك ، فإن أداء WebAssembly الذي تم إنشاؤه ليس جيدًا حاليًا.

هذا يرجع بشكل أساسي إلى تعليمات goto المفقودة. بدون تعليمات geto ، كان علينا اللجوء إلى استخدام حلقة toplevel والقفز في كل وظيفة. استخدام خوارزمية إعادة التشغيل ليس خيارًا بالنسبة لنا ، لأنه عند التبديل بين goroutines نحتاج إلى أن نكون قادرين على استئناف التنفيذ في نقاط مختلفة من الوظيفة. لا يمكن أن يساعد relooper في ذلك ، فقط تعليمات goto يمكن.

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

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

فقط للتوضيح ، لن يكون الانتقال المنتظم كافياً لذلك ، يلزم الحصول على goto المحسوب لحالة الاستخدام الخاصة بك ، هل هذا صحيح؟

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

يبدو أن لديك تدفق تحكم طبيعي في كل وظيفة ، ولكنك تحتاج أيضًا إلى القدرة على القفز من إدخال الوظيفة إلى مواقع أخرى معينة في "الوسط" ، عند استئناف goroutine - كم عدد هذه المواقع الموجودة؟ إذا كانت كل كتلة أساسية مفردة ، فسيتم إجبار المعيد على إصدار حلقة من المستوى الأعلى تمر بها كل تعليمات ، ولكن إذا كانت قليلة فقط ، فلا ينبغي أن تكون هذه مشكلة. (هذا ما يحدث في الواقع مع دعم setjmp في emscripten - نحن فقط ننشئ المسارات الإضافية الضرورية بين الكتل الأساسية في LLVM ، وندع عملية إعادة التشغيل بشكل طبيعي.)

كل استدعاء لوظيفة أخرى هو مثل هذا الموقع ومعظم الكتل الأساسية تحتوي على تعليمات مكالمة واحدة على الأقل. نحن نقوم بفك واستعادة مكدس المكالمات بشكل أو بآخر.

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

هل سيكون من الممكن استدعاء الوظيفة بنمط CPS أو تنفيذ call/cc في WASM؟

Heimdell ، يوجد دعم لبعض أشكال الاستمرارية المحددة (ويعرف أيضًا باسم "تبديل المكدس") على خريطة الطريق ، والتي يجب أن تكون كافية لأي تجريد تحكم مثير للاهتمام تقريبًا. لا يمكننا دعم عمليات الاستمرارية غير المحدودة (أي استدعاء / نسخة كاملة) ، على الرغم من ذلك ، حيث يمكن خلط مكدس استدعاء Wasm بشكل تعسفي مع لغات أخرى ، بما في ذلك الاستدعاءات المتكررة إلى المضمن ، وبالتالي لا يمكن افتراض أنها قابلة للنسخ أو النقل.

من خلال القراءة من خلال هذا الموضوع ، لدي انطباع بأن التسميات التعسفية وحالات الانتقال تواجه عقبة كبيرة قبل أن تصبح ميزة:

  • تدفق التحكم غير المنظم يجعل الرسوم البيانية للتحكم غير القابل للاختزال ممكنة
  • إلغاء * أي "تحقق سريع ، بسيط ، سهل ، تحويل واحد إلى نموذج SSA"
  • فتح مترجم JIT للأداء غير الخطي
  • لا ينبغي أن يعاني الأشخاص الذين يتصفحون صفحات الويب من التأخير إذا كان مترجم اللغة الأصلي يمكنه القيام بالعمل المسبق

_ * على الرغم من أنه قد تكون هناك بدائل مثل خوارزمية بناء SSA أثناء الطيران من Braun et al والتي تتعامل مع التحكم غير القابل للاختزال في التدفق_

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

وفقًا لورقة مصمم المخطط جاي ستيل عام 1977 ، Lambda: The Ultimate GOTO ، يجب أن يكون التحول ممكنًا ، ويجب أن يكون أداء المكالمات الخلفية قادرًا على مطابقة gotos بشكل وثيق.

أفكار؟

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

هذا هو ما سيفعله كل مترجم على أي حال ، لا أحد ممن أعرفه يدافع عن أشياء غير مُدارة من النوع الذي يسبب الكثير من المشاكل في JVM ، فقط من أجل رسم بياني لـ EBBs المكتوبة. تمتلك LLVM و GCC و Cranelift والبقية جميعًا نموذج SSA (ربما غير قابل للاختزال) كتمثيل داخلي ، وللمجمعين من Wasm to original نفس التمثيل الداخلي ، لذلك نريد الحفاظ على أكبر قدر ممكن من هذه المعلومات و إعادة بناء أقل قدر ممكن من تلك المعلومات. السكان المحليون خاسرون ، لأنهم لم يعودوا SSA ، وتدفق التحكم في Wasm ضائع ، لأنه لم يعد CFG تعسفيًا. من المحتمل أن يكون AFAIK الذي يحتوي على Wasm عبارة عن آلة تسجيل SSA غير محدودة التسجيل مع معلومات حيوية للتسجيل دقيقة الحبيبات هي الأفضل بالنسبة لبرنامج codegen ، لكن حجم الكود قد يكون منتفخًا ، ومن المحتمل أن تكون آلة المكدس ذات التحكم في التدفق المصممة على CFG التعسفي هي أفضل حل وسط . قد أكون مخطئًا بشأن حجم الكود باستخدام جهاز تسجيل ، ومع ذلك ، قد يكون من الممكن ترميزه بكفاءة.

الشيء الذي يدور حول تدفق التحكم غير القابل للاختزال هو أنه إذا كان غير قابل للاختزال في الواجهة الأمامية ، فإنه لا يزال غير قابل للاختزال في wasm ، ولا يجعل تحويل إعادة التشغيل / المكدس تدفق التحكم قابلاً للاختزال ، بل يقوم فقط بتحويل عدم الاختزال إلى الاعتماد على قيم وقت التشغيل. هذا يعطي الخلفية معلومات أقل وبالتالي يمكن أن ينتج رمزًا أسوأ ، فإن الطريقة الوحيدة لإنتاج رمز جيد لـ CFGs غير القابلة للاختزال في الوقت الحالي هي اكتشاف الأنماط المنبعثة من relooper و stackifier وتحويلها مرة أخرى إلى CFG غير قابل للاختزال. ما لم تكن تقوم بتطوير V8 ، والذي يدعم AFAIK فقط تدفق التحكم القابل للاختزال ، فإن دعم تدفق التحكم غير القابل للاختزال يعد فوزًا بحتًا - فهو يجعل كل من الواجهات الأمامية والخلفية أبسط بطريقة (يمكن للواجهة الأمامية فقط إرسال رمز بنفس التنسيق الذي تخزنه داخليًا ، لا بد من اكتشاف الأنماط) أثناء إنتاج مخرجات أفضل في حالة أن تدفق التحكم غير قابل للاختزال والإخراج يكون جيدًا أو أفضل في الحالة المعتادة التي يكون فيها التحكم في التدفق قابلاً للاختزال.

بالإضافة إلى أنه سيسمح لـ GCC و Go بالبدء في إنتاج WebAssembly.

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

ألا يمكن لـ v8 دمج relooper من أجل قبول مدخلات CFGs؟ يبدو أنه تم حظر أجزاء كبيرة من النظام البيئي في تفاصيل تنفيذ الإصدار 8.

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

graph ، هل يمكنك تقديم مزيد من التفاصيل حول كيفية "بطء عبارات التبديل"؟ أبحث دائمًا عن فرصة لتحسين الأداء ... (إذا كنت لا ترغب في التعثر في هذا الموضوع ، أرسل لي بريدًا إلكترونيًا مباشرةً ، [email protected].)

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

for(y = ....) {
    for(x = ....) {
        switch(type){
        case IS_RGBA:....
         ....
        case IS_BGRA
        ....
        case IS_RGB
        ....
....

أفترض أن المترجم كان يحول طاولة القفز إلى أي دعم كان. لم أنظر إلى التجميع الذي تم إنشاؤه لذا لا يمكنني تأكيد ذلك.

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

graph سيكون المعيار الكامل مفيدًا جدًا هنا. بشكل عام ، يمكن أن يتحول المفتاح في C إلى جدول قفزة سريع جدًا في wasm ، ولكن هناك حالات زاوية لا تعمل بشكل جيد حتى الآن ، والتي قد نحتاج إلى إصلاحها ، إما في LLVM أو في المتصفحات.

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

graph ، إذا كان emscripten ينتج br_table ، فسيقوم jit أحيانًا بإنشاء جدول قفزة وأحيانًا (إذا كان يعتقد أنه سيكون أسرع) سيبحث في مسافة المفتاح خطيًا أو باستخدام بحث ثنائي مضمّن. يعتمد ما تفعله غالبًا على حجم المفتاح. من الممكن بالطبع ألا تكون سياسة الاختيار هي الأمثل ... أتفق مع kripken ، سيكون الكود

(لا تعرف شيئًا عن الإصدار 8 أو jsc ، لكن Firefox حاليًا لا يتعرف على سلسلة if-then-else كمفتاح محتمل ، لذلك ليس من الجيد عادةً فتح مفاتيح الشفرة مثل سلاسل if-then-else. ربما لا تكون نقطة التعادل في أكثر من مقارنات أو ثلاث مقارنات.)

@ لارس تي هانسنkripkengraph فإنه قد يكون جيدا أن br_table حاليا مجرد الامم المتحدة والأمثل للغاية كما يبدو هذا التبادل لإظهار: https://twitter.com/battagline/status/1168310096515883008

aardappel ، هذا فضولي ، المعايير التي Firefox على نظامي ، كانت نقطة التعادل في حوالي 5 حالات كما أتذكرها وبعد ذلك كان br_table هو الفائز. microbenchmark بالطبع ، مع بعض المحاولة لتوزيع مفاتيح البحث بشكل متساوٍ. إذا كان عش "if" منحازًا نحو المفاتيح الأكثر احتمالاً بحيث لا تكون هناك حاجة إلى أكثر من اختبارين ، فإن عش "if" سيفوز.

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

@ lars-t-hansen نعم ، لا نعرف حالة الاختبار الخاصة به ، فربما كانت لها قيمة شاذة. في كلتا الحالتين ، يبدو أن Chrome لديه المزيد من العمل الذي يتعين عليه القيام به أكثر من Firefox.

أنا في إجازة ، ومن ثم قلة الردود. شكرا لتفهمك.

kripken @ lars-t-hansen لقد أجريت بعض الاختبارات ويبدو أنه كان أفضل الآن في Firefox. لا تزال هناك بعض الحالات التي يكون فيها مفتاح if-else out-Performs. هنا حالة:


Main.cpp

#include <stdio.h>

#include <chrono>
#include <random>

class Chronometer {
public:
    Chronometer() {

    }

    void start() {
        mStart = std::chrono::steady_clock::now();
    }

    double seconds() {
        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
        return std::chrono::duration_cast<std::chrono::duration<double>>(end - mStart).count();
    }

private:
    std::chrono::steady_clock::time_point mStart;
};

int main() {
    printf("Starting tests!\n");
    Chronometer timer;
    // we want to prevent optimizations based on known size as most applications
    // do not know the size in advance.
    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<> dis(100000000, 1000000000);
    std::uniform_int_distribution<> opKind(0, 3);
    int maxFrames = dis(gen);
    int switchSelect = 0;
    constexpr int SW1 = 1;
    constexpr int SW2 = 8;
    constexpr int SW3 = 32;
    constexpr int SW4 = 38;

    switch(opKind(gen)) {
    case 0:
        switchSelect = SW1;
        break;
    case 1:
        switchSelect = SW2; break;
    case 2:
        switchSelect = SW3; break;
    case 4:
        switchSelect = SW4; break;
    }
    printf("timing with SW = %d\n", switchSelect);
    timer.start();
    int accumulator = 0;
    for(int i = 0; i < maxFrames; ++i) {
        switch(switchSelect) {
        case SW1:
            accumulator = accumulator*3 + i; break;
        case SW2:
            accumulator = (accumulator < 3)*i; break;
        case SW3:
            accumulator = (accumulator&0xFF)*i + accumulator; break;
        case SW4:
            accumulator = (accumulator*accumulator) - accumulator + i; break;
        }
    }
    printf("switch time = %lf seconds\n", timer.seconds());
    printf("accumulated value: %d\n", accumulator);
    timer.start();
    accumulator = 0;
    for(int i = 0; i < maxFrames; ++i) {
        if(switchSelect == SW1)
            accumulator = accumulator*3 + i;
        else if(switchSelect == SW2)
            accumulator = (accumulator < 3)*i;
        else if(switchSelect == SW3)
            accumulator = (accumulator&0xFF)*i + accumulator;
        else if(switchSelect == SW4)
            accumulator = (accumulator*accumulator) - accumulator + i;
    }
    printf("if-else time = %lf seconds\n", timer.seconds());
    printf("accumulated value: %d\n", accumulator);

    return 0;
}

اعتمادا على قيمة التبديل تفوق if-else. مثال الإخراج:

Starting tests!
timing with SW = 32
switch time = 2.049000 seconds
accumulated value: 0
if-else time = 0.401000 seconds
accumulated value: 0

كما ترى بالنسبة لـ switchSelect = 32 إذا كان آخر أسرع. بالنسبة للحالات الأخرى ، يكون if-else أسرع قليلاً. بالنسبة لمفتاح الحالة ، حدد = 1 & 0 ، يكون بيان التبديل أسرع.

Test in Firefox 69.0.3 (64-bit)
compiled using: emcc -O3 -std=c++17 main.cpp -o main.html
emcc version: emcc (Emscripten gcc/clang-like replacement) 1.39.0 (commit e047fe4c1ecfae6ba471ca43f2f630b79516706b)

باستخدام أحدث emscripen مستقر اعتبارًا من 20 أكتوبر 2019. تثبيت جديد ./emcc activate latest .

لقد لاحظت أعلاه وجود خطأ إملائي ، لكن لا ينبغي أن يؤثر ذلك على مسألة أن حالة if-else هي حالة SW3 أسرع لأنها تنفذ نفس التعليمات.

مرة أخرى مع تجاوز هذا نقطة التعادل 5: من المثير للاهتمام أنه بالنسبة إلى switchSelect = 32 في هذه الحالة ، فهو مشابه في السرعة كما لو كان if-else. كما ترى فإن 1003 if-else أسرع قليلاً. يجب أن يفوز التبديل في هذه الحالة.

Starting tests!
timing with SW = 1003
switch time = 2.253000 seconds
accumulated value: 1903939380
if-else time = 2.197000 seconds
accumulated value: 1903939380


main.cpp

#include <stdio.h>

#include <chrono>
#include <random>

class Chronometer {
public:
    Chronometer() {

    }

    void start() {
        mStart = std::chrono::steady_clock::now();
    }

    double seconds() {
        std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
        return std::chrono::duration_cast<std::chrono::duration<double>>(end - mStart).count();
    }

private:
    std::chrono::steady_clock::time_point mStart;
};

int main() {
    printf("Starting tests!\n");
    Chronometer timer;
    // we want to prevent optimizations based on known size as most applications
    // do not know the size in advance.
    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<> dis(100000000, 1000000000);
    std::uniform_int_distribution<> opKind(0, 8);
    int maxFrames = dis(gen);
    int switchSelect = 0;
    constexpr int SW1 = 1;
    constexpr int SW2 = 8;
    constexpr int SW3 = 32;
    constexpr int SW4 = 38;
    constexpr int SW5 = 64;
    constexpr int SW6 = 67;
    constexpr int SW7 = 1003;
    constexpr int SW8 = 256;

    switch(opKind(gen)) {
    case 0:
        switchSelect = SW1;
        break;
    case 1:
        switchSelect = SW2; break;
    case 2:
        switchSelect = SW3; break;
    case 3:
        switchSelect = SW4; break;
    case 4:
        switchSelect = SW5; break;
    case 5:
        switchSelect = SW6; break;
    case 6:
        switchSelect = SW7; break;
    case 7:
        switchSelect = SW8; break;
    }
    printf("timing with SW = %d\n", switchSelect);
    timer.start();
    int accumulator = 0;
    for(int i = 0; i < maxFrames; ++i) {
        switch(switchSelect) {
        case SW1:
            accumulator = accumulator*3 + i; break;
        case SW2:
            accumulator = (accumulator < 3)*i; break;
        case SW3:
            accumulator = (accumulator&0xFF)*i + accumulator; break;
        case SW4:
            accumulator = (accumulator*accumulator) - accumulator + i; break;
        case SW5:
            accumulator = (accumulator << 3) - accumulator + i; break;
        case SW6:
            accumulator = (i - accumulator) & 0xFF; break;
        case SW7:
            accumulator = i*i + accumulator; break;
        }
    }
    printf("switch time = %lf seconds\n", timer.seconds());
    printf("accumulated value: %d\n", accumulator);
    timer.start();
    accumulator = 0;
    for(int i = 0; i < maxFrames; ++i) {
        if(switchSelect == SW1)
            accumulator = accumulator*3 + i;
        else if(switchSelect == SW2)
            accumulator = (accumulator < 3)*i;
        else if(switchSelect == SW3)
            accumulator = (accumulator&0xFF)*i + accumulator;
        else if(switchSelect == SW4)
            accumulator = (accumulator*accumulator) - accumulator + i;
        else if(switchSelect == SW5)
            accumulator = (accumulator << 3) - accumulator + i;
        else if(switchSelect == SW6)
            accumulator = (i - accumulator) & 0xFF;
        else if(switchSelect == SW7)
            accumulator = i*i + accumulator;

    }
    printf("if-else time = %lf seconds\n", timer.seconds());
    printf("accumulated value: %d\n", accumulator);

    return 0;
}


شكرًا يا رفاق على إلقاء نظرة على حالات الاختبار هذه.

هذا switch ضئيل للغاية على الرغم من أنه يجب على LLVM تحويله إلى ما يعادل مجموعة if-then's على أي حال ، ولكن يبدو أنه يفعل ذلك بطريقة أقل كفاءة من دليل if-thens. هل حاولت تشغيل wasm2wat لترى كيف تختلف هاتان الحلقتان في الكود؟

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

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

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

لم أعتقد أبدًا أن الانتقال يمكن أن يكون مثل هذا النقاش الساخن 😮. أنا في قارب من كل لغة يجب أن يكون لدي الانتقال 😁. سبب آخر لإضافة الأمر goto هو أنه يقلل من تعقيد المترجم لعملية التحويل البرمجي إلى wasm. أنا متأكد من أن هذا مذكور أعلاه في مكان ما. الآن ليس لدي ما أشكو منه 😞.

أي مزيد من التقدم هناك؟

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

لم يكن هناك تقدم كبير مؤخرًا ، ولكن قد ترغب في إلقاء نظرة على اقتراح funclets .

graph لي ، اقتراحك يبدو مثل "دعونا نكسر كل شيء ، ونأمل في الأفضل".
إنه لا يعمل هكذا. هناك العديد من الفوائد من بنية WebAssembly الحالية (التي ليست واضحة ، للأسف). حاول الغوص بشكل أعمق في فلسفة الوسم.

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

من الواضح أن هذا كان في حالته الحالية لديه بعض الإغفالات الرئيسية. يعمل الناس على سد الفجوات (مثل تلك التي ذكرها binji ) ، لكنني لا أعتقد أن "هيكل النظام العالمي" بحاجة إلى إعادة صياغة. فقط رأيي المتواضع.

vshymanskyy إن اقتراح funclets ، الذي يوفر وظائف مكافئة للتسميات التعسفية و gotos ، قابل للتحقق تمامًا ، في الوقت الخطي.

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

أعتقد أن هذا المفهوم الخاطئ بأن تدفق التحكم غير القابل للاختزال لا يمكن التحقق من صحته في الوقت الخطي ينبع من JVM ، حيث يجب تنفيذ تدفق التحكم غير القابل للاختزال باستخدام المترجم الفوري بدلاً من تجميعه. هذا لأن JVM ليس لديها أي طريقة لتمثيل بيانات تعريف النوع لتدفق التحكم غير القابل للاختزال ، وبالتالي لا يمكنها القيام بالتحويل من آلة المكدس إلى آلة التسجيل. "الانتقال التعسفي" (أي الانتقال إلى البايت / التعليمات X) لا يمكن التحقق منه على الإطلاق ، ولكن فصل الوظيفة إلى كتل مكتوبة ، والتي يمكن بعد ذلك القفز بينها بترتيب عشوائي ، ليس من الصعب التحقق منه أكثر من فصل وحدة إلى وظائف مكتوبة ، والتي يمكن بعد ذلك القفز بينها بترتيب تعسفي. لا تحتاج إلى الانتقال السريع إلى البايت X على غرار الانتقال السريع لتنفيذ أي أنماط مفيدة من شأنها أن تنبعث من المجمعين مثل GCC و LLVM.

أنا فقط أحب العملية هنا. يوضح الجانب "أ" سبب الحاجة إلى ذلك في تطبيقات معينة. يقول Side B إنهم يفعلون ذلك بشكل خاطئ ، لكنه لا يقدم أي دعم لهذا التطبيق. يشرح الجانب "أ" كيف أن أيا من الحجج الواقعية لـ "ب" لا تصمد. لا يريد الجانب "ب" التعامل معه لأنهم يعتقدون أن الجانب "أ" يفعل ذلك بشكل خاطئ. يحاول الجانب "أ" تحقيق هدف. يقول الجانب ب أن هذا هو الهدف الخاطئ ، ويصفه بأنه كسول أو متوحش. تضيع المعاني الفلسفية الأعمق في الجانب "أ". تضيع البراغماتية في الجانب "ب" ، حيث يزعمون أن لديهم نوعًا من الأرضية الأخلاقية العليا. يرى الجانب أ هذا على أنه عملية آلية غير أخلاقية. في النهاية ، يظل الجانب B بشكل عام متحكمًا في المواصفات للأفضل أو للأسوأ ، وقد أنجزوا قدرًا لا يُصدق من خلال نقائهم النسبي.

بصراحة ، لقد علقت أنفي هنا لأنه منذ سنوات ، كنت أحاول إنشاء منفذ TinyCC إلى WASM حتى أتمكن من تشغيل بيئة تطوير على ESP8266 تستهدف ESP8266. لدي فقط ما يقرب من 4 ميغا بايت من التخزين ، لذا فإن تضمين إعادة التشغيل والتبديل إلى AST ، بالإضافة إلى العديد من التغييرات الأخرى غير وارد. (ملاحظة جانبية: كيف تكون إعادة التشغيل هي الشيء الوحيد مثل إعادة التشغيل؟ إنه أمر مروع جدًا ولم يقم أحد بإعادة كتابة هذا المصاص في C !؟) حتى لو كان ذلك ممكنًا في هذه المرحلة ، لا أعرف ما إذا كنت سأكتب هدف TinyCC إلى WASM لأنه لم يعد مثيرًا للاهتمام بالنسبة لي بعد الآن.

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

هل يمكن لشخص مسؤول عن V8 أن يؤكد في هذا الموضوع أن المعارضة ضد تدفق التحكم غير القابل للاختزال لا تتأثر بالتنفيذ الحالي لـ V8؟

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

حسنًا ، بقدر ما هو مؤسف ، فإن التطبيقات الحالية الموجودة حتى الآن مهمة جدًا لدرجة أن المستقبل (من المفترض أن يكون أطول من الماضي) ليس مهمًا جدًا. كنت أحاول أن أوضح أنه في # 1202 ، كان الاتساق أكثر أهمية من عدد قليل من التطبيقات ، لكن يبدو أنني متوهم. نتمنى لك التوفيق في توضيح أن بعض قرارات التنمية في مكان ما في مشروع ما لا تشكل حقيقة عالمية ، أو يجب أن تكون صحيحة افتراضيًا.

هذا الخيط هو أحد طيور الكناري في منجم الفحم W3C. على الرغم من أنني أكن احترامًا كبيرًا للعديد من أفراد W3C ، إلا أن قرار تكليف JavaScript بـ Ecma International ، وليس W3C ، لم يتم دون تحيز.

مثل cnlohr ، كنت آمل في الحصول على منفذ wasm TCC ، ولسبب وجيه ؛

"تم تصميم Wasm كهدف تجميع محمول للغات البرمجة ، مما يتيح النشر على الويب لتطبيقات العميل والخادم." - webassembly.org

بالتأكيد ، يمكن لأي شخص أن يعبر عن سبب كون goto هو [INSERT JARGON] ، ولكن ماذا عن تفضيلنا للمعايير على الآراء. يمكننا أن نتفق جميعًا على أن POSIX C هدف أساسي جيد ، لا سيما بالنظر إلى أن خطوط اليوم إما أن تكون ناتجة عن C أو تم قياسها مقابل عنوان الصفحة الرئيسية لـ WASM وتروج لنفسها كهدف تجميع محمول للنطاقات. بالتأكيد ، سيتم تعيين بعض الميزات مثل الخيوط و simd. ولكن ، لتجاهل شيء أساسي تمامًا مثل goto ، حتى لا نعطيه حشمة رسم خرائط الطريق ، لا يتوافق مع الغرض المعلن لـ WASM ومثل هذا الموقف من هيئة التقييس التي تعطي الضوء الأخضر <marquee> أبعد من شاحب.

وفقًا لمعيار التشفير SEI CERT C Rec. "ضع في اعتبارك استخدام سلسلة goto عند ترك دالة عند الخطأ عند استخدام الموارد وتحريرها " ؛

تتطلب العديد من الوظائف تخصيص موارد متعددة. قد يؤدي الفشل والعودة في مكان ما في منتصف هذه الوظيفة دون تحرير جميع الموارد المخصصة إلى حدوث تسرب للذاكرة. من الخطأ الشائع أن تنسى تحرير أحد (أو كل) الموارد بهذه الطريقة ، لذا فإن سلسلة goto هي أبسط وأنظف طريقة لتنظيم عمليات الخروج مع الحفاظ على ترتيب الموارد المحررة.

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

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

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

بدون خطة لدعم goto ، WASM هو هدف تجميع الألعاب ، وهذا جيد ، ربما هذا ما يرى W3C الويب. آمل أن يصل WASM كمعيار أعلى ، من مساحة العنوان 32 بت ، ويدخل في سباق التجميع. آمل أن يبتعد الخطاب الهندسي عن "هذا غير ممكن ..." لنتتبع بسرعة امتدادات GCC C مثل الملصقات كقيم لأن WASM يجب أن يكون رائعًا. شخصيًا ، تعتبر TCC أكثر إثارة للإعجاب وأكثر فائدة في هذه المرحلة ، دون كل العبث الضائع ، بدون الصفحة المقصودة للهيبستر والشعار اللامع.

@ d4tocchini :

وفقًا لمعيار التشفير SEI CERT C Rec. "ضع في اعتبارك استخدام سلسلة goto عند ترك دالة عند الخطأ عند استخدام الموارد وتحريرها " ؛

تتطلب العديد من الوظائف تخصيص موارد متعددة. قد يؤدي الفشل والعودة في مكان ما في منتصف هذه الوظيفة دون تحرير جميع الموارد المخصصة إلى حدوث تسرب للذاكرة. من الخطأ الشائع أن تنسى تحرير أحد (أو كل) الموارد بهذه الطريقة ، لذا فإن سلسلة goto هي أبسط وأنظف طريقة لتنظيم عمليات الخروج مع الحفاظ على ترتيب الموارد المحررة.

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

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

rossberg ، نقطة جيدة بشأن الفواصل المصنفة في هذا المثال ، لكنني لا أتفق مع رفضت Python

إذا تم إسقاط goto بسهولة من الاعتبار ، فهل يجب إعادة النظر أيضًا في

هل هناك لغة لا يمكن كتابتها بلغة سي؟ يجب أن تُعلم لغة C كلغة بميزات WASM. إذا لم يكن POSIX C ممكنًا مع WASM اليوم ، فهناك خارطة الطريق المناسبة لك.

لا يتعلق الأمر بموضوع الجدل حقًا ، ولكن حتى لا نغفل أن الأخطاء العشوائية كامنة هنا وهناك في الجدل بشكل عام:

صنفت بايثون فواصل

هل يمكنك التفصيل؟ (Aka: Python لا تحتوي على فواصل.)

pfalcon ، نعم فواصل / تواصل بيثون

إذا كان يجب إسقاط goto بسهولة من الاعتبار ، فهل يجب إعادة النظر في مئات استخدامات goto داخل مصدر emscripten أيضًا؟

1) لاحظ مقدار ذلك الموجود في musl libc ، وليس بشكل مباشر في emscripten. (ثاني أكثر استخدامًا هو الاختبارات / الطرف الثالث)
2) بنيات مستوى المصدر ليست هي نفسها تعليمات الرمز الثانوي
3) ليس Emscripten في نفس مستوى التجريد مثل معيار wasm ، لذلك لا ينبغي إعادة النظر فيه على هذا الأساس.

على وجه التحديد ، قد يكون من المفيد اليوم إعادة كتابة gotos من libc ، لأنه بعد ذلك سيكون لدينا سيطرة أكبر على cfg الناتج عن الثقة في relooper / cfgstackify للتعامل معها بشكل جيد. ليس لدينا لأنه قدر غير ضئيل من العمل ينتهي به الأمر برمز متباين بشدة من musl المنبع.

يميل مطورو Emscripten (آخر مرة راجعتها) إلى الرأي القائل بأن البنية الشبيهة بـ goto ستكون رائعة حقًا ، لهذه الأسباب الواضحة ، لذلك من غير المرجح أن تتجاهلها من الاعتبار ، حتى لو استغرق الأمر سنوات للوصول إلى حل وسط مقبول.

مثل هذا الموقف من هيئة التقييس أن الضوء الأخضر <marquee> هو أبعد من اللون الباهت.

هذا هو بيان لا سيما asinine.

1) نحن في الإنترنت الأوسع أمامنا أكثر من عقد من الزمن لاتخاذ هذا القرار
2) We-the-wasm-CG هم مجموعة منفصلة تمامًا (تقريبًا؟) من الأشخاص من تلك العلامة ، وربما يكونون منزعجين بشكل فردي من أخطاء الماضي الواضحة أيضًا.

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

كان من الممكن إعادة صياغة هذا إلى "أنا محبط" دون الوقوع في مشاكل في اللهجة.

كما يظهر هذا الموضوع ، فإن هذه المحادثات صعبة بما يكفي كما هي.

هناك مستوى جديد من القلق العميق عندما تريد إعادة كتابة مجموعة وظائف موثوقة ومفهومة للغاية لجميع الوظائف الجديدة فقط لأن بيئة استخدامها يجب أن تمر عبر خطوات إضافية لدعمها. (على الرغم من أنني ما زلت في معسكر please-add-goto لأنني أكره أن أكون مرتبطًا باستخدام مترجم واحد فقط)

أعتقد أن هذا الخيط قد انتقل إلى ما بعد كونه منتجًا - فقد استمر منذ أكثر من أربع سنوات ويبدو أنه تم استخدام كل حجة ممكنة مع وضد goto s التعسفي هنا ؛ وتجدر الإشارة أيضًا إلى أن أيا من هذه الحجج ليست جديدة بشكل خاص ؛)

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

تعتبر خيارات تصميم Wasm التي تمنع القفزات التعسفية جوهر فلسفتها. من غير المحتمل أن تدعم goto s بدون شيء مثل funclets ، للأسباب نفسها التي لا تدعم القفزات غير المباشرة الخالصة.

تعتبر خيارات تصميم Wasm التي تمنع القفزات التعسفية جوهر فلسفتها. من غير المحتمل أن تدعم الانتقال بدون شيء مثل funclets ، للأسباب نفسها التي لا تدعم القفزات غير المباشرة الخالصة.

penzn لماذا توقف اقتراح funclets ؟ إنه موجود منذ أكتوبر 2018 وما زال في المرحلة 0.

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

تضمين التغريدة

  1. لاحظ مقدار ذلك الموجود في musl libc ، وليس بشكل مباشر في emscripten. (ثاني أكثر استخدامًا هو الاختبارات / الطرف الثالث)

نعم ، كانت الإشارة إلى مقدار استخدامها في لغة سي بشكل عام.

  1. بنيات مستوى المصدر ليست هي نفسها تعليمات الرمز الثانوي

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

  1. Emscripten ليس بنفس مستوى التجريد مثل معيار wasm ، لذلك ، لا ينبغي إعادة النظر فيه على هذا الأساس.

كانت النقطة هي أنك ستجد goto s في غالبية مشاريع C الكبيرة ، حتى داخل WebAssembly toolchain بشكل عام. هدف مترجم محمول للغات بشكل عام غير معبر بدرجة كافية لاستهداف المجمعين الخاصين به لا يتوافق تمامًا مع طبيعة مؤسستنا.

على وجه التحديد ، قد يكون من المفيد اليوم إعادة كتابة gotos من libc ، لأنه بعد ذلك سيكون لدينا سيطرة أكبر على cfg الناتج عن الثقة في relooper / cfgstackify للتعامل معها بشكل جيد.

هذا دائري. لقد أثار الكثيرون أعلاه أسئلة خطيرة دون إجابة فيما يتعلق بعصمة مثل هذا المطلب.

ليس لدينا لأنه قدر غير ضئيل من العمل ينتهي به الأمر برمز متباين بشدة من musl المنبع.

من الممكن إزالة أشياء أخرى ، كما قلت ، إنه قدر غير ضئيل

يميل مطورو Emscripten (آخر مرة راجعتها) إلى الرأي القائل بأن البنية الشبيهة بـ goto ستكون رائعة حقًا ، لهذه الأسباب الواضحة ، لذلك من غير المرجح أن تتجاهلها من الاعتبار ، حتى لو استغرق الأمر سنوات للوصول إلى حل وسط مقبول.

بصيص من الأمل! سأكون راضيًا إذا تم أخذ دعم الانتقال / التسمية على محمل الجد مع عنصر خريطة الطريق + دعوة رسمية لتحريك الكرة ، حتى لو مرت سنوات.

هذا هو بيان لا سيما asinine.

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

"هل يمكن لشخص مسؤول عن V8 أن يؤكد في هذا الموضوع أن المعارضة ضد تدفق التحكم غير القابل للاختزال لا تتأثر بالتنفيذ الحالي لـ V8؟

أنا أسأل لأن هذا هو أكثر ما يزعجني. [...]

إذا استمعت يا رفاق إلى أي استخدام ، فخذ ملاحظات @ neelance بخصوص Go 1.11 إلى القلب. من الصعب المجادلة معه. بالتأكيد ، يمكننا جميعًا القيام بعملية إزالة الغبار غير التافهة لـ goto ، ولكن حتى ذلك الحين ، فإننا نتلقى نجاحًا جادًا لا يمكن إصلاحه إلا من خلال تعليمات goto.

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

إليك مشكلة أخرى في العالم الواقعي ناتجة عن فقد goto / funclets: https://github.com/golang/go/issues/42979

بالنسبة لهذا البرنامج ، يقوم المترجم Go حاليًا بإنشاء ثنائي wasm مع 18000 block متداخلة wasm2wat أحصل على ملف wat 4.7 جيجا بايت. 🤯

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

أود أن أضيف أنني أجد أنه من الغريب كيف يبدو أن الناس يعتقدون أنه أمر جيد تمامًا إذا كان مترجم واحد فقط (Emscripten [1]) يمكنه دعم WebAssembly بشكل واقعي.
يذكرني نوعًا ما بحالة libopus (معيار يعتمد بشكل معياري على التعليمات البرمجية المحمية بحقوق النشر).

كما أنني أجد أنه من الغريب كيف يبدو أن مطوري WebAssembly يعارضون ذلك بشدة ، على الرغم من إخبار الجميع تقريبًا من نهاية المجمع للأشياء بأنه مطلوب. تذكر: يعد WebAssembly معيارًا وليس بيانًا. والحقيقة هي أن معظم المجمّعين الحديثين يستخدمون شكلاً من أشكال كتل SSA + الأساسية داخليًا (أو شيء مكافئ تقريبًا ، له نفس الخصائص) ، والتي لا تحتوي على مفهوم الحلقات الصريحة [2]. حتى JITs تستخدم شيئًا مشابهًا ، هذا هو مدى شيوعه.
إن المطلب المطلق لإعادة الظهور دون الحاجة إلى مهرب من "just use goto" هو ، على حد علمي [3] ، غير مسبوق خارج نطاق مترجمي لغة إلى لغة --- وحتى ذلك الحين ، فإن المترجمين من لغة إلى لغة هم فقط استهداف لغات أقل. على وجه الخصوص ، لم أسمع أبدًا عن ضرورة القيام بذلك لأي نوع من أنواع الأشعة تحت الحمراء أو الرمز الثانوي ، بخلاف WebAssembly.

ربما حان الوقت لإعادة تسمية WebAssembly إلى WebEmscripten (WebScripten؟).

كما قال @ d4tocchini ، إذا لم يكن الأمر يتعلق بوضع احتكاري لـ WebAssembly (ضروري ، نظرًا للوضع المعياري) ، فمن المحتمل أن يكون قد تم تقسيمه الآن ، إلى شيء يمكن أن يدعم بشكل معقول ما يعرف مطورو المترجمين بالفعل أنه يحتاج إلى دعمه.
ولا ، "استخدم فقط emscripten" ليست حجة مضادة صالحة ، لأنها تجعل المعيار يعتمد على بائع مترجم واحد. آمل ألا أحتاج أن أخبرك لماذا هذا سيء.

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

إليك مشكلة أخرى في العالم الواقعي ناتجة عن فقد goto / funclets: golang / go # 42979

بالنسبة لهذا البرنامج ، يقوم المترجم Go حاليًا بإنشاء ثنائي wasm مع 18000 block متداخلة wasm2wat أحصل على ملف wat 4.7 جيجا بايت. 🤯

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

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

ولا ، "استخدم فقط emscripten" ليست حجة مضادة صالحة

أعتقد أن الحجة المضادة الحقيقية في هذا السياق هي أن مترجمًا آخر يرغب في استهداف Wasm يمكنه / يجب عليه تنفيذ خوارزمية تشبه إعادة التشغيل الخاصة به. أنا شخصياً أعتقد أن Wasm يجب أن يكون له في النهاية حلقة متعددة الأجسام (قريبة من funclets) أو شيء مشابه يكون هدفًا طبيعيًا لـ goto .

@ conrad-watt هناك العديد من العوامل التي تجعل كل مهمة تستخدم عدة كتل أساسية في CFG. أحدها هو أن هناك فحصًا للطول على الشريحة لأن الطول غير معروف في وقت الترجمة. بشكل عام ، أود أن أقول إن المترجمين يعتبرون الكتل الأساسية بناءًا رخيصًا نسبيًا ، ولكن مع wasm تكون باهظة الثمن إلى حد ما ، خاصة في هذه الحالة بالذات.

neelance في المثال المعدل حيث يتم تقسيم الكود بين عدة وظائف ، يظهر مقدار حمل الذاكرة (وقت التشغيل / التجميع) أقل بكثير. هل يتم إنشاء عدد أقل من الكتل في هذه الحالة ، أم أن الوظائف المنفصلة تعني أن المحرك GC يمكن أن يكون أكثر دقة؟

@ conrad-watt ليس رمز Go هو الذي يستخدم الذاكرة ، ولكن مضيف WebAssembly: عندما أقوم بإنشاء مثيل ثنائي wasm باستخدام Chrome 86 ، تنتقل وحدة المعالجة المركزية الخاصة بي إلى 100٪ لمدة دقيقتين واستخدام الذاكرة لعلامة التبويب في ذروته عند 11.3 جيجا بايت. هذا قبل تنفيذ كود wasm binary / Go. شكل ثنائي wasm هو الذي يسبب المشكلة.

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

لمحاولة إزالة الغموض عن سؤالي السابق - إذا تم تجميع الإصدار المقسم من الكود إلى Wasm مع عدد أقل من الكتل (بسبب بعض مشكلات إعادة التشغيل) ، فسيكون ذلك تفسيرًا واحدًا لتقليل حجم الذاكرة ، وسيكون دافعًا جيدًا لإضافة المزيد من العناصر العامة التحكم في التدفق إلى Wasm.

بدلاً من ذلك ، قد ينتج عن الكود المقسم (تقريبًا) نفس العدد الإجمالي للكتل ، ولكن نظرًا لأن كل وظيفة يتم تجميعها بشكل منفصل JIT ، فإن البيانات الوصفية / الأشعة تحت الحمراء المستخدمة لتجميع كل وظيفة يمكن أن تكون أكثر شغفًا باستخدام GC بواسطة محرك Wasm . حدثت مشكلة مماثلة في V8 منذ سنوات عند تحليل / تجميع وظائف asm.js الكبيرة. في هذه الحالة ، فإن إدخال المزيد من تدفق التحكم العام إلى Wasm لن يحل المشكلة.

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

أعتقد أن هناك بعض النمو الهائل في التعقيد في وقت تشغيل Chrome فيما يتعلق بعمق block s المتداخلة. يحتوي الإصدار المقسم على نفس عدد الكتل ولكن عمق أقصى أصغر.

في هذه الحالة ، فإن إدخال المزيد من تدفق التحكم العام إلى Wasm لن يحل المشكلة.

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

أعتقد أن هناك بعض النمو الهائل في التعقيد في وقت تشغيل Chrome فيما يتعلق بعمق الكتل المتداخلة. يحتوي الإصدار المقسم على نفس عدد الكتل ولكن عمق أقصى أصغر.

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

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

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

توافق على أنه من الأفضل (من وجهة نظر تقنية بحتة) أن يكون لديك هدف أكثر طبيعية هنا.

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

والعكس صحيح: المهمة الأولى متداخلة بعمق ، وليست الأخيرة. متداخلة block s و br_table في الجزء العلوي هو كيف يتم التعبير عن عبارة switch التقليدية في wasm. هذه هي طاولة القفز التي ذكرتها. لا توجد 3000 حلقة متداخلة.

إذا كان اختلاف التداخل أقل وضوحًا من ذلك ، فسيكون حدسي هو أن V8 لا يفعل تقريبًا أي GC'ing للبيانات الوصفية أثناء تجميع وظيفة Wasm واحدة ، لذلك حتى لو كان لدينا شيء مثل اقتراح funclets معدلة في اللغة منذ البداية ، ستظل نفس النفقات العامة مرئية بدون إجراء بعض التحسينات المثيرة للاهتمام في GC.

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

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

بالتأكيد ، لكن مشكلة الأداء هنا ستكون متعامدة مع كيفية التعبير عن التحكم في التدفق بين تلك الكتل الأساسية بلغة المصدر الأصلية (أي ليس دافعًا لتدفق التحكم العام في Wasm). لمعرفة ما إذا كان V8 سيئًا بشكل خاص هنا ، يمكن للمرء التحقق مما إذا كان FireFox / SpiderMonkey أو Lucet / Cranelift يعرضان نفس النفقات العامة الخاصة بالتجميع.

لقد أجريت المزيد من الاختبارات: لم يُظهر Firefox و Safari أي مشاكل على الإطلاق. ومن المثير للاهتمام ، أن Chrome قادر حتى على تشغيل الكود قبل انتهاء العملية المكثفة ، لذلك يبدو أن بعض المهام غير الضرورية تمامًا لتشغيل برنامج wasm الثنائي تواجه مشكلة التعقيد.

بالتأكيد ، لكن مشكلة الأداء هنا ستكون متعامدة مع كيفية التعبير عن التحكم في التدفق بين تلك الكتل الأساسية بلغة المصدر الأصلية.

أرى وجهة نظرك.

ما زلت أعتقد أن تمثيل الكتل الأساسية ليس عن طريق تعليمات القفز ولكن عبر متغير قفزة وجدول قفزة ضخم / كتل متداخلة يعبر عن المفهوم البسيط للكتل الأساسية بطريقة معقدة للغاية. يؤدي هذا إلى زيادة الأداء ومخاطر حدوث مشكلات معقدة مثل تلك التي رأيناها هنا. أعتقد أن الأنظمة الأبسط هي أفضل وأكثر قوة من الأنظمة المعقدة. ما زلت لم أر الحجج التي تقنعني أن النظام الأبسط هو اختيار سيء. لقد سمعت فقط أن V8 سيواجه صعوبة في تنفيذ تدفق التحكم التعسفي وسؤالي المفتوح لإخباري أن هذا البيان خاطئ (https://github.com/WebAssembly/design/issues/796#issuecomment-623431527) لم يفعل تم الرد عليها حتى الان.

تضمين التغريدة

حتى أن Chrome قادر على تشغيل الكود قبل انتهاء العملية المكثفة

يبدو أن برنامج التحويل البرمجي الأساسي Liftoff على ما يرام ، والمشكلة تكمن في تحسين المترجم TurboFan. يرجى تقديم مشكلة ، أو الرجاء تقديم حالة اختبار ويمكنني تقديم واحدة إذا كنت تفضل ذلك.

بشكل أكثر عمومية: هل تعتقد أن خطط تبديل حزمة wasm ستكون قادرة على حل مشكلات تنفيذ goroutine الخاصة بـ Go؟ هذا هو أفضل رابط يمكنني العثور عليه ، لكنه نشط تمامًا الآن ، مع اجتماع نصف أسبوعي ، والعديد من حالات الاستخدام القوية التي تحفز العمل. إذا كان بإمكان Go استخدام coroutines wasm لتجنب نمط التبديل الكبير ، فأنا أعتقد أن الانتقال التعسفي لن يكون ضروريًا.

لا يستخدم برنامج التحويل البرمجي Go خوارزمية relooper ، لأنها غير متوافقة بطبيعتها مع مفهوم تبديل goroutines.

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

سأكون سعيدًا جدًا لتجربة ذلك أثناء التنقل ، إذا كنت مهتمًا! من الواضح أن هذا لن يكون جيدًا مثل دعم تبديل المكدس المدمج في wasm ، ولكنه قد يكون أفضل من نمط التبديل الكبير بالفعل. وسيكون من الأسهل التبديل إلى دعم تبديل المكدس المدمج لاحقًا. بشكل ملموس ، كيف يمكن أن تعمل هذه التجربة هو جعل Go يصدر كودًا منظمًا بشكل طبيعي ، دون القلق بشأن تبديل المكدس على الإطلاق ، وأرسل فقط مكالمة إلى وظيفة maybe_switch_goroutine في النقاط المناسبة. سيعتني تحويل Asyncify بالباقي بشكل أساسي.

أنا مهتم بـ gotos لمحاكيات إعادة التحويل البرمجي الديناميكي مثل qemu. على عكس المجمعين الآخرين ، لا يمتلك qemu في أي وقت معرفة بهيكل تدفق التحكم في البرنامج ، وبالتالي فإن gotos هي الهدف الوحيد المعقول. قد تعالج Tailcalls هذا ، من خلال تجميع كل كتلة كوظيفة وكل منها على شكل مكالمة ذيل.

kripken شكرا

يبدو أن برنامج التحويل البرمجي الأساسي Liftoff على ما يرام ، والمشكلة تكمن في تحسين المترجم TurboFan. يرجى تقديم مشكلة ، أو الرجاء تقديم حالة اختبار ويمكنني تقديم واحدة إذا كنت تفضل ذلك.

إليك ملف ثنائي wasm يمكنك تشغيله باستخدام wasm_exec.html .

هل تعتقد أن خطط تبديل حزمة wasm ستكون قادرة على حل مشكلات تطبيق Go's goroutine؟

نعم ، للوهلة الأولى يبدو أن هذا من شأنه أن يساعد.

ومع ذلك ، فقد حصلنا على نتائج جيدة مع استخدام التحكم المنظم في التدفق + Asyncify.

هذا يبدو واعدا ايضا سنحتاج إلى تنفيذ relooper في Go ، لكن أعتقد أن هذا جيد. أحد الجوانب السلبية الصغيرة هو أنه يضيف تبعية إلى binaryen لإنتاج ثنائيات wasm. ربما سأكتب اقتراحًا قريبًا.

أعتقد أن خوارزمية مكدس LLVM أسهل / أفضل ، في حال كنت ترغب في تنفيذ ذلك: https://medium.com/leaningtech/solving-the-structured-control-flow-problem-once-and-for-all-5123117b1ee2

لقد قدمت اقتراحًا لمشروع Go: https://github.com/golang/go/issues/43033

neelance ، من الجيد رؤية اقتراحkripken يساعد قليلاً مع golang + wasm. النظر في هذه المشكلة هو واحد من goo / labels وليس تبديل المكدس ، وبالنظر إلى أن Asyncify يقدم إصدارات جديدة / خاصة للأغلفة مع Asyncify حتى يتم تحرير تبديل المكدس ، وما إلى ذلك - هل ستصف هذا بأنه حل أم أنه أقل من التخفيف الأمثل؟ كيف يقارن هذا بالفوائد المقدرة إذا كانت تعليمات الانتقال متاحة؟

إذا كانت حجة Linus Torvalds "Good Taste" للقوائم المرتبطة تستند إلى أناقة إزالة بيان فرع وحيد بغلاف خاص ، فمن الصعب رؤية هذا النوع من الجمباز ذي الغلاف الخاص باعتباره فوزًا أو حتى خطوة في الاتجاه الصحيح. بعد استخدامك شخصيًا لـ gotos من أجل apis غير المتزامن في C ، للتحدث عن تبديل المكدس قبل أن تؤدي تعليمات goto إلى إطلاق جميع أنواع الروائح.

من فضلك صححني إذا كنت أخطئ في القراءة ، ولكن بصرف النظر عن الردود التي تبدو سريعة ومركزة على الخصائص الهامشية لبعض الأسئلة التي أثيرت ، يبدو أن المشرفين هنا لم يقدموا أي توضيح بشأن المسألة المطروحة ولم يجيبوا على الأسئلة الصعبة. مع كل الاحترام الواجب ، أليس هذا التعظم البطيء هو السمة المميزة لسياسة الشركات؟ إذا كان هذا هو الحال ، فأنا أفهم المحنة ... تخيل كل اللغات / المترجمات التي يمكن أن تتباهى بها علامة Wasm التجارية إذا كان ANSI C فقط اختبارًا متوافقًا!

neelancedarkuranium @ d4tocchini ليس جميع المساهمين الوسم يعتقدون أن عدم وجود غوتو هو الشيء الصحيح، في الواقع، أود أن معدل شخصيا على أنها التصميم خطأ الوسم # 1. أنا أؤيد تمامًا إضافته (إما كوظائف أو بشكل مباشر).

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

  1. انضم إلى Wasm CG.
  2. يستثمر شخص ما الوقت ليصبح بطلًا لاقتراح الانتقال. أوصي بالبدء من اقتراح funclets الحالي ، حيث تم التفكير فيه جيدًا من قبل sunfishcode ليكون "الأقل تدخلاً" للمحركات والأدوات الحالية التي تعتمد على بنية الكتلة ، لذلك لديها فرصة أكبر للنجاح من الخام اذهب إلى.
  3. ساعده في دفعه خلال مراحل الاقتراح الأربعة. يتضمن ذلك عمل تصميمات جيدة لأي اعتراضات يتم طرحها في طريقك ، وبدء المناقشات ، بهدف إرضاء عدد كافٍ من الأشخاص بحيث تحصل على أصوات الأغلبية عند التقدم عبر المراحل.

@ d4tocchini بصراحة ، أرى حاليًا الحلول المقترحة على أنها "أفضل طريقة للمضي قدمًا في ظل الظروف التي لا يمكنني تغييرها" ويعرف أيضًا باسم "الحل البديل". ما زلت أعتبر تعليمات Jump / goto (أو funclets) أبسط طريقة وبالتالي فهي مفضلة. (لا يزال بفضل kripken لاقتراح البدائل

aardappel على حد علمي ، حاول @ sunfishcode دفع اقتراح funclets وفشل. لماذا سيكون مختلف بالنسبة لي؟

neelance لا أعتقد أن sunfishcode لديه الكثير من الوقت لدفع الاقتراح إلى ما بعد إنشائه الأولي ، لذلك فهو "متوقف" بدلاً من "فشل". كما كنت أحاول الإشارة ، يتطلب الأمر من أحد الأبطال القيام بعمل مستمر للحصول على اقتراح للوصول إلى طول الطريق من خلال خط الأنابيب.

تضمين التغريدة

شكرا على testcase! أستطيع أن أؤكد نفس المشكلة محليا. قمت بتقديم https://bugs.chromium.org/p/v8/issues/detail؟id=11237

سنحتاج إلى تنفيذ relooper في Go [..] أحد الجوانب السلبية الصغيرة هو أنه يضيف تبعية إلى binaryen لإنتاج ثنائيات wasm.

راجع للشغل ، إذا كان سيساعد ، فيمكننا إنشاء مكتبة من ثنائي كملف C واحد. ربما يكون هذا أسهل للتكامل؟

أيضًا ، باستخدام Binaryen ، يمكنك استخدام تطبيق Relooper الموجود . يمكنك تمرير الكتل الأساسية من الأشعة تحت الحمراء والسماح لها بالقيام بإعادة التوطين.

تضمين التغريدة

أعتقد أن خوارزمية مكدس LLVM أسهل / أفضل ،

لاحظ أن هذا الرابط لا يتعلق بـ LLVM المنبع ، هذا هو مترجم Cheerp (وهو شوكة من LLVM). المكدس الخاص بهم له اسم مشابه لـ LLVM ، لكنه مختلف.

لاحظ أيضًا أن منشور Cheerp يشير إلى الخوارزمية الأصلية من عام 2011 - لم يواجه تطبيق إعادة التشغيل الحديث (كما ذكرنا سابقًا) المشكلات التي

kripken شكرا لتقديم القضية.

راجع للشغل ، إذا كان سيساعد ، فيمكننا إنشاء مكتبة من ثنائي كملف C واحد. ربما يكون هذا أسهل للتكامل؟

من غير المرجح. تم تحويل مترجم Go نفسه إلى Pure Go منذ فترة ، ولا يستخدم afaik أي تبعيات أخرى للغة C. لا أعتقد أن هذا سيكون استثناء.

إليك الحالة الحالية لمقترح funclets: الخطوة التالية في العملية هي الدعوة إلى تصويت CG للدخول إلى المرحلة 1.

أنا نفسي أركز حاليًا على مناطق أخرى في WebAssembly وليس لدي النطاق الترددي لدفع funclets إلى الأمام ؛ إذا كان أي شخص مهتمًا بتولي دور البطولة في funclets ، فسأكون سعيدًا بتسليمها.

من غير المرجح. تم تحويل مترجم Go نفسه إلى Pure Go منذ فترة ، ولا يستخدم afaik أي تبعيات أخرى للغة C. لا أعتقد أن هذا سيكون استثناء.

إلى جانب ذلك ، هذا لا يحل مشكلة الاستخدام المكثف لـ relooper مما يتسبب في منحدرات أداء خطيرة في أوقات تشغيل WebAssembly.

تضمين التغريدة

أعتقد أن هذا يمكن أن يكون أفضل حالة لإضافة أشياء إلى wasm ، لكن شخصًا ما سيحتاج إلى جمع بيانات مقنعة من كود في العالم الحقيقي يُظهر مثل هذه المنحدرات الجادة. أنا لم أر مثل هذه البيانات بنفسي. لا يدعم تحليل أوجه القصور في الأداء مثل "ليس سريعًا جدًا: تحليل أداء WebAssembly مقابل الكود الأصلي" (2019) تدفق التحكم باعتباره عاملاً مهمًا أيضًا (يلاحظون قدرًا أكبر من تعليمات الفرع ، لكن هذه ليست كذلك نظرًا لتدفق التحكم المنظم - بل يرجع ذلك إلى فحوصات السلامة).

kripken هل لديك أي اقتراحات حول كيفية جمع مثل هذه البيانات؟ كيف يمكن للمرء أن يظهر أن عجز الأداء يرجع إلى تدفق التحكم المنظم؟

من غير المحتمل أن يكون هناك الكثير من العمل لتحليل أداء مرحلة التجميع ، وهو جزء من الشكوى هنا.

أنا مندهش إلى حد ما من عدم وجود بنية حالة تبديل حتى الآن ، لكن funclets تستوعب ذلك.

تضمين التغريدة

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

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

هناك خيار آخر يتمثل في تجميع النموذج إلى شيء تعتقد أنه يمكنه التعامل مع تدفق التحكم على النحو الأمثل ، أي "التراجع عن" الهيكلة. يجب أن يكون LLVM قادرًا على القيام بذلك ، لذا فإن تشغيل wasm في جهاز افتراضي يستخدم LLVM (مثل WAVM أو غسالة) أو من خلال WasmBoxC قد يكون أمرًا مثيرًا للاهتمام. ربما يمكنك تعطيل تحسينات CFG في LLVM ومعرفة مدى أهمية ذلك.

تضمين التغريدة

مثير للاهتمام ، هل فاتني شيء عن أوقات الترجمة أو استخدام الذاكرة؟ يجب أن يكون تدفق التحكم المنظم أفضل بالفعل هناك - على سبيل المثال ، من السهل جدًا الانتقال إلى نموذج SSA من ذلك ، مقارنةً بـ CFG عام. كان هذا في الواقع أحد الأسباب التي جعلت تدفق التحكم المنظم في المقام الأول. يتم قياس ذلك أيضًا بعناية شديدة لأنه يؤثر على أوقات التحميل على الويب.

(أو هل تقصد أداء المترجم على جهاز المطور؟ صحيح أن wasm يميل في اتجاه القيام بمزيد من العمل هناك ، وبدرجة أقل على العميل.)

قصدت أداء التجميع في أداة التضمين ، ولكن يبدو أنه يتم التعامل مع ذلك على أنه خطأ ، وليس بالضرورة مشكلة تتعلق بالأداء الخالص؟

تضمين التغريدة

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

تضمين التغريدة

يجب أن يكون تدفق التحكم المنظم أفضل بالفعل هناك - على سبيل المثال ، من السهل جدًا الانتقال إلى نموذج SSA من ذلك ، مقارنةً بـ CFG عام. كان هذا في الواقع أحد الأسباب التي جعلت تدفق التحكم المنظم في المقام الأول. يتم قياس ذلك أيضًا بعناية شديدة لأنه يؤثر على أوقات التحميل على الويب.

سؤال محدد للغاية ، فقط في حالة: هل تعرف أي مترجم Wasm يقوم بذلك بالفعل - "بسيط جدًا" الانتقال من "تدفق التحكم المنظم" إلى نموذج SSA. لأنه من نظرة سريعة ، فإن تدفق التحكم في Wasm ليس منظمًا (بالكامل / نهائيًا). التحكم المنظم رسميًا هو الذي لا يوجد فيه break s ، continue s ، return s (تقريبًا ، نموذج البرمجة للنظام ، بدون سحر مثل call / cc). عندما تكون موجودة ، يمكن أن يسمى تدفق التحكم هذا تقريبًا "شبه منظم".

هناك مجموعة SSA معروفة جيدًا لتدفق التحكم المنظم بالكامل: http://citeseerx.ist.psu.edu/viewdoc/summary؟doi=10.1.1.45.4503 . إليك ما يجب أن تقوله حول تدفق التحكم شبه المنظم:

بالنسبة إلى العبارات المنظمة ، أظهرنا كيفية إنشاء كل من نموذج SSA وشجرة المسيطر في مسار واحد أثناء التحليل. سنبين في القسم التالي أنه من الممكن حتى توسيع طريقتنا لتشمل فئة معينة من العبارات غير المنظمة (LOOP / EXIT و RETURN) والتي قد تسبب خروجًا من هياكل التحكم عند نقاط عشوائية. ومع ذلك ، نظرًا لأن مثل هذه المخارج هي نوع من الانتقال (المنضبط) ، فليس من المستغرب أن يكون التعامل معها أصعب بكثير من التعامل مع البيانات المنظمة.

OTOH ، هناك خوارزمية أخرى معروفة ، https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf والتي يمكن القول أيضًا أنها تمريرة واحدة ، ولكن ليس لديها مشاكل ليس فقط مع التحكم غير المنظم التدفق ، ولكن حتى مع تدفق التحكم غير القابل للاختزال (وإن لم ينتج عنه النتيجة المثلى له).

لذا ، فإن السؤال هو مرة أخرى ما إذا كنت تعلم أن بعض المشاريع قد واجه مشكلة توسيع خوارزمية Brandis / Mössenböck بالفعل ، وحقق فوائد ملموسة على هذا المسار مقارنةً بـ Braun et al. الخوارزمية (كملاحظة جانبية ، حدسي الحدسي هو أن Braun algo هو بالضبط امتداد "الحد الأعلى" ، على الرغم من أنني غبي جدًا لإثبات ذلك بشكل حدسي لنفسي ، ولا أتحدث عن دليل رسمي ، لذلك هذا هو - حدس بديهي ).

والموضوع العام للسؤال هو تحديد (على الرغم من أنني سأقول "حافظ") السبب النهائي وراء عدم اشتراك Wasm في دعم الانتقال التعسفي. لأن مشاهدة هذا الموضوع لسنوات ، فإن النموذج العقلي الذي قمت بإنشائه هو أنه تم القيام به لتجنب مواجهة CFGs غير القابلة للاختزال . وبالفعل، فإن الأكاذيب الهوة بين CFGS اختزال وغير القابل للاختزال، مع العديد من خوارزميات التحسين يجري (بكثير) أسهل للCFGS اختزالها، وهذا هو السؤال الذي يردده أبتيميزر ومشفرة في (شبه) منظم التحكم في التدفق في الوسم هو مجرد وسيلة رخيصة لضمان اختزال.

إن ذكر أي سهولة خاصة في إنتاج SSA لـ CFG منظم (ولا يبدو أن Wasm CFG منظمًا بالمعنى الرسمي) بطريقة ما يفسد الصورة الواضحة أعلاه. لهذا السبب أسأل عما إذا كانت هناك مراجع محددة تفيد بأن بناء SSA يستفيد عمليا من نموذج Wasm CFG.

شكرا.

@ kripken أنا مرتبك بعض الشيء الآن


مصدر برنامجك له تدفق تحكم معين. هذا CFG إما قابل للاختزال أو لا ، على سبيل المثال تم استخدام goto في لغة المصدر أم لا. لا توجد طريقة لتغيير هذه الحقيقة. يمكن تحويل CFG هذا إلى رمز آلة ، على سبيل المثال كما يفعل مترجم Go في الأصل.

إذا كان CFG قابل للاختزال بالفعل ، فكل شيء على ما يرام ويمكن لـ wasm VM تحميله بسرعة. يجب أن يكون أي تصريح ترجمة قادرًا على اكتشاف أن هذه هي الحالة البسيطة والقيام بالأمر السريع. لا ينبغي أن يؤدي السماح بعناصر CFG غير القابلة للاختزال إلى إبطاء هذه الحالة.

إذا كان CFG غير قابل للاختزال ، فهناك خياران:

  • المترجم يجعلها قابلة للاختزال ، على سبيل المثال عن طريق إدخال طاولة القفز. هذه الخطوة تفقد المعلومات. من الصعب استعادة CFG الأصلي دون الحاجة إلى تحليل خاص بالمترجم الذي أنتج الثنائي. بسبب فقدان المعلومات هذا ، سيكون أي رمز جهاز يتم إنشاؤه أبطأ إلى حد ما من الرمز الذي تم إنشاؤه من CFG الأولي. قد نتمكن من إنشاء رمز الجهاز هذا باستخدام خوارزمية مرور واحدة ، ولكن ذلك يكون على حساب فقدان المعلومات. [1]

  • نسمح للمترجم بإصدار CFG غير قابل للاختزال. قد يضطر الجهاز الظاهري إلى جعله قابلاً للاختزال. يؤدي هذا إلى إبطاء وقت التحميل ، ولكن فقط في الحالات التي يكون فيها CFG غير قابل للاختزال. المترجم لديه الخيار للاختيار بين تحسين أداء وقت التحميل أو أداء وقت التشغيل.

[1] إنني أدرك أنه لا يعد فقدان المعلومات حقًا إذا كان لا يزال هناك طريقة ما لعكس العملية ، لكن لا يمكنني وصفها بطريقة أفضل.


أين الخلل في تفكيري؟

تضمين التغريدة

هل تعرف أي مترجم Wasm يقوم بذلك في الواقع - "بسيط جدًا" ينتقل من "تدفق التحكم المنظم" إلى نموذج SSA.

حول VMs: لا أعرف مباشرة. لكن IIRC في اليوم الماضي قال titzer و لأحدهما أن يشرح بالتفصيل. لست متأكدًا مما إذا كانت عدم القابلية للاختزال هي المشكلة برمتها هناك أم لا. ولست متأكدًا مما إذا كانوا قد طبقوا تلك الخوارزميات التي ذكرتها أم لا.

حول أشياء أخرى غير الأجهزة الافتراضية: يستفيد مُحسِّن Binaryen بالتأكيد من تدفق التحكم المنظم لـ wasm ، وليس فقط أنه قابل للاختزال. تعد التحسينات المختلفة أبسط لأننا نعرف دائمًا مكان وجود رؤوس الحلقة ، على سبيل المثال ، والتي تم شرحها في الوسم. (من الصعب إجراء تحسينات أخرى لـ OTOH ، ولدينا أيضًا CFG IR عام لهؤلاء ...)

تضمين التغريدة

إذا كان CFG قابل للاختزال بالفعل ، فكل شيء على ما يرام ويمكن لـ wasm VM تحميله بسرعة. يجب أن يكون أي تصريح ترجمة قادرًا على اكتشاف أن هذه هي الحالة البسيطة والقيام بالأمر السريع. لا ينبغي أن يؤدي السماح بعناصر CFG غير القابلة للاختزال إلى إبطاء هذه الحالة.

ربما لا أفهمك تمامًا. لكن إمكانية تحميل برنامج wasm VM بسرعة لا يعتمد فقط على ما إذا كان يمكن اختزاله أم لا ، ولكن على كيفية تشفيره. على وجه التحديد ، كان بإمكاننا تخيل تنسيق هو CFG عام ، ومن ثم يحتاج VM إلى القيام بالعمل للتحقق من أنه قابل للاختزال. اختار Wasm تجنب هذا العمل - الترميز قابل للاختزال بالضرورة (أي ، عندما تقرأ الوسم وتقوم بالتحقق التافه ، فأنت تثبت أيضًا أنه قابل للاختزال دون القيام بأي عمل إضافي).

بالإضافة إلى ذلك ، لا يمنح تشفير wasm ضمانًا لقابلية الاختزال دون الحاجة إلى التحقق من ذلك. كما أنه يعلق على رؤوس الحلقة ، و if ، والأشياء المفيدة الأخرى (كما تصادف أن أشرت بشكل منفصل سابقًا في هذا التعليق). لست متأكدًا من مقدار الاستفادة من أجهزة VMs للإنتاج ، لكنني أتوقع أن يفعلوا ذلك. (ربما خاصة في المجمعين الأساسيين؟)

بشكل عام ، أعتقد أن السماح لـ CFGs غير القابلة للاختزال يمكن أن يبطئ الحالة السريعة ، ما لم يتم ترميز العناصر غير القابلة للاختزال بطريقة منفصلة (مثل الوظائف المقترحة).

تضمين التغريدة

شكرا على توضيحك.

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

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

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

تضمين التغريدة

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

ولكن هناك جانبًا سلبيًا لإضافة أي بناء جديد لأنه يضيف تعقيدًا إلى wasm. على وجه الخصوص ، فهذا يعني مساحة سطح أكبر على أجهزة VM مما يعني المزيد من الأخطاء المحتملة ومشكلات الأمان. اتجه Wasm نحو وجود أكبر قدر من التعقيد من جانب المطور قدر الإمكان من أجل تقليل التعقيد على الجهاز الظاهري.

لا تتعلق بعض مقترحات wasm بالسرعة فقط ، مثل GC (الذي يسمح بجمع الدورة باستخدام JS). لكن بالنسبة للمقترحات التي تدور حول السرعة ، مثل الوظائف ، نحتاج إلى إظهار أن السرعة تبرر التعقيد. كان لدينا هذا الجدل حول SIMD والذي يتعلق أيضًا بالسرعة ، وقررنا أنه يستحق ذلك لأننا رأينا أنه يمكن أن يحقق بشكل موثوق تسريعًا كبيرًا للغاية على رمز العالم الحقيقي (2x أو حتى أكثر).

(هناك فوائد أخرى بخلاف السرعة للسماح بملفات CFG العامة ، فأنا أوافق ، مثل تسهيل الأمر على المجمعين لاستهداف الوساطة. ولكن يمكننا حل ذلك بدون إضافة تعقيد إلى أجهزة عرض الوسائط الوهمية. نحن نقدم بالفعل الدعم لـ CFGs التعسفية في LLVM و Binaryen ، مما يسمح للمجمعين بإصدار CFGs وعدم القلق بشأن تدفق التحكم المنظم. إذا لم يكن ذلك جيدًا بما فيه الكفاية ، فيجب علينا - الأدوات التي أعني الأشخاص ، بمن فيهم أنا - القيام بالمزيد.)

لا تتعلق Funclet بالسرعة بقدر ما تتعلق بالسماح للغات ذات التحكم غير التافه بالتدفق إلى WebAssembly ، حيث يكون C و Go الأكثر وضوحًا ولكنه ينطبق على أي لغة غير متزامنة / تنتظر. أيضًا ، يؤدي اختيار تدفق التحكم الهرمي في الواقع إلى المزيد من الأخطاء في الأجهزة الظاهرية ، كما يتضح من حقيقة أن جميع مجمعي Wasm بخلاف V8 يحللون تدفق التحكم الهرمي إلى CFG على أي حال. يمكن أن تمثل EBBs في CFG بنيات تدفق التحكم المتعددة في Wasm وأكثر من ذلك ، ويؤدي وجود بنية واحدة لتجميعها إلى أخطاء أقل بكثير من وجود العديد من الأنواع المختلفة باستخدامات مختلفة.

حتى Lightbeam ، وهو مترجم متدفق بسيط للغاية ، شهد انخفاضًا كبيرًا في أخطاء الترجمة بعد إضافة خطوة ترجمة إضافية أدت إلى تحلل تدفق التحكم إلى CFG. يتضاعف هذا بالنسبة للجانب الآخر من هذه العملية - يعد Relooper أكثر عرضة للخطأ من إصدار funclets ، وقد أخبرني المطورين الذين يعملون على Wasm backends لـ LLVM والمجمعين الآخرين الذين كانوا funclets ليتم تنفيذها أنهم سيصدرون كل تعمل باستخدام funclets وحدها ، من أجل تحسين موثوقية وبساطة codegen. جميع المترجمين الذين ينتجون Wasm يستخدمون EBBs ، جميع المترجمين الذين يستخدمون Wasm باستثناء واحد منهم يستخدمون EBBs ، وهذا الرفض لتنفيذ Funclets أو أي طريقة أخرى لتمثيل CFGs هو ببساطة إضافة خطوة ضائعة بين ذلك تضر جميع الأطراف المعنية بخلاف فريق V8 .

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

تضمين التغريدة

يمكنك بسهولة إضافة القيد الذي يمكن اختزاله في تدفق التحكم في الوظائف

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

يقوم جميع مجمعي Wasm بخلاف V8 بتحليل تدفق التحكم الهرمي إلى CFG على أي حال.

هل تشير إلى نهج "بحر العقد" الذي تستخدمه TurboFan؟ لست خبيرًا في ذلك ، لذا سأترك الأمر للآخرين للرد.

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

لا تتعلق Funclet بالسرعة بقدر ما تتعلق بالسماح للغات ذات تدفق التحكم غير التافه بالتجميع إلى WebAssembly [..] يعد Relooper أكثر عرضة للخطأ من إصدار funclets

أوافق 100٪ على جانب الأدوات. من الصعب إصدار تعليمات برمجية منظمة من معظم المترجمين! لكن النقطة المهمة هي أنه يجعل الأمر أكثر بساطة من جانب VM ، وهذا ما اختارت شركة wasm القيام به. لكن مرة أخرى ، أوافق على أن هذا له مفاضلات ، بما في ذلك الجوانب السلبية التي ذكرتها.

هل فهمت wasm هذا الخطأ مرة أخرى في عام 2015؟ انه ممكن. أعتقد أننا أخطأنا في بعض الأشياء (مثل قابلية التصحيح ، والتبديل المتأخر إلى آلة مكدس). لكن من غير الممكن إصلاح تلك الأشياء في وقت لاحق ، وهناك حد مرتفع لإضافة أشياء جديدة ، خاصة تلك المتداخلة.

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

  • يمكنني النظر في نقل كود Binaryen CFG إلى Go ، إذا كان ذلك سيساعد مترجم Go -neelance ؟
  • يمكننا تنفيذ funclets أو شيء من هذا القبيل على جانب الأدوات فقط. وهذا يعني أننا نقدم رمز مكتبة لهذا اليوم ، ولكن يمكننا أيضًا إضافة تنسيق ثنائي. (توجد سابقة بالفعل للإضافة إلى التنسيق الثنائي wasm على جانب الأدوات ، في ملفات كائن wasm.)

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

إذا كان هناك أي عمل ملموس تم إنجازه في هذا الشأن ، فمن الجدير بالذكر أن (AFAIU) أصغر طريقة اصطلاحية لإضافة هذا إلى Wasm (كما لمح rossberg ) ستكون تقديم تعليمات الكتلة

multiloop (t in ) _n_ t out (_instr_ * end ) _n_

التي تحدد الهيئات المسماة n (مع التعليقات التوضيحية لنوع الإدخال n). الأسرة ر تعليمات ثم يتم تعميمها حتى يتسنى لجميع العلامات التي حددها multiloop هي في النطاق في كل الجسم، في النظام (كما هو الحال في أي هيئة يمكن تشعبت لمن داخل أي هيئة أخرى). عندما يتفرع جسم

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

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

تحرير: تعديل هذا للحصول على لحالة استخدام

يعد مبدأ تصميم Wasm الخاص بتفريغ العمل على الأدوات لجعل المحركات أبسط / أسرع أمرًا مهمًا للغاية وسيظل مفيدًا للغاية.

ومع ذلك ، مثل كل شيء غير تافه ، إنها مقايضة ، وليست أبيض وأسود. أعتقد أن لدينا هنا حالة يكون فيها الألم الذي يعاني منه المنتجون غير متناسب مع آلام المحركات. معظم المجمعين الذين نود إحضارهم إلى Wasm إما يستخدمون هياكل CFG التعسفية داخليًا (SSA) أو يتم استخدامها لاستهداف الأشياء التي لا تمانع في الحصول على (وحدات المعالجة المركزية). نحن نجعل العالم يقفز من خلال الأطواق ليس بالكثير من المكاسب.

شيء مثل funclets (أو multiloop) لطيف لأنه معياري: إذا لم يكن المنتج بحاجة إليه ، فستعمل الأشياء كما كان من قبل. إذا كان المحرك لا يستطيع حقًا التعامل مع CFGs التعسفية ، فيمكنه في الوقت الحالي إصداره كما لو كان نوعًا من الإنشاءات loop + br_table ، وفقط أولئك الذين يستخدمونه هم من يدفعون الثمن . ثم "يقرر السوق" ونرى ما إذا كان هناك ضغط على المحركات لإصدار شفرة أفضل لها. يخبرني شيء ما أنه إذا كان هناك الكثير من كود Wasm الذي يعتمد على funclets ، فلن يكون الأمر بمثابة كارثة كبيرة للمحركات لإصدار كود جيد لها كما يعتقد بعض الناس.

يمكنك ذلك ، لكنها ليست تافهة - ستحتاج الأجهزة الافتراضية إلى التحقق من ذلك. لا أعتقد أن هذا ممكن في مسار خطي واحد ، والذي سيكون مشكلة للمترجمات الأساسية ، الموجودة الآن في معظم أجهزة VM.

ربما أسيء فهم التوقعات الخاصة بمترجم أساسي ، لكن لماذا يهتمون بذلك؟ إذا رأيت الانتقال ، أدخل تعليمات القفز.

أوافق 100٪ على جانب الأدوات. من الصعب إصدار تعليمات برمجية منظمة من معظم المترجمين! لكن النقطة المهمة هي أنه يجعل الأمر أكثر بساطة من جانب VM ، وهذا ما اختارت شركة wasm القيام به. لكن مرة أخرى ، أوافق على أن هذا له مفاضلات ، بما في ذلك الجوانب السلبية التي ذكرتها.

لا ، كما قلت عدة مرات في تعليقي الأصلي ، فإنه _ لا _ يجعل الأمور أسهل على جانب VM. عملت على مترجم أساسي لأكثر من عام وأصبحت حياتي أسهل وأصبحت الشفرة المنبعثة أسرع بعد أن أضفت خطوة مؤقتة حولت تدفق التحكم في Wasm إلى CFG.

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

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

ربما أسيء فهم التوقعات الخاصة بمترجم أساسي ، لكن لماذا يهتمون بذلك؟ إذا رأيت الانتقال ، أدخل تعليمات القفز.

الأمر ليس بهذه البساطة نظرًا لأنك تحتاج إلى تعيين آلة التسجيل اللانهائية لـ Wasm (لا ، إنها ليست آلة مكدس ) إلى السجلات المحدودة للأجهزة المادية ، ولكن هذه مشكلة يجب على أي مترجم دفق حلها وهي متعامدة تمامًا مع CFGs مقابل تدفق التحكم الهرمي.

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

أي حالات يؤدي فيها تدفق التحكم غير القابل للاختزال إلى صعوبة كتابة التطبيقات ، والتي لا يمكنني التفكير في أي منها ، يمكن فقط إيقافها وإرجاع خطأ ، وإذا كنت بحاجة إلى خوارزمية منفصلة غير متدفقة للكشف عن عنصر التحكم هذا بنسبة 100٪ التدفق غير قابل للاختزال (لذلك لا تقبل بطريق الخطأ تدفق التحكم غير القابل للاختزال) ثم يمكن تشغيله بشكل منفصل عن المترجم الأساسي نفسه. قيل لي من قبل شخص لدي سبب للاعتقاد بأنه سلطة في هذا الموضوع (على الرغم من أنني سأتجنب استدعاءهم لأنني أعرف أنهم لا يريدون أن يتم جرهم إلى هذا الموضوع) أن هناك خوارزمية دفق بسيطة نسبيًا لاكتشاف عدم اختزال CFG ، لكن لا يمكنني القول مباشرة أن هذا صحيح.

تضمين التغريدة

ربما أسيء فهم التوقعات الخاصة بمترجم أساسي ، لكن لماذا يهتمون بذلك؟ إذا رأيت الانتقال ، أدخل تعليمات القفز.

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

تضمين التغريدة

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

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

ثم "يقرر السوق" ونرى ما إذا كان هناك ضغط على المحركات لإصدار شفرة أفضل لها.

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

تضمين التغريدة

عملت على مترجم أساسي لأكثر من عام وأصبحت حياتي أسهل وأصبحت الشفرة المنبعثة أسرع بعد أن أضفت خطوة مؤقتة حولت تدفق التحكم في Wasm إلى CFG.

مثير جدا! أي VM كان ذلك؟

سأكون أيضًا فضوليًا على وجه التحديد إذا كان تمريرة واحدة / دفقًا أم لا (إذا كان كذلك ، كيف تعاملت مع أجهزة دعم الحلقة؟) ، وكيف يتم تسجيل التخصيص.

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

لـ loop backupges: قم بتعريف backge كتعليمات تنتقل إلى سابقًا في دفق التعليمات. في أسوأ الأحوال ، إذا تم وضع الكتل بشكل عكسي ، فستحصل على المزيد من عمليات التحقق من الدعم أكثر مما تحتاج إليه بشدة.

لتخصيص السجل: هذا مجرد تخصيص قياسي

تضمين التغريدة

يمكنني النظر في نقل كود Binaryen CFG إلى Go ، إذا كان ذلك سيساعد مترجم Go -neelance ؟

لدمج Asyncify؟ الرجاء التعليق على الاقتراح .

comex

نقاط جيدة!

التكلفة الإضافية الوحيدة هي تتبع أول وآخر ذكر لكل متغير

نعم ، أعتقد أن هذا فرق كبير. يعد تخصيص سجل المسح الخطي أفضل (ولكنه أبطأ) مما يفعله المترجمون الأساسيون حاليًا ، حيث يقومون بالتجميع بطريقة دفق سريعة حقًا. أي أنه لا توجد خطوة أولية للعثور على آخر ذكر لكل متغير - فهم يجمعون في مسار واحد ، ويصدرون رمزًا كما يذهبون دون رؤية الكود لاحقًا في وظيفة wasm ، بمساعدة الهيكل ، كما أنهم يجعلون الأمر بسيطًا الخيارات كما يذهبون ("غبي" هي الكلمة المستخدمة في هذا المنشور).

يجب أن يعمل نهج دفق V8 لتسجيل التخصيص تمامًا إذا سُمح للكتل بأن تكون متكررة بشكل متبادل (كما في https://github.com/WebAssembly/design/issues/796#issuecomment-742690194) ، نظرًا لأن الأعمار الوحيدة التي يتعاملون معها مقيدة داخل كتلة واحدة (مكدس) أو يفترض أن تكون على مستوى الوظيفة (محلية).

IIUC (بالإشارة إلى تعليق titzer ) تكمن المشكلة الرئيسية لمحرك V8 في نوع CFGs التي يمكن لـ Turbofan تحسينها.

تضمين التغريدة

لقد حاولنا أن نجعل الوسم بسيطًا قدر الإمكان على الجهاز الظاهري بحيث لا يتطلب تحسينات معقدة

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

على سبيل المثال ، أنا متأكد تمامًا من أن LLVM (منتجنا الأول Wasm حاليًا) لن يتحول إلى استخدام funclets حتى يثق بأنه ليس تراجعًا في الأداء في المحركات الرئيسية.

kripken إنه جزء من Wasmtime. نعم ، إنه يتدفق وكان من المفترض أن يكون تعقيدًا (O (N) لكنني انتقلت إلى شركة جديدة قبل أن يتحقق ذلك بالكامل ، لذا فهي فقط "O (N) -ish"). https://github.com/bytecodealliance/wasmtime/tree/main/crates/lightbeam

شكرا @ Vurich ، مثيرة للاهتمام. سيكون من الرائع رؤية أرقام الأداء عند توفرها ، خاصةً لبدء التشغيل وكذلك الإنتاجية. أظن أن أسلوبك سيترجم بشكل أبطأ من النهج الذي اتبعه مهندسو V8 و SpiderMonkey ، مع إصدار كود أسرع. لذا فهي مقايضة مختلفة في هذا الفضاء. يبدو من المعقول ألا يستفيد منهجك من تدفق التحكم المنظم لـ wasm ، كما قلت ، بينما يستفيد منهجهم.

لا ، إنه مترجم متدفق ويصدر رمزًا أسرع من أي من هذين المحركين (على الرغم من وجود حالات متدهورة لم يتم إصلاحها في الوقت الذي غادرت فيه المشروع). على الرغم من أنني بذلت قصارى جهدي لإصدار رمز سريع ، إلا أنه مصمم بشكل أساسي لإرسال التعليمات البرمجية بسرعة مع كون كفاءة الإخراج مصدر قلق ثانوي. تكلفة بدء التشغيل ، على حد علمي ، هي صفر (أعلى من التكلفة المتأصلة في Wasmtime والتي يتم مشاركتها بين الخلفيات الخلفية) لأن كل بنية بيانات تبدأ غير مهيأة ويتم التجميع بالتعليمات تلو التعليمات. بينما ليس لدي أرقام لمقارنتها بـ V8 أو SpiderMonkey في متناول اليد ، لدي أرقام لمقارنتها بـ Cranelift (المحرك الأساسي في wasmtime). لقد عفا عليها الزمن عدة أشهر في هذه المرحلة ولكن يمكنك أن ترى أنها لا تصدر رمزًا أسرع من Cranelift فحسب ، بل إنها تصدر أيضًا رمزًا أسرع من Cranelift. في ذلك الوقت ، قام أيضًا بإصدار رمز أسرع من SpiderMonkey ، على الرغم من أنه سيتعين عليك أن تأخذ كلامي لذلك لن ألومك إذا كنت لا تصدقني. على الرغم من أنه ليس لدي أرقام أحدث لتسليمها ، أعتقد أن الحالة الآن هي أن كلاً من Cranelift و SpiderMonkey قد أصلحا مجموعة صغيرة من الأخطاء التي كانت المصدر الرئيسي لمخرجاتها منخفضة الأداء في هذه العلامات الدقيقة عند مقارنتها بشعاع Lightbeam ، لكن فارق سرعة الترجمة لم يتغير طوال الوقت الذي كنت فيه في المشروع لأن كل مترجم لا يزال مصممًا بشكل أساسي كما هو ، والبنية الخاصة به هي التي تؤدي إلى مستويات مختلفة من الأداء. بينما أقدر تكهناتك ، لا أعرف من أين يأتي افتراضك بأن الطريقة التي أوجزتها ستكون أبطأ.

فيما يلي المعايير ، معايير ::compile مخصصة لسرعة التجميع و ::run هي معايير لسرعة تنفيذ إخراج كود الآلة. https://gist.github.com/Vurich/8696e67180aa3c93b4548fb1f298c29e

المنهجية هنا ، يمكنك استنساخها وإعادة تشغيل المعايير لتأكيد النتائج بنفسك ، لكن من المحتمل أن تكون العلاقات العامة غير متوافقة مع أحدث إصدار من wasmtime ، لذا ستظهر لك فقط مقارنة الأداء في الوقت الذي قمت فيه بتحديث آخر مرة. العلاقات العامة. https://github.com/bytecodealliance/wasmtime/pull/1660

ومع ذلك ، فإن حجتي هي _لا _ في أن CFGs هي تمثيل داخلي مفيد للأداء في برنامج التحويل البرمجي المتدفق. حجتي هي أن CFGs لا تؤثر سلبًا على الأداء في أي مترجم ، وبالتأكيد ليس على المستوى الذي من شأنه أن يبرر منع فرق GCC and Go تمامًا من إنتاج WebAssembly على الإطلاق. لا أحد تقريبًا في هذا الموضوع يجادل ضد funclets أو امتداد مماثل لـ wasm قد عمل بالفعل في المشاريع التي يزعم أنها ستتأثر سلبًا بهذا الاقتراح. كي لا نقول إنك بحاجة إلى خبرة مباشرة للتعليق على هذا الموضوع على الإطلاق ، أعتقد أن كل شخص لديه مستوى معين من المدخلات القيمة ، ولكن هذا يعني أن هناك خطًا بين وجود رأي مختلف حول لون ركوب الدراجة وبين صنع الادعاءات التي لا تستند إلا إلى تكهنات خاملة.

تضمين التغريدة

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

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

السبب أنا متشكك يمكنك تغلب V8 وسبايدر مونكي مرات الترجمة الأساس هو لأنه، كما هو الحال في و صلات أعطى في وقت سابق، يتم ضبطها تلك المجمعين الأساس اثنين للغاية لوقت الترجمة. على وجه الخصوص ، لا يقومون بإنشاء أي IR داخلي ، بل ينتقلون مباشرة من رمز wasm إلى الجهاز. قلت إن المترجم الخاص بك يصدر بالفعل IR داخليًا (لـ CFG) - أتوقع أن تكون أوقات الترجمة الخاصة بك أبطأ لمجرد ذلك (بسبب المزيد من التفرع ، وعرض النطاق الترددي للذاكرة ، وما إلى ذلك).

لكن من فضلك ضع علامة مرجعية مقابل هؤلاء المجمعين الأساسيين! أرغب في رؤية البيانات التي تظهر أن تخميني خاطئ ، وأنا متأكد من أن مهندسي V8 و SpiderMonkey سيفعلون ذلك أيضًا. هذا يعني أنك وجدت تصميمًا أفضل يجب أن يفكروا في اعتماده.

للاختبار ضد V8 ، يمكنك تشغيل d8 --liftoff --no-wasm-tier-up ، وبالنسبة لـ SpiderMonkey يمكنك تشغيل sm --wasm-compiler=baseline .

(شكرًا لك على التعليمات الخاصة بالمقارنة مع Cranelift ، لكن Cranelift ليس مترجمًا أساسيًا ، لذا فإن مقارنة أوقات الترجمة مقابلها ليست ذات صلة في هذا السياق. مثيرة للاهتمام للغاية بخلاف ذلك ، أوافق.)

حدسي هو أن المجمعين الأساسيين لن يضطروا إلى تغيير إستراتيجية التجميع الخاصة بهم بشكل كبير لدعم funclets / والتقسيمات " المشار إليها بواسطة kripken من خلال

FWIW ، سأكون مهتمًا برؤية هذه الميزة للمناقشة في اجتماع CG مستقبلي ، وأنا أتفق بشكل عام مع Vurich على أن ممثلي المحرك يمكن أن يعترضوا بأنفسهم إذا لم يكونوا مستعدين لتطبيقها. ومع ذلك ، يجب أن نتعامل مع أي اعتراض على محمل الجد (لقد سبق أن أشرت في اجتماعات شخصية أنه في متابعة هذه الميزة ، يجب أن نحاول تجنب إصدار WebAssembly من ملحمة JavaScript

تضمين التغريدة

نعم ، أعتقد أن هذا فرق كبير. يعد تخصيص سجل المسح الخطي أفضل (ولكنه أبطأ) مما يفعله المترجمون الأساسيون حاليًا ، حيث يقومون بالتجميع بطريقة دفق سريعة حقًا. أي أنه لا توجد خطوة أولية للعثور على آخر ذكر لكل متغير - فهم يجمعون في مسار واحد ، ويصدرون رمزًا كما يذهبون دون رؤية الكود لاحقًا في وظيفة wasm ، بمساعدة الهيكل ، كما أنهم يجعلون الأمر بسيطًا الخيارات كما يذهبون ("غبي" هي الكلمة المستخدمة في هذا المنشور).

واو ، هذا حقًا بسيط جدًا.

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

كما يذكر منشور المدونة ، لا يحافظ المترجم الأساسي لـ SpiderMonkey's wasm الأساسي على حالة مخصص التسجيل من خلال "صلات التحكم في التدفق" (أي الكتل الأساسية مع أسلاف متعددة) ، بدلاً من ذلك باستخدام ABI ثابت ، أو التعيين من مكدس wasm إلى المكدس الأصلي والسجلات . اكتشفت من خلال الاختبار أنه يستخدم أيضًا ABI ثابتًا عند إدخال الكتل ، على الرغم من أن هذا ليس ارتباطًا بتدفق التحكم في معظم الحالات!

ABI الثابت هو كما يلي (في x86):

  • إذا كان هناك عدد غير صفري من المعلمات (عند إدخال كتلة) أو إرجاع (عند الخروج من كتلة) ، فإن الجزء العلوي من مكدس wasm ينتقل إلى rax ، وبقية مكدس wasm يتوافق مع x86 كومة.
  • خلاف ذلك ، فإن مجموع wasm المكدس يتوافق مع المكدس x86.

لماذا هذا مهم؟

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

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

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

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

(قد يكون التحقق مختلفًا أيضًا ولكنه لا يزال يمر مرة واحدة).

حسنًا ، تنبيهات مسبقة:

  1. هذا ليس كون بديل. نحن عالقون في إنشاء امتدادات متوافقة مع الإصدارات السابقة لـ WebAssembly الحالي.
  2. يعد مترجم SpiderMonkey الأساسي مجرد تطبيق واحد ، ومن المحتمل أنه دون المستوى الأمثل فيما يتعلق بتسجيل التخصيص: إذا كان أكثر ذكاءً قليلاً ، فإن فائدة وقت التشغيل ستفوق تكلفة وقت الترجمة.
  3. حتى إذا لم يكن المجمّعون الأساسيون بحاجة إلى معلومات إضافية ، فقد يحتاجها تحسين المجمعين لبناء SSA سريعًا.

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

@ كونراد وات كوميكس

هذه نقاط جيدة جدا! قد يكون حدسي حول المجمّعين الأساسيين خاطئًا إذن.

و comex - نعم ، كما قلت ، هذه المناقشة منفصلة عن تحسين المجمعين حيث قد تستفيد SSA من الهيكل. ربما يستحق الاقتباس قليلاً من أحد الروابط من قبل :

حسب التصميم ، يعد تحويل رمز WebAssembly إلى الأشعة تحت الحمراء لـ TurboFan (بما في ذلك إنشاء SSA) في مسار واحد مباشر فعالًا للغاية ، ويرجع ذلك جزئيًا إلى تدفق التحكم المنظم لـ WebAssembly.

@ conrad-watt أنا أتفق بالتأكيد أننا بحاجة فقط للحصول على ردود فعل مباشرة من VM الناس ، وعقل متفتح. لأكون واضحًا ، هدفي هنا ليس إيقاف أي شيء. لقد علقت هنا مطولاً لأن العديد من التعليقات بدت وكأنها تعتقد أن تدفق التحكم المنظم لـ wasm كان خطأ واضحًا أو خطأ يجب معالجته بوضوح باستخدام funclets / multiloop - أردت فقط تقديم تاريخ التفكير هنا ، وأن هناك أسبابًا قوية للنموذج الحالي ، لذلك قد لا يكون من السهل تحسينه.

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

في جانب "for" ، من المفيد أن تعرف مقدمًا أي الكتل قد تم دعمها. يستطيع المترجم المتدفق تتبع الخصائص غير الظاهرة في نظام نوع WebAssembly (على سبيل المثال ، يكون الفهرس في i المحلي ضمن حدود المصفوفة في arr المحلي). عند القفز للأمام ، قد يكون من المفيد كتابة تعليق توضيحي على الهدف بما تحمله الخصائص في تلك المرحلة. بهذه الطريقة عندما يتم الوصول إلى تسمية ، يمكن تجميع كتلتها باستخدام الخصائص الموجودة في جميع الحواف الداخلية ، على سبيل المثال لإزالة عمليات التحقق من حدود الصفيف. ولكن إذا كان من المحتمل أن يكون للتسمية دعم غير معروف ، فلا يمكن تجميع كتلها بهذه المعرفة. بالطبع ، يمكن للمترجم غير المتدفق إجراء بعض التحليل الأكثر أهمية للحلقة الثابتة ، ولكن بالنسبة لمترجم التدفق ، من المفيد ألا تقلق بشأن ما قد يكون في المستقبل. (فكرة جانبية: يذكر Vurich أن WebAssembly ليس آلة تكديس نظرًا لاستخدامه للسكان المحليين. في # 1381 ، أوضحت بعض الأسباب لتقليل الاعتماد على السكان المحليين وإضافة المزيد من عمليات المكدس. يبدو أن جعل تخصيص التسجيل أسهل سببًا آخر في هذا الاتجاه.)

على الجانب "المعارض" ، ركزت المناقشة حتى الآن على السيطرة المحلية فقط. هذا جيد بالنسبة لـ C ، ولكن ماذا عن C ++ أو لغات أخرى مختلفة مع استثناءات مماثلة؟ ماذا عن اللغات ذات الأشكال الأخرى من التحكم غير المحلي؟ غالبًا ما تكون الأشياء ذات النطاق الديناميكي منظمة بطبيعتها (أو على الأقل لا أعرف أي أمثلة على النطاقات الديناميكية التكرارية المتبادلة). أعتقد أن هذه الاعتبارات يمكن معالجتها ، ولكن عليك تصميم شيء مع وضعها في الاعتبار حتى تكون النتيجة قابلة للاستخدام في هذه الإعدادات. هذا شيء كنت أفكر فيه ، ويسعدني أن أشارك أفكاري قيد التقدم (تبدو تقريبًا امتدادًا لحلقة @ conrad-watt) مع أي شخص مهتم (على الرغم من أن هذا يبدو خارج الموضوع) ، ولكن كنت أرغب على الأقل في تقديم تنبيه مفاده أن هناك أكثر من مجرد تدفق تحكم محلي يجب أخذه في الاعتبار.

(أود أيضًا إضافة +1 آخر لسماع المزيد من الأشخاص من VM ، على الرغم من أنني أعتقد أن kripken يقوم بعمل رائع يمثل الاعتبارات.)

عندما أقول إن Lightbeam ينتج IR داخليًا ، فهذا أمر مضلل تمامًا ويجب أن أوضح. كنت أعمل في المشروع لفترة وأحيانًا يمكنك الحصول على رؤية نفقية. في الأساس ، يستهلك Lightbeam تعليمات الإدخال عن طريق التعليمات (يحتوي في الواقع على حد أقصى من التعليمات الفردية ولكن هذا ليس مهمًا بشكل خاص) ، ولكل تعليمة ينتج عددًا من تعليمات IR الداخلية ، بشكل كسول وفي مساحة ثابتة. الحد الأقصى لعدد التعليمات لكل تعليمات Wasm ثابت وصغير ، شيء مثل 6. لا يتم إنشاء مخزن مؤقت لتعليمات الأشعة تحت الحمراء للوظيفة بأكملها والعمل على ذلك. ثم يقرأ تعليمات IR واحدة تلو الأخرى. يمكنك حقًا التفكير في الأمر على أنه يحتوي على مكتبة من وظائف المساعد الأكثر عمومية والتي تنفذ كل تعليمات Wasm من حيث ، أنا أشير إليها فقط على أنها IR لأن ذلك يساعد في شرح كيف أن لديها نموذجًا مختلفًا للتحكم في التدفق وما إلى ذلك. ربما لا ينتج رمزًا بنفس سرعة برامج التحويل البرمجي الأساسية لـ V8 أو SpiderMonkey ولكن هذا لأنه لم يتم تحسينه بالكامل وليس لأنه معيب من الناحية المعمارية. نقطتي هي أنني أنمذجة داخليًا لتدفق التحكم الهرمي في Wasm كما لو كان CFG ، بدلاً من إنتاج مخزن مؤقت لـ IR في الذاكرة بالطريقة التي يعمل بها LLVM أو Cranelift.

هناك خيار آخر يتمثل في تجميع النموذج إلى شيء تعتقد أنه يمكنه التعامل مع تدفق التحكم على النحو الأمثل ، أي "التراجع عن" الهيكلة. يجب أن يكون LLVM قادرًا على القيام بذلك ، لذا فإن تشغيل wasm في جهاز افتراضي يستخدم LLVM (مثل WAVM أو غسالة) أو من خلال WasmBoxC قد يكون أمرًا مثيرًا للاهتمام.

kripken للأسف ، لا يبدو أن LLVM قادرة على التراجع عن الهيكلة حتى الآن. يجب أن يكون ممر تحسين خيوط الانتقال قادرًا على القيام بذلك ، لكنه لا يتعرف على هذا النمط حتى الآن. فيما يلي مثال يوضح بعض رموز C ++ التي تحاكي كيفية قيام خوارزمية إعادة التشغيل بتحويل CFG إلى حلقة + تبديل. تمكنت دول مجلس التعاون الخليجي من "dereloop" ، لكن clang لا تفعل ذلك: https://godbolt.org/z/GGM9rP

تضمين التغريدة نعم ، يمكن أن تكون هذه الأشياء غير متوقعة إلى حد كبير ، لذلك قد لا يكون هناك خيار أفضل من التحقق من الشفرة المنبعثة (كما تفعل ورقة "No So Fast" المرتبطة سابقًا) ، وتجنب محاولات الاختصارات مثل الاعتماد على مُحسِّن LLVM.

comex

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

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

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

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

تضمين التغريدة

بالنسبة إلى funclets / gotos ، قمت بقراءة مواصفات funclet في اليوم الآخر ، وللوهلة الأولى لا يبدو أن المترجم أحادي المسار يجب أن يواجه أي مشاكل حقيقية معه ، وبالتأكيد ليس مع مخطط regalloc مبسط. ولكن حتى مع وجود مخطط أفضل ، قد يكون الأمر جيدًا: يجب أن تقرر الحافة الأولى للوصول إلى نقطة الانضمام ما هي مهمة السجل ، ويجب أن تتوافق الحواف الأخرى.

@ conrad-watt كما ذكرت للتو في اجتماع CG ، أعتقد أننا سنكون مهتمين جدًا برؤية التفاصيل حول الشكل الذي ستبدو عليه الحلقة المتعددة.

aardappel نعم ، لقد rossberg رسمها في الأصل ردًا على المسودة الأولى من funclets.

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

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

https://gist.github.com/conrad-watt/6a620cb8b7d8f0191296e3eb24dffdef

أعتقد أن السؤالين الفوريين القابل للتنفيذ (راجع قسم المتابعة لمزيد من التفاصيل) هما:

  • هل يمكننا العثور على برامج "جامحة" تعاني حاليًا وستستفيد من الأداء من multiloop ؟ قد تكون هذه برامج تقدم لها تحويلات LLVM تدفق تحكم غير قابل للاختزال حتى لو لم يكن موجودًا في البرنامج المصدر.
  • هل هناك عالم يتم فيه تنفيذ multiloop من جانب المنتج أولاً ، مع بعض طبقة نشر الارتباط / الترجمة لـ Wasm "الويب"؟

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

نظرًا لأن هذه المناقشات قد تتفرع إلى حد ما ، فقد يكون من المناسب تدوير بعضها في مشكلات في مستودع

أنا سعيد جدًا برؤية تقدم في هذه القضية. شكرا جزيلا لجميع الأشخاص المعنيين!

هل يمكننا أن نجد برامج "جامحة" تعاني حاليًا وستستفيد من أداء multiloop من ناحية الأداء؟ قد تكون هذه برامج تقدم لها تحويلات LLVM تدفق تحكم غير قابل للاختزال حتى لو لم يكن موجودًا في البرنامج المصدر.

أود أن أحذر قليلاً من التفكير الدائري: البرامج ذات الأداء السيئ حاليًا أقل احتمالًا أن تحدث "في البرية" لهذا السبب بالضبط.

أعتقد أن معظم برامج Go يجب أن تستفيد كثيرًا. يحتاج برنامج التحويل البرمجي Go إما إلى مجموعات WebAssembly أو multiloop لتتمكن من إصدار تعليمات برمجية فعالة تدعم goroutines Go.

غالبًا ما تؤدي أدوات مطابقة التعبير العادي المُجمَّعة مسبقًا ، جنبًا إلى جنب مع غيرها من آلات الحالة المُترجمة مسبقًا ، إلى تدفق تحكم غير قابل للاختزال. من الصعب تحديد ما إذا كانت خوارزمية "الاندماج" لأنواع الواجهة ستؤدي إلى تدفق تحكم غير قابل للاختزال.

  • توافق على أن يتم نقل هذه المناقشة إلى المشكلات الموجودة في funclets (أو الريبو الجديد).
  • توافق على أن العثور على البرنامج الذي قد يستفيد منه من الصعب تحديده دون أن يكون LLVM (و Go ، وغيرهما) ينبعث في الواقع تدفق التحكم الأمثل (والذي قد يكون غير قابل للاختزال). قد يكون عدم الكفاءة الناتج عن FixIrreducibleControlFlow والأصدقاء هو مشكلة "الموت بألف تخفيضات" عبر ثنائي كبير.
  • على الرغم من أنني أرحب بتطبيق الأدوات فقط باعتباره الحد الأدنى المطلق للتقدم الناتج عن هذه المناقشة ، إلا أنه لن يكون هو الأمثل ، حيث يتمتع المنتجون الآن بخيار صعب بالاستفادة من هذه الوظيفة للراحة (ولكنهم يواجهون بعد ذلك تراجعًا في الأداء لا يمكن التنبؤ به / المنحدرات) ، أو قم بالعمل الشاق لتغيير مخرجاتها وفقًا للنسخة القياسية لجعل الأشياء يمكن التنبؤ بها.
  • إذا تقرر أن "gotos" هي في أحسن الأحوال ميزة للأدوات فقط ، فأنا أزعم أنه من المحتمل أن تفلت من ميزة أبسط من multiloop ، لأن كل ما يهمك هو راحة المنتج. بالحد الأدنى المطلق ، سيكون goto <function_byte_offset> هو الشيء الوحيد المطلوب إدراجه في هيئات Wasm العادية للسماح لـ WABT أو Binaryen بتحويله إلى Wasm قانوني. تعتبر أشياء مثل توقيعات الكتابة مفيدة إذا احتاجت المحركات إلى التحقق من حلقة متعددة بسرعة ، ولكن إذا كانت أداة ملائمة ، فقد تجعلها أيضًا مريحة للغاية للانبعاث.

توافق على أن العثور على البرنامج الذي قد يستفيد منه من الصعب تحديده دون أن يكون LLVM (و Go ، وغيرهما) ينبعث في الواقع تدفق التحكم الأمثل (والذي قد يكون غير قابل للاختزال).

أوافق على أن الاختبار على سلاسل الأدوات المعدلة + الأجهزة الافتراضية سيكون هو الأمثل. ولكن يمكننا مقارنة تكوينات الصوامع الحالية بالتركيبات الأصلية التي تتمتع بالتحكم الأمثل في التدفق. Not So Fast وآخرون نظروا إلى هذا بطرق مختلفة (عدادات الأداء ، التحقيق المباشر) ولم يجدوا أن تدفق التحكم غير القابل للاختزال عامل مهم.

وبشكل أكثر تحديدًا ، لم يجدوا أنه عامل مهم لـ C / C ++. قد يكون لذلك علاقة بـ C / C ++ أكثر من أداء تدفق التحكم غير القابل للاختزال. (أنا بصراحة لا أعرف.) يبدو أن neelance لديه سبب للاعتقاد بأن الأمر نفسه لن يكون صحيحًا بالنسبة لـ Go.

أشعر أن هناك جوانب متعددة لهذه المشكلة ، ومن المفيد معالجتها من خلال اتجاهات متعددة.

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

ثانيًا ، يمكننا التفكير في ميزات "pre" -WebAssembly التي تستحق دمجها مباشرةً في WebAssembly "الحقيقي". يمكننا القيام بذلك بطريقة مستنيرة لأنه سيكون لدينا وحدات WebAssembly "المسبقة" التي يمكننا تحليلها قبل تحويلها إلى وحدات WebAssembly "الحقيقية".

منذ بضع سنوات ، حاولت تجميع محاكي كود ثنائي معين للغة ديناميكية (https://github.com/ciao-lang/ciao) لتجميع الويب وكان الأداء بعيدًا عن المستوى الأمثل (أحيانًا أبطأ 10 مرات من الإصدار الأصلي). احتوت حلقة التنفيذ الرئيسية على مفتاح إرسال رمز ثانوي كبير ، وتم ضبط المحرك بدقة لعقود ليعمل على أجهزة فعلية ، ونستخدم بشدة الملصقات و gotos. أتساءل عما إذا كان هذا النوع من البرامج سيستفيد من دعم تدفق التحكم غير القابل للاختزال أو إذا كانت المشكلة مشكلة أخرى. لم يكن لدي الوقت لإجراء مزيد من التحقيقات ولكن يسعدني المحاولة مرة أخرى إذا كان من المعروف أن الأمور قد تحسنت. بالطبع أفهم أن تجميع لغات أخرى من VM إلى wasm ليس هو حالة الاستخدام الرئيسية ، ولكن سيكون من الجيد معرفة ما إذا كان هذا ممكنًا في النهاية ، خاصة وأن الثنائيات العالمية التي تعمل بكفاءة ، في كل مكان ، هي إحدى المزايا الموعودة لـ كان م. (شكراً واعتذاراً إذا تمت مناقشة هذا الموضوع بالذات في بعض القضايا الأخرى)

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

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

beriberikix picture beriberikix  ·  7تعليقات

thysultan picture thysultan  ·  4تعليقات

jfbastien picture jfbastien  ·  6تعليقات

dpw picture dpw  ·  3تعليقات

void4 picture void4  ·  5تعليقات