আপনার অ্যাপের জাভাস্ক্রিপ্টে একটি হট পাথকে WebAssembly দিয়ে প্রতিস্থাপন করা হচ্ছে

এটা ধারাবাহিকভাবে দ্রুত, ইয়ো

আমার পূর্ববর্তী নিবন্ধগুলিতে আমি কীভাবে WebAssembly আপনাকে C/C++ এর লাইব্রেরি ইকোসিস্টেম ওয়েবে আনতে অনুমতি দেয় সে সম্পর্কে কথা বলেছিলাম। একটি অ্যাপ যা C/C++ লাইব্রেরির ব্যাপক ব্যবহার করে তা হল squosh , আমাদের ওয়েব অ্যাপ যা আপনাকে C++ থেকে WebAssembly-তে সংকলিত বিভিন্ন কোডেক দিয়ে ছবি কম্প্রেস করতে দেয়।

WebAssembly হল একটি নিম্ন-স্তরের ভার্চুয়াল মেশিন যা .wasm ফাইলে সংরক্ষিত বাইটকোড চালায়। এই বাইট কোড দৃঢ়ভাবে টাইপ করা হয়েছে এবং এমনভাবে গঠন করা হয়েছে যে এটি জাভাস্ক্রিপ্টের চেয়ে অনেক দ্রুত হোস্ট সিস্টেমের জন্য কম্পাইল এবং অপ্টিমাইজ করা যায়। WebAssembly কোড চালানোর জন্য একটি পরিবেশ প্রদান করে যেটিতে স্যান্ডবক্সিং এবং এমবেডিং ছিল শুরু থেকেই।

আমার অভিজ্ঞতায়, ওয়েবে বেশিরভাগ পারফরম্যান্স সমস্যা বাধ্যতামূলক লেআউট এবং অত্যধিক পেইন্টের কারণে সৃষ্ট হয় তবে প্রতিবার এবং তারপরে একটি অ্যাপকে একটি গণনামূলকভাবে ব্যয়বহুল কাজ করতে হবে যা অনেক সময় নেয়। WebAssembly এখানে সাহায্য করতে পারেন.

গরম পথ

স্কুশ-এ আমরা একটি জাভাস্ক্রিপ্ট ফাংশন লিখেছি যা একটি ইমেজ বাফারকে 90 ডিগ্রির গুণিতক দ্বারা ঘোরায়। যদিও অফস্ক্রিন ক্যানভাস এর জন্য আদর্শ হবে, আমরা যে ব্রাউজারগুলিকে টার্গেট করছিলাম সেগুলি জুড়ে এটি সমর্থিত নয় এবং Chrome-এ একটু বগি

এই ফাংশনটি একটি ইনপুট চিত্রের প্রতিটি পিক্সেলের উপর পুনরাবৃত্তি করে এবং এটিকে আউটপুট চিত্রের একটি ভিন্ন অবস্থানে অনুলিপি করে ঘূর্ণন অর্জন করতে। একটি 4094px বাই 4096px চিত্রের জন্য (16 মেগাপিক্সেল) এর ভিতরের কোড ব্লকের 16 মিলিয়ন পুনরাবৃত্তির প্রয়োজন হবে, যাকে আমরা "হট পাথ" বলি। এত বড় সংখ্যক পুনরাবৃত্তি সত্ত্বেও, আমরা পরীক্ষিত তিনটি ব্রাউজারের মধ্যে দুটি 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;
    }
}

তবে একটি ব্রাউজার 8 সেকেন্ডের বেশি সময় নেয়। ব্রাউজারগুলি যেভাবে জাভাস্ক্রিপ্ট অপ্টিমাইজ করে তা সত্যিই জটিল এবং বিভিন্ন ইঞ্জিন বিভিন্ন জিনিসের জন্য অপ্টিমাইজ করে৷ কিছু কাঁচা সম্পাদনের জন্য অপ্টিমাইজ করে, কিছু DOM এর সাথে ইন্টারঅ্যাকশনের জন্য অপ্টিমাইজ করে৷ এই ক্ষেত্রে, আমরা একটি ব্রাউজারে একটি অপ্টিমাইজড পাথ হিট করেছি৷

অন্যদিকে WebAssembly সম্পূর্ণরূপে কাঁচা এক্সিকিউশন গতির চারপাশে নির্মিত। তাই যদি আমরা এই ধরনের কোডের জন্য ব্রাউজার জুড়ে দ্রুত, অনুমানযোগ্য পারফরম্যান্স চাই, WebAssembly সাহায্য করতে পারে।

অনুমানযোগ্য কর্মক্ষমতা জন্য WebAssembly

সাধারণভাবে, JavaScript এবং WebAssembly একই শীর্ষ কর্মক্ষমতা অর্জন করতে পারে। যাইহোক, জাভাস্ক্রিপ্টের জন্য এই কর্মক্ষমতা শুধুমাত্র "দ্রুত পথ" এ পৌঁছানো যেতে পারে এবং সেই "দ্রুত পথে" থাকা প্রায়ই কঠিন। WebAssembly অফার করে এমন একটি মূল সুবিধা হল অনুমানযোগ্য কর্মক্ষমতা, এমনকি ব্রাউজার জুড়ে। কঠোর টাইপিং এবং নিম্ন-স্তরের আর্কিটেকচার কম্পাইলারকে শক্তিশালী গ্যারান্টি দেওয়ার অনুমতি দেয় যাতে WebAssembly কোড শুধুমাত্র একবার অপ্টিমাইজ করতে হয় এবং সর্বদা "দ্রুত পথ" ব্যবহার করবে।

WebAssembly জন্য লেখা

পূর্বে আমরা C/C++ লাইব্রেরি নিয়েছিলাম এবং ওয়েবে তাদের কার্যকারিতা ব্যবহার করার জন্য WebAssembly-এ কম্পাইল করেছি। আমরা সত্যিই লাইব্রেরির কোড স্পর্শ করিনি, আমরা ব্রাউজার এবং লাইব্রেরির মধ্যে সেতু তৈরি করতে অল্প পরিমাণে C/C++ কোড লিখেছি। এবার আমাদের অনুপ্রেরণা ভিন্ন: আমরা WebAssembly কে মাথায় রেখে স্ক্র্যাচ থেকে কিছু লিখতে চাই যাতে আমরা WebAssembly এর সুবিধাগুলো ব্যবহার করতে পারি।

ওয়েব অ্যাসেম্বলি আর্কিটেকচার

WebAssembly- এর জন্য লেখার সময়, WebAssembly আসলে কী তা সম্পর্কে আরও কিছুটা বোঝা উপকারী।

WebAssembly.org উদ্ধৃত করতে:

যখন আপনি WebAssembly-এ C বা রাস্ট কোডের একটি অংশ কম্পাইল করেন, আপনি একটি .wasm ফাইল পাবেন যাতে একটি মডিউল ঘোষণা রয়েছে। এই ঘোষণায় মডিউলটি তার পরিবেশ থেকে প্রত্যাশিত "আমদানি" এর একটি তালিকা, রপ্তানির একটি তালিকা যা এই মডিউল হোস্টের কাছে উপলব্ধ করে (ফাংশন, ধ্রুবক, মেমরির অংশ) এবং অবশ্যই এর মধ্যে থাকা ফাংশনগুলির জন্য প্রকৃত বাইনারি নির্দেশাবলী। .

এমন কিছু যা আমি এটির দিকে নজর না দেওয়া পর্যন্ত বুঝতে পারিনি: যে স্ট্যাকটি WebAssembly কে একটি "স্ট্যাক-ভিত্তিক ভার্চুয়াল মেশিন" করে তোলে সেটি মেমরির অংশে সংরক্ষণ করা হয় না যা WebAssembly মডিউল ব্যবহার করে। স্ট্যাকটি সম্পূর্ণভাবে VM-অভ্যন্তরীণ এবং ওয়েব ডেভেলপারদের কাছে অ্যাক্সেসযোগ্য নয় (DevTools ব্যতীত)। এইভাবে WebAssembly মডিউলগুলি লেখা সম্ভব যেগুলির জন্য কোনও অতিরিক্ত মেমরির প্রয়োজন নেই এবং শুধুমাত্র VM-অভ্যন্তরীণ স্ট্যাক ব্যবহার করুন।

আমাদের ক্ষেত্রে আমাদের চিত্রের পিক্সেলগুলিতে নির্বিচারে অ্যাক্সেসের অনুমতি দেওয়ার জন্য এবং সেই চিত্রের একটি ঘোরানো সংস্করণ তৈরি করতে আমাদের কিছু অতিরিক্ত মেমরি ব্যবহার করতে হবে। এটি WebAssembly.Memory এর জন্য।

মেমরি ব্যবস্থাপনা

সাধারণত, আপনি একবার অতিরিক্ত মেমরি ব্যবহার করলে আপনি সেই মেমরিটিকে কোনো না কোনোভাবে পরিচালনা করার প্রয়োজন খুঁজে পাবেন। মেমরির কোন অংশ ব্যবহার করা হয়? কোনটি বিনামূল্যে? C-তে, উদাহরণস্বরূপ, আপনার malloc(n) ফাংশন রয়েছে যা n পরপর বাইটের মেমরি স্পেস খুঁজে পায়। এই ধরনের ফাংশনকে "বরাদ্দকারী"ও বলা হয়। অবশ্যই ব্যবহৃত বরাদ্দকারীর বাস্তবায়ন আপনার WebAssembly মডিউলে অন্তর্ভুক্ত করা আবশ্যক এবং আপনার ফাইলের আকার বৃদ্ধি করবে। এই মেমরি ম্যানেজমেন্ট ফাংশনগুলির এই আকার এবং কার্যকারিতা ব্যবহৃত অ্যালগরিদমের উপর নির্ভর করে বেশ উল্লেখযোগ্যভাবে পরিবর্তিত হতে পারে, যে কারণে অনেক ভাষা ("dmalloc", "emmalloc", "wee_alloc", ইত্যাদি) থেকে বেছে নেওয়ার জন্য একাধিক বাস্তবায়নের প্রস্তাব দেয়।

আমাদের ক্ষেত্রে আমরা WebAssembly মডিউল চালানোর আগে ইনপুট ইমেজের মাত্রা (এবং আউটপুট ইমেজের মাত্রা) জানি। এখানে আমরা একটি সুযোগ দেখেছি: ঐতিহ্যগতভাবে, আমরা একটি WebAssembly ফাংশনে একটি প্যারামিটার হিসাবে ইনপুট চিত্রের RGBA বাফারটি পাস করব এবং একটি রিটার্ন মান হিসাবে ঘোরানো চিত্রটি ফিরিয়ে দেব। সেই রিটার্ন মান তৈরি করতে আমাদের বরাদ্দকারী ব্যবহার করতে হবে। কিন্তু যেহেতু আমরা জানি মোট মেমরির পরিমাণ (ইনপুট ইমেজের আকারের দ্বিগুণ, একবার ইনপুটের জন্য এবং একবার আউটপুটের জন্য), আমরা JavaScript ব্যবহার করে ইনপুট ইমেজটিকে WebAssembly মেমরিতে রাখতে পারি, 2nd তৈরি করতে WebAssembly মডিউল চালান, ঘোরানো চিত্র এবং তারপর ফলাফলটি পড়ার জন্য জাভাস্ক্রিপ্ট ব্যবহার করুন। আমরা কোনো মেমরি ব্যবস্থাপনা ব্যবহার না করেই পার পেয়ে যেতে পারি!

পছন্দের জন্য নষ্ট হয়ে গেছে

আপনি যদি আসল JavaScript ফাংশনটি দেখেন যা আমরা WebAssembly-fy করতে চাই, আপনি দেখতে পাবেন যে এটি একটি সম্পূর্ণরূপে গণনামূলক কোড যার কোনো জাভাস্ক্রিপ্ট-নির্দিষ্ট API নেই। যেমন এই কোডটি যেকোনো ভাষায় পোর্ট করার জন্য এটি মোটামুটি সোজা হওয়া উচিত। আমরা WebAssembly-এ কম্পাইল করা 3টি ভিন্ন ভাষা মূল্যায়ন করেছি: C/C++, Rust এবং AssemblyScript। প্রতিটি ভাষার জন্য আমাদের একমাত্র প্রশ্নের উত্তর দিতে হবে: মেমরি ম্যানেজমেন্ট ফাংশন ব্যবহার না করে আমরা কীভাবে কাঁচা মেমরি অ্যাক্সেস করব?

সি এবং এমস্ক্রিপ্টেন

Emscripten হল WebAssembly টার্গেটের জন্য একটি C কম্পাইলার। Emscripten-এর লক্ষ্য হল GCC বা ক্ল্যাং-এর মতো সুপরিচিত C কম্পাইলারগুলির জন্য ড্রপ-ইন প্রতিস্থাপন হিসাবে কাজ করা এবং বেশিরভাগই ফ্ল্যাগ সামঞ্জস্যপূর্ণ। এটি এমস্ক্রিপ্টেন-এর মিশনের একটি মূল অংশ কারণ এটি বিদ্যমান C এবং C++ কোডগুলিকে WebAssembly-এ সংকলন করা যতটা সম্ভব সহজ করতে চায়।

কাঁচা মেমরি অ্যাক্সেস করা C এর প্রকৃতির মধ্যে রয়েছে এবং সেই কারণেই পয়েন্টার বিদ্যমান:

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

এখানে আমরা 0x124 নম্বরটিকে সাইনবিহীন, 8-বিট পূর্ণসংখ্যা (বা বাইট) এ একটি পয়েন্টারে পরিণত করছি। এটি কার্যকরভাবে ptr ভেরিয়েবলকে মেমরি অ্যাড্রেস 0x124 থেকে শুরু করে একটি অ্যারেতে পরিণত করে, যা আমরা অন্য যেকোনো অ্যারের মতো ব্যবহার করতে পারি, যা আমাদের পড়ার এবং লেখার জন্য পৃথক বাইট অ্যাক্সেস করতে দেয়। আমাদের ক্ষেত্রে আমরা একটি চিত্রের একটি RGBA বাফার দেখছি যা আমরা ঘূর্ণন অর্জনের জন্য পুনরায় অর্ডার করতে চাই। একটি পিক্সেল সরানোর জন্য আমাদের আসলে একবারে পরপর 4 বাইট সরাতে হবে (প্রতিটি চ্যানেলের জন্য একটি বাইট: 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;
    }
}

সম্পূর্ণ জাভাস্ক্রিপ্ট ফাংশনটি সি-তে পোর্ট করার পরে, আমরা emcc দিয়ে C ফাইলটি কম্পাইল করতে পারি:

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

বরাবরের মতো, emscripten c.js নামে একটি আঠালো কোড ফাইল এবং c.wasm নামে একটি wasm মডিউল তৈরি করে। লক্ষ্য করুন যে wsm মডিউলটি শুধুমাত্র ~260 বাইট পর্যন্ত জিজিপ করে, যখন gzip-এর পরে আঠালো কোড প্রায় 3.5KB হয়। কিছু নাড়াচাড়া করার পরে, আমরা আঠালো কোডটি ছিঁড়ে ফেলতে এবং ভ্যানিলা এপিআইগুলির সাথে WebAssembly মডিউলগুলিকে ইনস্ট্যান্টিয়েট করতে সক্ষম হয়েছি। যতক্ষণ না আপনি C স্ট্যান্ডার্ড লাইব্রেরি থেকে কিছু ব্যবহার করছেন না ততক্ষণ পর্যন্ত এমস্ক্রিপ্টেনের সাথে এটি প্রায়ই সম্ভব।

মরিচা

মরিচা হল একটি নতুন, আধুনিক প্রোগ্রামিং ভাষা যার একটি সমৃদ্ধ টাইপ সিস্টেম, কোন রানটাইম নেই এবং একটি মালিকানা মডেল যা মেমরি-নিরাপত্তা এবং থ্রেড-নিরাপত্তার নিশ্চয়তা দেয়। জং একটি মূল বৈশিষ্ট্য হিসাবে WebAssembly সমর্থন করে এবং রাস্ট টিম WebAssembly ইকোসিস্টেমে অনেক চমৎকার টুলিং অবদান রেখেছে।

এই সরঞ্জামগুলির মধ্যে একটি হল wasm-pack , রাস্টওয়াসম ওয়ার্কিং গ্রুপ দ্বারা। wasm-pack আপনার কোড নেয় এবং এটিকে একটি ওয়েব-বন্ধুত্বপূর্ণ মডিউলে পরিণত করে যা ওয়েবপ্যাকের মতো বান্ডলারের সাথে বাক্সের বাইরে কাজ করে। wasm-pack একটি অত্যন্ত সুবিধাজনক অভিজ্ঞতা, কিন্তু বর্তমানে শুধুমাত্র মরিচা-এর জন্য কাজ করে। গ্রুপটি অন্যান্য WebAssembly-টার্গেটিং ভাষার জন্য সমর্থন যোগ করার কথা বিবেচনা করছে।

মরিচা-এ, স্লাইসগুলি হল অ্যারেগুলি যা সি-তে থাকে। এবং সি-তে যেমন, আমাদের স্লাইস তৈরি করতে হবে যা আমাদের শুরুর ঠিকানাগুলি ব্যবহার করে। এটি মেমরি সুরক্ষা মডেলের বিরুদ্ধে যায় যা রাস্ট প্রয়োগ করে, তাই আমাদের পথ পেতে আমাদের 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;
    }
}

ব্যবহার করে মরিচা ফাইল কম্পাইল করা হচ্ছে

$ wasm-pack build

আঠালো কোডের প্রায় 100 বাইট সহ একটি 7.6KB wasm মডিউল দেয় (উভয় জিজিপের পরে)।

সমাবেশ স্ক্রিপ্ট

এসেম্বলিস্ক্রিপ্ট একটি মোটামুটি তরুণ প্রজেক্ট যার লক্ষ্য হল একটি টাইপস্ক্রিপ্ট-টু-ওয়েবঅ্যাসেম্বলি কম্পাইলার। এটা মনে রাখা গুরুত্বপূর্ণ, যাইহোক, এটি শুধুমাত্র কোনো TypeScript ব্যবহার করবে না। অ্যাসেম্বলিস্ক্রিপ্ট টাইপস্ক্রিপ্টের মতো একই সিনট্যাক্স ব্যবহার করে কিন্তু তাদের নিজস্ব জন্য স্ট্যান্ডার্ড লাইব্রেরি স্যুইচ করে। তাদের স্ট্যান্ডার্ড লাইব্রেরি WebAssembly এর ক্ষমতার মডেল করে। তার মানে আপনি 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() ফাংশনের ছোট ধরনের সারফেস বিবেচনা করে, এই কোডটি অ্যাসেম্বলিস্ক্রিপ্টে পোর্ট করা মোটামুটি সহজ ছিল। ফাংশন load<T>(ptr: usize) এবং store<T>(ptr: usize, value: T) কাঁচা মেমরি অ্যাক্সেস করতে এসেম্বলিস্ক্রিপ্ট দ্বারা প্রদান করা হয়। আমাদের অ্যাসেম্বলিস্ক্রিপ্ট ফাইল কম্পাইল করতে, আমাদের শুধুমাত্র AssemblyScript/assemblyscript এনপিএম প্যাকেজ ইনস্টল করতে হবে এবং চালাতে হবে

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

এসেম্বলিস্ক্রিপ্ট আমাদের একটি ~300 বাইট ওয়াএসএম মডিউল এবং কোন আঠালো কোড প্রদান করবে। মডিউলটি শুধু ভ্যানিলা WebAssembly API-এর সাথে কাজ করে।

WebAssembly ফরেনসিক

2টি অন্যান্য ভাষার তুলনায় রাস্টের 7.6KB আশ্চর্যজনকভাবে বড়। WebAssembly ইকোসিস্টেমে কয়েকটি টুল রয়েছে যা আপনাকে আপনার WebAssembly ফাইলগুলিকে বিশ্লেষণ করতে সাহায্য করতে পারে (যে ভাষা দিয়েই তৈরি করা হয়েছে তা নির্বিশেষে) এবং কী চলছে তা আপনাকে জানাতে এবং আপনার পরিস্থিতির উন্নতি করতে সাহায্য করতে পারে৷

টুইগি

Twiggy হল Rust এর WebAssembly টিমের আরেকটি টুল যা একটি WebAssembly মডিউল থেকে একগুচ্ছ অন্তর্দৃষ্টিপূর্ণ ডেটা বের করে। টুলটি মরিচা-নির্দিষ্ট নয় এবং আপনাকে মডিউলের কল গ্রাফের মতো জিনিসগুলি পরিদর্শন করতে, অব্যবহৃত বা অপ্রয়োজনীয় বিভাগগুলি নির্ধারণ করতে এবং আপনার মডিউলের মোট ফাইলের আকারে কোন বিভাগগুলি অবদান রাখছে তা নির্ধারণ করতে দেয়৷ পরবর্তীটি টুইগির top কমান্ড দিয়ে করা যেতে পারে:

$ twiggy top rotate_bg.wasm
টুইগি ইনস্টলেশন স্ক্রিনশট

এই ক্ষেত্রে আমরা দেখতে পাচ্ছি যে আমাদের ফাইলের আকারের বেশিরভাগই বরাদ্দকারী থেকে আসে। এটি আশ্চর্যজনক ছিল যেহেতু আমাদের কোড গতিশীল বরাদ্দ ব্যবহার করছে না। আরেকটি বড় অবদানকারী ফ্যাক্টর হল একটি "ফাংশন নাম" উপধারা।

wasm-ফালা

wasm-strip হল WebAssembly Binary Toolkit বা সংক্ষেপে wabt-এর একটি টুল। এটিতে কয়েকটি সরঞ্জাম রয়েছে যা আপনাকে WebAssembly মডিউলগুলি পরিদর্শন এবং ম্যানিপুলেট করার অনুমতি দেয়। wasm2wat হল একটি disassembler যা একটি বাইনারি wasm মডিউলকে মানব-পাঠযোগ্য বিন্যাসে পরিণত করে। Wabt-এ wat2wasm ও রয়েছে যা আপনাকে সেই মানব-পাঠযোগ্য বিন্যাসটিকে একটি বাইনারি wasm মডিউলে ফিরিয়ে আনতে দেয়। যদিও আমরা আমাদের WebAssembly ফাইলগুলি পরিদর্শন করার জন্য এই দুটি পরিপূরক সরঞ্জাম ব্যবহার করেছি, আমরা wasm-strip কে সবচেয়ে দরকারী বলে মনে করেছি। wasm-strip একটি WebAssembly মডিউল থেকে অপ্রয়োজনীয় বিভাগ এবং মেটাডেটা সরিয়ে দেয়:

$ wasm-strip rotate_bg.wasm

এটি মরিচা মডিউলের ফাইলের আকার 7.5KB থেকে 6.6KB (gzip-এর পরে) কমিয়ে দেয়।

wasm-অপ্ট

wasm-opt হল Binaryen থেকে একটি টুল। এটি একটি WebAssembly মডিউল নেয় এবং এটি শুধুমাত্র বাইটকোডের উপর ভিত্তি করে আকার এবং কর্মক্ষমতা উভয়ের জন্য অপ্টিমাইজ করার চেষ্টা করে। Emscripten এর মত কিছু টুল ইতিমধ্যেই এই টুলটি চালায়, কিছু অন্যরা করে না। এই টুলগুলি ব্যবহার করে কিছু অতিরিক্ত বাইট চেষ্টা করা এবং সংরক্ষণ করা সাধারণত একটি ভাল ধারণা।

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

wasm-opt সাহায্যে আমরা gzip-এর পরে মোট 6.2KB ছাড়তে আরও মুষ্টিমেয় বাইট শেভ করতে পারি।

#![no_std]

কিছু পরামর্শ এবং গবেষণার পর, আমরা #![no_std] বৈশিষ্ট্যটি ব্যবহার করে, রাস্টের স্ট্যান্ডার্ড লাইব্রেরি ব্যবহার না করে আমাদের রাস্ট কোডটি পুনরায় লিখেছি। এটি আমাদের মডিউল থেকে বরাদ্দকারী কোডটি সরিয়ে, গতিশীল মেমরি বরাদ্দ সম্পূর্ণরূপে অক্ষম করে। এই মরিচা ফাইলের সাথে কম্পাইল করা হচ্ছে

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

wasm-opt , wasm-strip এবং gzip-এর পরে একটি 1.6KB wasm মডিউল দিয়েছে৷ যদিও এটি এখনও সি এবং অ্যাসেম্বলিস্ক্রিপ্ট দ্বারা উত্পন্ন মডিউলগুলির চেয়ে বড়, এটি হালকা ওজন হিসাবে বিবেচিত হওয়ার মতো যথেষ্ট ছোট।

কর্মক্ষমতা

আমরা একা ফাইলের আকারের উপর ভিত্তি করে সিদ্ধান্তে যাওয়ার আগে — আমরা ফাইলের আকার নয়, কর্মক্ষমতা অপ্টিমাইজ করার জন্য এই যাত্রায় গিয়েছিলাম। তাহলে কিভাবে আমরা কর্মক্ষমতা পরিমাপ করেছি এবং ফলাফল কি ছিল?

বেঞ্চমার্ক কিভাবে

WebAssembly একটি নিম্ন-স্তরের বাইটকোড ফর্ম্যাট হওয়া সত্ত্বেও, হোস্ট-নির্দিষ্ট মেশিন কোড তৈরি করার জন্য এটি এখনও একটি কম্পাইলারের মাধ্যমে পাঠাতে হবে। জাভাস্ক্রিপ্টের মতই, কম্পাইলার একাধিক পর্যায়ে কাজ করে। সহজভাবে বলা হয়েছে: প্রথম পর্যায়টি কম্পাইল করার ক্ষেত্রে অনেক দ্রুত কিন্তু ধীর কোড তৈরি করে। একবার মডিউলটি চলতে শুরু করলে, ব্রাউজার কোন অংশগুলি প্রায়শই ব্যবহার করা হয় তা পর্যবেক্ষণ করে এবং সেগুলিকে আরও অপ্টিমাইজিং কিন্তু ধীর কম্পাইলারের মাধ্যমে পাঠায়।

আমাদের ব্যবহারের ক্ষেত্রে আকর্ষণীয় যে একটি চিত্র ঘোরানোর জন্য কোডটি একবার ব্যবহার করা হবে, সম্ভবত দুবার। তাই বেশিরভাগ ক্ষেত্রেই আমরা কখনই অপ্টিমাইজিং কম্পাইলারের সুবিধা পাব না। বেঞ্চমার্ক করার সময় এটি মনে রাখা গুরুত্বপূর্ণ। আমাদের WebAssembly মডিউলগুলি 10,000 বার একটি লুপে চালানো অবাস্তব ফলাফল দেবে৷ বাস্তবসম্মত সংখ্যা পেতে, আমাদের একবার মডিউলটি চালানো উচিত এবং সেই একক রানের সংখ্যার উপর ভিত্তি করে সিদ্ধান্ত নেওয়া উচিত।

কর্মক্ষমতা তুলনা

প্রতি ভাষা গতির তুলনা
ব্রাউজার প্রতি গতি তুলনা

এই দুটি গ্রাফ একই ডেটাতে ভিন্ন দৃষ্টিভঙ্গি। প্রথম গ্রাফে আমরা প্রতি ব্রাউজারে তুলনা করি, দ্বিতীয় গ্রাফে আমরা ব্যবহৃত ভাষা প্রতি তুলনা করি। দয়া করে মনে রাখবেন যে আমি একটি লগারিদমিক টাইমস্কেল বেছে নিয়েছি। এটিও গুরুত্বপূর্ণ যে সমস্ত বেঞ্চমার্ক একই 16 মেগাপিক্সেল পরীক্ষার চিত্র এবং একই হোস্ট মেশিন ব্যবহার করছে, একটি ব্রাউজার ছাড়া, যা একই মেশিনে চালানো যাবে না।

এই গ্রাফগুলিকে খুব বেশি বিশ্লেষণ না করেই, এটা স্পষ্ট যে আমরা আমাদের মূল কার্যক্ষমতা সমস্যার সমাধান করেছি: সমস্ত WebAssembly মডিউল ~500ms বা তার কম সময়ে চলে৷ এটি নিশ্চিত করে যে আমরা শুরুতে কী রেখেছি: WebAssembly আপনাকে অনুমানযোগ্য কার্যক্ষমতা দেয়। আমরা কোন ভাষা বেছে নিই না কেন, ব্রাউজার এবং ভাষার মধ্যে পার্থক্য ন্যূনতম। সঠিকভাবে বলতে গেলে: সমস্ত ব্রাউজার জুড়ে জাভাস্ক্রিপ্টের স্ট্যান্ডার্ড বিচ্যুতি হল ~400ms, যখন সমস্ত ব্রাউজার জুড়ে আমাদের সমস্ত WebAssembly মডিউলের স্ট্যান্ডার্ড বিচ্যুতি হল ~80ms৷

প্রচেষ্টা

আরেকটি মেট্রিক হল আমাদের WebAssembly মডিউল তৈরি এবং squosh-এ সংহত করার জন্য আমাদের যে পরিমাণ প্রচেষ্টা করতে হয়েছিল। প্রচেষ্টার জন্য একটি সাংখ্যিক মান নির্ধারণ করা কঠিন, তাই আমি কোনও গ্রাফ তৈরি করব না তবে কয়েকটি জিনিস রয়েছে যা আমি উল্লেখ করতে চাই:

সমাবেশ স্ক্রিপ্ট ঘর্ষণহীন ছিল. এটি শুধু আপনাকে WebAssembly লেখার জন্য TypeScript ব্যবহার করার অনুমতি দেয় না, যা আমার সহকর্মীদের জন্য কোড-রিভিউকে খুব সহজ করে তোলে, কিন্তু এটি আঠালো-মুক্ত WebAssembly মডিউলও তৈরি করে যা শালীন কর্মক্ষমতা সহ খুব ছোট। TypeScript ইকোসিস্টেমের টুলিং, যেমন সুন্দর এবং tslint, সম্ভবত কাজ করবে।

wasm-pack এর সাথে সংমিশ্রণে মরিচাও অত্যন্ত সুবিধাজনক, তবে বড় WebAssembly প্রকল্পগুলিতে আরও বেশি করে বাইন্ডিং এবং মেমরি পরিচালনার প্রয়োজন। একটি প্রতিযোগিতামূলক ফাইলের আকার অর্জনের জন্য আমাদের সুখী-পথ থেকে কিছুটা বিচ্যুত হতে হয়েছিল।

C এবং Emscripten বাক্সের বাইরে একটি খুব ছোট এবং অত্যন্ত পারফরম্যান্সযুক্ত WebAssembly মডিউল তৈরি করেছে, কিন্তু আঠালো কোডে ঝাঁপিয়ে পড়ার সাহস ছাড়াই এবং এটিকে খালি প্রয়োজনে কমিয়ে আনার মোট আকার (WebAssembly মডিউল + আঠালো কোড) বেশ বড় হয়ে যায়।

উপসংহার

সুতরাং আপনার যদি JS হট পাথ থাকে এবং আপনি WebAssembly এর সাথে দ্রুত বা আরও বেশি সামঞ্জস্যপূর্ণ করতে চান তবে আপনার কোন ভাষা ব্যবহার করা উচিত। পারফরম্যান্স প্রশ্নগুলির সাথে সর্বদা হিসাবে, উত্তর হল: এটি নির্ভর করে। তাই আমরা কি জাহাজ?

তুলনা গ্রাফ

আমরা ব্যবহৃত বিভিন্ন ভাষার মডিউল আকার/পারফরম্যান্স ট্রেডঅফের সাথে তুলনা করে, সেরা পছন্দটি হয় C বা এসেম্বলিস্ক্রিপ্ট বলে মনে হয়। আমরা মরিচা শিপিং করার সিদ্ধান্ত নিয়েছে . এই সিদ্ধান্তের একাধিক কারণ রয়েছে: এখন পর্যন্ত Squosh-এ পাঠানো সমস্ত কোডেক Emscripten ব্যবহার করে সংকলিত হয়েছে। আমরা WebAssembly ইকোসিস্টেম সম্পর্কে আমাদের জ্ঞানকে প্রসারিত করতে এবং উৎপাদনে একটি ভিন্ন ভাষা ব্যবহার করতে চেয়েছিলাম। এসেম্বলিস্ক্রিপ্ট একটি শক্তিশালী বিকল্প, কিন্তু প্রকল্পটি তুলনামূলকভাবে তরুণ এবং কম্পাইলারটি রাস্ট কম্পাইলারের মতো পরিপক্ক নয়।

যদিও রাস্ট এবং অন্যান্য ভাষার আকারের মধ্যে ফাইলের আকারের পার্থক্যটি স্ক্যাটার গ্রাফে বেশ কঠোর দেখায়, বাস্তবে এটি এত বড় ব্যাপার নয়: 500B বা 1.6KB এমনকি 2G এর উপরে লোড করতে সেকেন্ডের 1/10তমেরও কম সময় লাগে। এবং মরিচা আশা করি শীঘ্রই মডিউল আকারের পরিপ্রেক্ষিতে ফাঁকটি বন্ধ করবে।

রানটাইম পারফরম্যান্সের পরিপ্রেক্ষিতে, এসেম্বলিস্ক্রিপ্টের তুলনায় ব্রাউজার জুড়ে রাস্টের দ্রুত গড় রয়েছে। বিশেষ করে বড় প্রকল্পগুলিতে ম্যানুয়াল কোড অপ্টিমাইজেশনের প্রয়োজন ছাড়াই মরিচা দ্রুত কোড তৈরি করতে পারে। কিন্তু এটি আপনাকে ব্যবহার করা থেকে বিরত রাখা উচিত নয় যা আপনি সবচেয়ে আরামদায়ক।

যে সব বলা হচ্ছে: এসেম্বলিস্ক্রিপ্ট একটি মহান আবিষ্কার হয়েছে. এটি ওয়েব ডেভেলপারদের একটি নতুন ভাষা শেখার ছাড়াই WebAssembly মডিউল তৈরি করতে দেয়। এসেম্বলিস্ক্রিপ্ট টিম অত্যন্ত প্রতিক্রিয়াশীল এবং সক্রিয়ভাবে তাদের টুলচেন উন্নত করার জন্য কাজ করছে। আমরা অবশ্যই ভবিষ্যতে অ্যাসেম্বলিস্ক্রিপ্টের উপর নজর রাখব।

আপডেট: মরিচা

এই নিবন্ধটি প্রকাশ করার পরে, রাস্ট দলের নিক ফিটজেরাল্ড আমাদেরকে তাদের চমৎকার রাস্ট ওয়াসম বইয়ের দিকে নির্দেশ করেছেন, যাতে ফাইলের আকার অপ্টিমাইজ করার একটি বিভাগ রয়েছে। সেখানে নির্দেশাবলী অনুসরণ করে (সবচেয়ে উল্লেখযোগ্যভাবে লিঙ্ক টাইম অপ্টিমাইজেশান এবং ম্যানুয়াল প্যানিক হ্যান্ডলিং সক্ষম করা) আমাদেরকে "স্বাভাবিক" মরিচা কোড লিখতে এবং ফাইলের আকার ফুলিয়ে না দিয়ে Cargo (মরিচা-এর npm ) ব্যবহারে ফিরে যেতে দেয়। মরিচা মডিউল জিজিপের পরে 370B দিয়ে শেষ হয়। বিশদ বিবরণের জন্য, অনুগ্রহ করে স্কুশ-এ আমি যে PR খুলেছিলাম তা একবার দেখুন।

অ্যাশলে উইলিয়ামস , স্টিভ ক্ল্যাবনিক , নিক ফিটজেরাল্ড এবং ম্যাক্স গ্রেকে এই যাত্রায় তাদের সমস্ত সাহায্যের জন্য বিশেষ ধন্যবাদ৷