अपने ऐप्लिकेशन के JavaScript में किसी हॉट पाथ को WebAssembly से बदलना

यह लगातार तेज़ है, यो

मेरे पिछले समय में वे लेख जिनमें WebAssembly के बारे में बताया गया था C/C++ के लाइब्रेरी ईकोसिस्टम को वेब पर लाया जा सकता है. एक ऐसा ऐप्लिकेशन जो C/C++ लाइब्रेरी का बहुत ज़्यादा इस्तेमाल किया जाता है, जिसे squoosh कहते हैं. ऐसा वेब ऐप्लिकेशन जिसकी मदद से इमेज कंप्रेस किए जा सकते हैं. ऐसा करने के लिए, कई तरह के कोडेक इस्तेमाल किए जा सकते हैं C++ से WebAssembly में कंपाइल किया गया.

WebAssembly एक लो-लेवल वर्चुअल मशीन है, जो स्टोर किए गए बाइट कोड पर काम करती है .wasm फ़ाइलों में. इस बाइट कोड को अच्छी तरह से टाइप किया गया है और इसे इस तरह से बनाया गया है कि इसे होस्ट सिस्टम के लिए इससे ज़्यादा तेज़ी से कंपाइल और ऑप्टिमाइज़ किया जा सकता है JavaScript यह काम कर सकता है. WebAssembly, ऐसे कोड को चलाने के लिए एनवायरमेंट देता है जिसमें शुरू से ही ध्यान में रखते हुए सैंडबॉक्स करना और एम्बेड करना.

मेरे हिसाब से, वेब पर परफ़ॉर्मेंस से जुड़ी ज़्यादातर समस्याएं ऐसे लेकिन बहुत ज़्यादा पेंट करने की ज़रूरत होती है, लेकिन ऐप्लिकेशन को समय-समय पर बहुत महंगा टास्क है, जिसमें बहुत समय लगता है. WebAssembly से मदद मिल सकती है यहां.

द हॉट पाथ

स्क्वॉश में हमने एक JavaScript फ़ंक्शन लिखा जो इमेज बफ़र को 90 डिग्री के गुणज से घुमाता है. हालांकि OffscreenCanvas इनके लिए सही रहेगा यह उन सभी ब्राउज़र पर काम नहीं करता जिन्हें हम टारगेट कर रहे थे. साथ ही, Chrome में गड़बड़ी.

यह फ़ंक्शन किसी इनपुट इमेज के हर पिक्सल में फिर से दोहराया जाता है और इसे रोटेशन पाने के लिए आउटपुट इमेज में अलग जगह. 4094px x 4096px (16 मेगापिक्सल) वाली इमेज को अपलोड करने के लिए, 1.6 करोड़ से ज़्यादा बार इनर कोड ब्लॉक, जिसे हम "हॉट पाथ" कहते हैं. इसके बड़े होने के बावजूद बार-बार इस्तेमाल किए जाते हैं, तो हमने तीन ब्राउज़र में से दो ब्राउज़र की जांच करके 2 में काम पूरा किया सेकंड या उससे कम. इस तरह के इंटरैक्शन के लिए स्वीकार की जाने वाली अवधि.

for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
    for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
    const in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
    outBuffer[i] = inBuffer[in_idx];
    i += 1;
    }
}

हालांकि, एक ब्राउज़र को आठ सेकंड से ज़्यादा समय लगता है. ब्राउज़र का JavaScript को ऑप्टिमाइज़ करने का तरीका तो बहुत ही मुश्किल है और अलग-अलग इंजन अलग-अलग चीज़ों के हिसाब से ऑप्टिमाइज़ होते हैं. कुछ कॉन्फ़िगरेशन, प्रोसेस नहीं किए जा सकते और कुछ को डीओएम के साथ इंटरैक्शन के लिए ऑप्टिमाइज़ किया जाता है. तय सीमा में इस मामले में, हमने एक ब्राउज़र में एक ऐसे पाथ पर क्लिक किया है जो ऑप्टिमाइज़ नहीं किया गया है.

वहीं दूसरी ओर, WebAssembly को पूरी तरह से प्रोसेस नहीं किया गया है और न ही एक्ज़ीक्यूट किया जा सकता है. तो अगर हमें कोड के लिए तेज़ और अनुमानित परफ़ॉर्मेंस चाहिए, तो WebAssembly से मदद मिल सकती है.

परफ़ॉर्मेंस का अनुमान लगाने के लिए WebAssembly

आम तौर पर, JavaScript और WebAssembly की परफ़ॉर्मेंस एक जैसी हो सकती है. हालांकि, JavaScript के लिए इस प्रदर्शन तक केवल "तेज़ पथ", और उस "तेज़ रास्ते" पर बने रहना अक्सर पेचीदा होता है. इसका एक मुख्य फ़ायदा है WebAssembly ऑफ़र की परफ़ॉर्मेंस के बारे में अनुमान लगाया जा सकता है. यह सभी ब्राउज़र पर भी उपलब्ध है. द स्ट्रिक्ट टाइपिंग और लो-लेवल आर्किटेक्चर से कंपाइलर बेहतर ढंग से गारंटी देता है कि WebAssembly कोड को सिर्फ़ एक बार ऑप्टिमाइज़ करना होगा और हमेशा “तेज़ पथ” का उपयोग करें.

WebAssembly के लिए लिखना

पहले हम C/C++ लाइब्रेरी लेते थे और उन्हें WebAssembly में इकट्ठा करते थे, ताकि वेब पर सही तरीके से काम करने के लिए किया जा सकता है. लाइब्रेरी के कोड को हमने ठीक से नहीं छपाया, ब्राउज़र के बीच पुल बनाने के लिए बहुत कम C/C++ कोड लिखे हैं और लाइब्रेरी खोलें. इस बार हमारा मकसद अलग है: हम लिखना चाहते हैं जिससे हम WebAssembly में किसी WebAssembly के फ़ायदों का.

WebAssembly आर्किटेक्चर

WebAssembly के लिए लिखते समय, इसके बारे में थोड़ी और जानकारी हासिल करना फ़ायदेमंद होता है WebAssembly क्या है.

WebAssembly.org को कोट करने के लिए:

जब C या Rust कोड को WebAssembly में इकट्ठा किया जाता है, तो आपको .wasm मिलता है ऐसी फ़ाइल जिसमें मॉड्यूल का एलान किया गया है. इस एलान में ये चीज़ें शामिल हैं "इंपोर्ट" मॉड्यूल अपने एनवायरमेंट से उम्मीद करता है, जो एक्सपोर्ट की एक सूची होती है. मॉड्यूल, होस्ट (फ़ंक्शन, कॉन्सटेंट, मेमोरी के हिस्से) को उपलब्ध कराता है और बेशक, उसमें मौजूद फ़ंक्शन के असली बाइनरी निर्देश.

कुछ ऐसा जिसके बारे में मुझे तब तक पता नहीं चला, जब तक मैंने इस पर गौर नहीं किया: वह स्टैक जो WebAssembly एक "स्टैक-आधारित वर्चुअल मशीन" है के हिस्से में सेव नहीं होता है मेमोरी है जिसका इस्तेमाल WebAssembly मॉड्यूल करता है. स्टैक पूरी तरह से वीएम-इंटरनल है और वेब डेवलपर इसे ऐक्सेस नहीं कर सकते (इसमें DevTools की मदद शामिल नहीं है). जैसे कि यह संभव है ऐसे WebAssembly मॉड्यूल लिखने के लिए जिन्हें किसी अतिरिक्त मेमोरी की ज़रूरत नहीं होती और सिर्फ़ वीएम-इंटरनल स्टैक का इस्तेमाल करें.

हमारे मामले में, हमें आर्बिट्रेरी ऐक्सेस की अनुमति देने के लिए, कुछ अतिरिक्त मेमोरी का इस्तेमाल करना होगा हमारी इमेज के पिक्सल तक बदलने चाहिए और उस इमेज का घुमाया हुआ वर्शन जनरेट करते हैं. यह है WebAssembly.Memory किसलिए है.

मेमोरी मैनेज करना

आम तौर पर, अतिरिक्त मेमोरी इस्तेमाल करने के बाद, आपको किसी न किसी तरह उस मेमोरी को मैनेज किया जा सकता है. मेमोरी के कौनसे हिस्से इस्तेमाल किए जा रहे हैं? कौनसी सेवाएं बिना किसी शुल्क के उपलब्ध हैं? उदाहरण के लिए, C में malloc(n) फ़ंक्शन है, जो मेमोरी स्पेस ढूंढता है लगातार n बाइट में से. इस तरह के फ़ंक्शन को "एलोकेटर" भी कहा जाता है. बेशक, इस्तेमाल किए जा रहे ऐलोकेटर को लागू करने की प्रक्रिया को आपके WebAssembly मॉड्यूल का इस्तेमाल करने पर, आपकी फ़ाइल का साइज़ बढ़ जाएगा. यह आकार और प्रदर्शन में उपलब्ध ये सभी फ़ंक्शन, एक-दूसरे से काफ़ी अलग हो सकते हैं. यह बदलाव, एल्गोरिदम का इस्तेमाल किया जाता है. यही वजह है कि कई भाषाएं कई प्रोसेस लागू करने की सुविधा देती हैं चुनने के लिए, ("dmalloc", "emmalloc", "wee_alloc" वगैरह) चुनें.

हमारे मामले में, हमें इनपुट इमेज के डाइमेंशन के बारे में पता है (और इसलिए डाइमेंशन (आउटपुट इमेज के डाइमेंशन) को बदल सकते हैं. हम यहां हमें एक अवसर दिखा: परंपरागत रूप से, हम इनपुट इमेज के RGBA बफ़र को WebAssembly फ़ंक्शन में पैरामीटर जोड़ें और घुमाई गई इमेज को रिटर्न के तौर पर वापस करें वैल्यू. इस रिटर्न वैल्यू को जनरेट करने के लिए, हमें ऐलोकेटर का इस्तेमाल करना होगा. हालांकि, हमें पता है कि ज़रूरी मेमोरी की कुल मात्रा कितनी है (इनपुट के साइज़ से दोगुना एक बार इनपुट के लिए और एक बार आउटपुट के लिए), तो हम इनपुट इमेज को JavaScript का इस्तेमाल करके WebAssembly मेमोरी में, WebAssembly मॉड्यूल चलाकर दूसरी, घुमाई गई इमेज और फिर नतीजा पढ़ने के लिए JavaScript का इस्तेमाल करें. हम यह हासिल कर सकते हैं इसके लिए, आपको किसी मेमोरी मैनेजमेंट की ज़रूरत भी नहीं पड़ेगी!

पसंद के हिसाब से बनाया गया

अगर आपने ओरिजनल JavaScript फ़ंक्शन को देखा है जिसे हम WebAssembly-fy करना चाहते हैं, तो आप देख सकते हैं कि यह पूरी तरह से कंप्यूटेशनल जिसमें JavaScript से जुड़े एपीआई मौजूद नहीं हैं. इसलिए यह काफ़ी सीधा होना चाहिए इसे किसी भी भाषा में पोर्ट कर सकते हैं. हमने तीन अलग-अलग भाषाओं का आकलन किया जो WebAssembly में इकट्ठा किया जाता है: C/C++, Rust, और AssemblyScript. सिर्फ़ एक सवाल हमें हर भाषा के लिए जवाब देना होगा: हम रॉ मेमोरी कैसे ऐक्सेस करते हैं और मेमोरी मैनेजमेंट फ़ंक्शन का इस्तेमाल किए बिना?

C और एमस्क्रिप्टन

Emscripten, WebAssembly टारगेट के लिए सी कंपाइलर है. एम्स्क्रिप्टेन का लक्ष्य यह है कि GCC या clang जैसे जाने-माने C कंपाइलर के लिए ड्रॉप-इन रिप्लेसमेंट के तौर पर काम करता है फ़्लैग करने के लिए डिज़ाइन किया गया है. यह Emscripten के मिशन का अहम हिस्सा है क्योंकि यह मौजूदा C और C++ कोड को WebAssembly में कंपाइल करना चाहता है, किया जा सकता है.

रॉ मेमोरी को ऐक्सेस करना, C का काम है और इसके लिए पॉइंटर मौजूद हैं कारण:

uint8_t* ptr = (uint8_t*)0x124;
ptr[0] = 0xFF;

यहां हम 0x124 संख्या को पॉइंटर में, साइन नहीं किए गए 8-बिट में बदल रहे हैं पूर्णांक (या बाइट). इससे ptr वैरिएबल, बेहतर तरीके से एक अरे में बदल जाता है इसकी शुरुआत मेमोरी के पते 0x124 से होती है, जिसे हम किसी भी दूसरे कलेक्शन की तरह इस्तेमाल कर सकते हैं, इससे हमें पढ़ने और लिखने के लिए, अलग-अलग बाइट का ऐक्सेस मिलता है. हमारे मामले में, उस इमेज का RGBA बफ़र देख रहे हैं, जिसे पाने के लिए हम फिर से क्रम में लगाना चाहते हैं घुमाव. पिक्सल को एक जगह से दूसरी जगह ले जाने के लिए, हमें एक बार में लगातार चार बाइट मूव करनी होंगी (हर चैनल के लिए एक बाइट: R, G, B, और A). इसे आसान बनाने के लिए, हम साइन नहीं किए गए 32-बिट पूर्णांक का कलेक्शन. कन्वेंशन के मुताबिक, हमारी इनपुट इमेज शुरू होगी पता 4 पर लिखें और इनपुट इमेज के ठीक बाद हमारी आउटपुट इमेज शुरू हो जाएगी खत्म होने की तारीख:

int bpp = 4;
int imageSize = inputWidth * inputHeight * bpp;
uint32_t* inBuffer = (uint32_t*) 4;
uint32_t* outBuffer = (uint32_t*) (inBuffer + imageSize);

for (int d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
    for (int d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
    int in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
    outBuffer[i] = inBuffer[in_idx];
    i += 1;
    }
}

पूरे JavaScript फ़ंक्शन को C में पोर्ट करने के बाद, हम C फ़ाइल को कंपाइल कर सकते हैं emcc के साथ:

$ emcc -O3 -s ALLOW_MEMORY_GROWTH=1 -o c.js rotate.c

हमेशा की तरह, emscripten एक ग्लू कोड फ़ाइल जनरेट करता है, जिसे c.js कहा जाता है. साथ ही, एक Wasm मॉड्यूल जनरेट होता है c.wasm नाम का इस्तेमाल करें. ध्यान दें कि Wasm मॉड्यूल सिर्फ़ ~260 बाइट की gzip करता है, जबकि gzip के बाद ग्लू कोड करीब 3.5 केबी का है. कुछ उलझन होने के बाद, हम इसे छोड़ सकते थे ग्लू कोड को कॉपी कर सकता है और वनीला एपीआई की मदद से WebAssembly मॉड्यूल को इंस्टैंशिएट कर सकता है. आम तौर पर, Emscripten तब तक मुमकिन होता है, जब तक कि किसी चीज़ का इस्तेमाल नहीं किया जा रहा है चुनें.

Rust

Rust एक नई और मॉडर्न प्रोग्रामिंग भाषा है, जिसमें रिच टाइप सिस्टम है. इसमें कोई रनटाइम नहीं होता साथ ही, एक ऐसा मालिकाना हक भी है जो मेमोरी और थ्रेड की सुरक्षा की गारंटी देता है. ज़ंग WebAssembly की मुख्य सुविधा के तौर पर भी काम करती है और Rust की टीम WebAssembly इकोसिस्टम में बहुत से बेहतरीन टूल का योगदान दिया गया है.

इनमें से एक टूल wasm-pack है, रस्टवासम वर्किंग ग्रुप. wasm-pack अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है वह आपका कोड लेता है और उसे एक ऐसे वेब-फ़्रेंडली मॉड्यूल में बदल देता है जो काम करता हो वेबपैक जैसे बंडलर के साथ प्रॉडक्ट की अलग-अलग कैटगरी. wasm-pack बहुत ज़्यादा सुविधाजनक अनुभव, लेकिन वर्तमान में केवल Rust के लिए काम करता है. यह ग्रुप है हम WebAssembly में टारगेट की जाने वाली अन्य भाषाओं के लिए भी इसका इस्तेमाल करने के बारे में सोच रहे हैं.

Rust में, C वाली सरणियों को स्लाइस होता है. और C की तरह, हमें भी एक साइट बनानी है, स्लाइस हैं जो हमारे शुरुआती पते का इस्तेमाल करते हैं. यह मेमोरी की सुरक्षा वाले मॉडल पर लागू नहीं होता Rust लागू करते हैं, इसलिए हमें unsafe कीवर्ड का इस्तेमाल करना होगा, जिसकी मदद से हम ऐसे कोड लिख सकते हैं जो उस मॉडल का पालन नहीं करता.

let imageSize = (inputWidth * inputHeight) as usize;
let inBuffer: &mut [u32];
let outBuffer: &mut [u32];
unsafe {
    inBuffer = slice::from_raw_parts_mut::<u32>(4 as *mut u32, imageSize);
    outBuffer = slice::from_raw_parts_mut::<u32>((imageSize * 4 + 4) as *mut u32, imageSize);
}

for d2 in 0..d2Limit {
    for d1 in 0..d1Limit {
    let in_idx = (d1Start + d1 * d1Advance) * d1Multiplier + (d2Start + d2 * d2Advance) * d2Multiplier;
    outBuffer[i as usize] = inBuffer[in_idx as usize];
    i += 1;
    }
}

इसका इस्तेमाल करके Rust फ़ाइलों को कंपाइल करना

$ wasm-pack build

करीब 100 बाइट के ग्लू कोड (gzip के बाद) के साथ 7.6 केबी का Wasm मॉड्यूल होता है.

AssemblyScript

AssemblyScript में यह एक युवा प्रोजेक्ट है, जो TypeScript-to-WebAssembly कंपाइलर के तौर पर काम करता है. यह समय है हालांकि, यह ध्यान रखना ज़रूरी है कि यह सिर्फ़ किसी TypeScript का इस्तेमाल नहीं करेगा. AssemblyScript उसी सिंटैक्स का इस्तेमाल करती है जो TypeScript का इस्तेमाल करती है, लेकिन स्टैंडर्ड को बदल देती है लाइब्रेरी बनाई जा सकती है. उनकी मानक लाइब्रेरी WebAssembly. इसका मतलब यह है कि आप इस्तेमाल की जा रही किसी TypeScript को कंपाइल नहीं कर सकते WebAssembly में शामिल हो सकते हैं, लेकिन इसका मतलब है कि आपको नई चीज़ें सीखने की ज़रूरत नहीं है प्रोग्रामिंग भाषा है!

    for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
      for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
        let in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
        store<u32>(offset + i * 4 + 4, load<u32>(in_idx * 4 + 4));
        i += 1;
      }
    }

हमारे rotate() फ़ंक्शन में मौजूद छोटी टाइप की सतह को ध्यान में रखते हुए, यह इस कोड को AssemblyScript में पोर्ट करना काफ़ी आसान है. load<T>(ptr: usize) और store<T>(ptr: usize, value: T) फ़ंक्शन, AssemblyScript के ज़रिए रॉ मेमोरी ऐक्सेस करें. हमारी AssemblyScript फ़ाइल को कंपाइल करने के लिए, हमें सिर्फ़ AssemblyScript/assemblyscript एनपीएम पैकेज इंस्टॉल करके, उसे चलाना है

$ asc rotate.ts -b assemblyscript.wasm --validate -O3

AssemblyScript हमें ~300 बाइट Wasm मॉड्यूल और नहीं ग्लू कोड देगा. यह मॉड्यूल बस vanilla WebAssembly API के साथ काम करता है.

WebAssembly Forensics

अन्य भाषाओं की तुलना में, Rust का साइज़ 7.6 केबी से ज़्यादा है. यह लीजिए WebAssembly ईकोसिस्टम में मौजूद कुछ टूल हैं. इनकी मदद से, आपकी WebAssembly फ़ाइलें (चाहे वे किसी भी भाषा से बनाई गई हों) और आपको बताएंगे कि क्या समस्या आ रही है और आपकी स्थिति को बेहतर बनाने में भी मदद करेगा.

ट्विगी

Twiggy, Rust's का एक और टूल है WebAssembly टीम, जो WebAssembly से अहम जानकारी वाला डेटा इकट्ठा करती है मॉड्यूल का इस्तेमाल नहीं किया जाएगा. यह टूल, Rust के हिसाब से नहीं है. इसकी मदद से, मॉड्यूल का कॉल ग्राफ़, उपयोग न किए गए या अनावश्यक अनुभाग पता लगाने और कौनसे सेक्शन आपके मॉड्यूल के कुल फ़ाइल साइज़ में योगदान दे रहे हैं. कॉन्टेंट बनाने बाद में, Twiggy के top निर्देश से ऐसा किया जा सकता है:

$ twiggy top rotate_bg.wasm
Twiggy इंस्टॉल करने का स्क्रीनशॉट

इस मामले में हम देख सकते हैं कि हमारे ज़्यादातर फ़ाइल साइज़ ऐलोकेटर. यह आश्चर्य की बात है, क्योंकि हमारा कोड डायनैमिक एलोकेशन का इस्तेमाल नहीं कर रहा है. "फ़ंक्शन के नाम" भी एक बड़ा योगदान है सब-सेक्शन में दिखेगा.

Wasm-Strip

wasm-strip, WebAssembly बाइनरी टूलकिट का एक टूल है. इसका इस्तेमाल छोटे शब्दों में किया जाता है. इसमें शामिल है कुछ टूल का इस्तेमाल भी किया जा सकता है, जिनकी मदद से WebAssembly मॉड्यूल की जांच और उनमें बदलाव किया जा सकता है. wasm2wat एक डिसअसेंबलर है, जो बाइनरी Wasm मॉड्यूल को कोई भी व्यक्ति आसानी से पढ़ सकता है. वॉट में wat2wasm भी होता है, जिसकी मदद से जिसे कोई भी व्यक्ति आसानी से पढ़ सकता है. जबकि हमने इस्तेमाल किया था हमारी WebAssembly फ़ाइलों की जांच करने के लिए, ये दो पूरक टूल हैं. इनसे हमें पता चला है कि wasm-strip सबसे ज़्यादा काम की है. wasm-strip ग़ैर-ज़रूरी सेक्शन हटा देता है और एक WebAssembly मॉड्यूल से लिया गया मेटाडेटा:

$ wasm-strip rotate_bg.wasm

इससे रस्ट मॉड्यूल की फ़ाइल का साइज़ 7.5 केबी से कम होकर 6.6 केबी (gzip के बाद) हो जाएगा.

wasm-opt

wasm-opt, Binaryen का एक टूल है. यह एक WebAssembly मॉड्यूल लेता है और इसे साइज़ और की परफ़ॉर्मेंस सिर्फ़ बाइट कोड के आधार पर तय होती है. Emscripten जैसे कुछ टूल पहले से ही काम कर रहे हैं कुछ नहीं करते. आम तौर पर कुछ आज़माने और सेव करने में इन टूल का इस्तेमाल करके अतिरिक्त बाइट जोड़ना होगा.

wasm-opt -O3 -o rotate_bg_opt.wasm rotate_bg.wasm

wasm-opt के साथ हम कुछ बाइट शेव करके कुल इतनी बाइट कर सकते हैं gzip के बाद 6.2KB.

#![no_std]

कुछ सलाह और रिसर्च के बाद, हमने बिना किसी शुल्क के हमारा Rust कोड फिर से लिखा Rust की मानक लाइब्रेरी से, #![no_std] सुविधा. यह डाइनैमिक मेमोरी ऐलोकेशन को पूरी तरह से बंद कर देता है. साथ ही, हमारे मॉड्यूल से लिया गया ऐलोकेटर कोड. इस Rust फ़ाइल को कंपाइल किया जा रहा है के साथ

$ rustc --target=wasm32-unknown-unknown -C opt-level=3 -o rust.wasm rotate.rs

wasm-opt, wasm-strip, और gzip के बाद, 1.6 केबी का Wasm मॉड्यूल मिला. हालांकि, यह यह C और AssemblyScript के जनरेट किए गए मॉड्यूल से भी बड़ा है. साथ ही, यह छोटा है उसे हल्का माना जा सकता है.

परफ़ॉर्मेंस

सिर्फ़ फ़ाइल के साइज़ के आधार पर किसी नतीजे पर पहुंचने से पहले, हम इस सफ़र पर गए का इस्तेमाल करें. हमने परफ़ॉर्मेंस को कैसे मापा और इसका क्या नतीजा निकला?

मानदंड कैसे बनाएं

WebAssembly का एक लो-लेवल बाइट कोड फ़ॉर्मैट होने के बावजूद, इसे भेजना ज़रूरी है कंपाइलर की मदद से होस्ट के लिए मशीन कोड जनरेट करता है. JavaScript की तरह, कंपाइलर कई स्टेज में काम करता है. आसान शब्दों में: पहला चरण तो कंपाइल करने में ज़्यादा तेज़ी से काम करता है, लेकिन आम तौर पर धीमा कोड जनरेट करता है. मॉड्यूल शुरू होने के बाद चलाने पर, ब्राउज़र देखता है कि कौन से हिस्से अक्सर उपयोग किए जाते हैं और उन्हें यह काम करता है.

हमारे इस्तेमाल का उदाहरण यह है कि इमेज को घुमाने के लिए कोड का इस्तेमाल किया जाएगा एक बार, शायद दो बार. इसलिए ज़्यादातर मामलों में हमें ऑप्टिमाइज़ करने वाले कंपाइलर के फ़ायदे. इस बात का ध्यान रखना ज़रूरी है कि मानदंड. हमारे WebAssembly मॉड्यूल को लूप में 10,000 बार चलाने से, ऐसे नतीजे मिल सकते हैं जो भरोसेमंद नहीं हैं. सही आंकड़े पाने के लिए, हमें मॉड्यूल को एक बार चलाना चाहिए और उस सिंगल प्रोसेस से मिले आंकड़ों के आधार पर फै़सले लें.

परफ़ॉर्मेंस की तुलना

हर भाषा के हिसाब से स्पीड की तुलना
प्रति ब्राउज़र गति की तुलना

ये दोनों ग्राफ़ एक ही डेटा पर अलग-अलग व्यू होते हैं. पहले ग्राफ़ में, के अनुसार तुलना करें, दूसरे ग्राफ़ में हम उपयोग की गई भाषा के अनुसार तुलना करते हैं. प्लीज़ ध्यान दें कि मैंने एक लॉगारिद्मिक टाइमस्केल चुना है. यह भी ज़रूरी है कि बेंचमार्क एक ही 16 मेगापिक्सल की टेस्ट इमेज और उसी होस्ट का इस्तेमाल कर रहे थे एक ब्राउज़र को छोड़कर, जो एक ही मशीन पर नहीं चल सकता.

इन ग्राफ़ का ज़्यादा विश्लेषण किए बिना, यह साफ़ है कि हमने अपने मूल परफ़ॉर्मेंस की समस्या: सभी WebAssembly मॉड्यूल ~500 मि॰से॰ या उससे कम समय में चलते हैं. यह शुरुआत में ही तय कर देता है कि WebAssembly में आपको अनुमान लगाने में मदद मिलेगी परफ़ॉर्मेंस. हम चाहे कोई भी भाषा चुनें, अलग-अलग ब्राउज़र के और भाषाएं बहुत कम हैं. सटीक होने के लिए: JavaScript का मानक विचलन सभी ब्राउज़र में ~400ms है, जबकि हमारे सभी सभी ब्राउज़र के WebAssembly मॉड्यूल की लंबाई 80 मि॰से॰ होनी चाहिए.

प्रयास

दूसरी मेट्रिक यह है कि कॉन्टेंट बनाने और उसे इंटिग्रेट करने के लिए हमें काफ़ी मेहनत करनी पड़ी हमारे WebAssembly मॉड्यूल को स्क्वॉश में बदल दिया गया. किसी संख्यात्मक मान को असाइन करना मुश्किल है तो मैं कोई ग्राफ़ नहीं बनाऊंगी, लेकिन कुछ चीज़ों की इच्छा करूंगी इन बातों के बारे में बताएं:

AssemblyScript बिना किसी रुकावट के था. यह आपको न केवल TypeScript का इस्तेमाल करने की अनुमति देता है, WebAssembly में लिखने के लिए, मेरे साथ काम करने वालों के लिए कोड की समीक्षा करना काफ़ी आसान हो जाता है. हालांकि, यह यह ग्लू-फ़्री WebAssembly मॉड्यूल बनाती है, जो कि बहुत छोटे और सटीक परफ़ॉर्मेंस. TypeScript नेटवर्क में मौजूद टूल, जैसे कि prettier और tslint, काम करने की संभावना है.

wasm-pack के साथ ज़ंग करना भी बहुत आसान है, लेकिन यह बेहतरीन है WebAssembly के बड़े प्रोजेक्ट बाइंडिंग थे और मेमोरी मैनेजमेंट के लिए की ज़रूरत नहीं है. प्रतिस्पर्धा के लक्ष्य हासिल करने के लिए, हमें हैपी पाथ से कुछ हटकर काम करना पड़ा फ़ाइल आकार.

C और Emscripten ने एक बहुत छोटा और बहुत अच्छा परफ़ॉर्म करने वाला WebAssembly मॉड्यूल बनाया है लेकिन बिना हिम्मत के, ग्लू कोड में जाकर इसे इतना छोटा कर लें कुल साइज़ (WebAssembly मॉड्यूल + ग्लू कोड) की ज़रूरत होती है किया जा सकता है.

नतीजा

इसलिए, अगर आपके पास JS हॉट पाथ है और आपको उसे बनाना है, तो आपको किस भाषा का इस्तेमाल करना चाहिए WebAssembly के साथ तेज़ या एक जैसे तरीके से काम करता है. हमेशा की तरह, प्रदर्शन के साथ ही नहीं, जवाब है: यह निर्भर करता है. हमने क्या भेजा?

तुलना करने वाला ग्राफ़

अलग-अलग भाषाओं के मॉड्यूल के साइज़ / परफ़ॉर्मेंस की तुलना में अंतर करना का इस्तेमाल किया, तो लगता है सबसे अच्छा विकल्प C या AssemblyScript है. हमने Rust को भेजने का फ़ैसला किया. यह लीजिए यह फ़ैसला लेने की कई वजहें हैं: Squoosh में अब तक सभी कोडेक शिप किए गए हैं उन्हें Emscripten का इस्तेमाल करके कंपाइल किया जाता है. हम पब्लिशर की मदद करने के लिए, WebAssembly नेटवर्क पर, प्रोडक्शन में किसी दूसरी भाषा का इस्तेमाल किया जाता है. AssemblyScript एक अच्छा विकल्प है, लेकिन यह प्रोजेक्ट काफ़ी युवा है और कंपाइलर, रस्ट कंपाइलर जितना मैच्योर नहीं है.

हालांकि, Rust और दूसरी भाषाओं के साइज़ के बीच फ़ाइल के साइज़ में अंतर स्कैटर ग्राफ़ में बहुत ज़्यादा दिखती है, लेकिन असल में यह कोई बड़ी बात नहीं है: 500B या 1.6 केबी लोड होने में लगने वाला समय, 2G से ज़्यादा होने के बावजूद लोड होने में 1/10 सेकंड से भी कम समय लगता है. और उम्मीद है कि Rust, मॉड्यूल के साइज़ के मामले में इस अंतर को जल्द ही कम कर देगा.

रनटाइम परफ़ॉर्मेंस के लिहाज़ से, सभी ब्राउज़र पर Rust का औसत इससे ज़्यादा है AssemblyScript. विशेष रूप से बड़े प्रोजेक्ट में, Rust की मैन्युअल कोड ऑप्टिमाइज़ेशन की ज़रूरत के बिना, ज़्यादा तेज़ी से कोड जनरेट कर सकते हैं. हालांकि, यह आपको उस चीज़ का उपयोग करने से नहीं रोकना चाहिए जिसमें आप सहज महसूस करते हैं.

बस इतना ही कहा जा रहा है: AssemblyScript एक बेहतरीन खोज है. इसकी मदद से, वेब पर इसकी मदद से, डेवलपर नई चीज़ें सीखे बिना WebAssembly मॉड्यूल बना सकते हैं भाषा. AssemblyScript की टीम ने काफ़ी रिस्पॉन्स दिया है और यह काम कर रही है हम टूलचेन को बेहतर बनाने पर काम कर रहे हैं. हम इस पर अपनी AssemblyScript भविष्य में इस्तेमाल करें.

अपडेट: Rust

इस लेख को पब्लिश करने के बाद, निक फ़िट्ज़गेरल्ड Rust टीम की ओर से फ़ाइल के साइज़ को ऑप्टिमाइज़ करने की जानकारी वाला सेक्शन. इसे फ़ॉलो कर रहे हैं नीचे दिए गए निर्देश देखें (खास तौर पर, लिंक टाइम ऑप्टिमाइज़ेशन और मैन्युअल पैनिक हैंडलिंग) से हमें “सामान्य” रस्ट कोड लिखने और Cargo (रस्ट का npm) का इस्तेमाल करते हैं. Rust मॉड्यूल खत्म हो गया है gzip के बाद 370B तक. ज़्यादा जानकारी के लिए, कृपया Squoosh पर मैंने जो पीआर खोला उसे देखें.

इस सफ़र में मदद करने के लिए, ऐशली विलियम्स, स्टीव क्लाबनिक, निक फ़िट्ज़गेराल्ड, और मैक्स ग्रे को विशेष धन्यवाद.