ในงาน Chrome Dev Summit 2020 เราได้สาธิตการสนับสนุนการแก้ไขข้อบกพร่องของ Chrome สำหรับแอปพลิเคชัน WebAssembly บนเว็บเป็นครั้งแรก นับตั้งแต่นั้นมา ทีมได้ทุ่มเททำงานมากมายเพื่อให้ประสบการณ์ของนักพัฒนาซอฟต์แวร์สามารถปรับขนาดให้เหมาะกับแอปพลิเคชันขนาดใหญ่หรือแม้กระทั่งแอปพลิเคชันขนาดใหญ่ ในโพสต์นี้ เราจะแสดงลูกบิดที่เราเพิ่ม (หรือที่ใช้ในการทำงาน) ในเครื่องมือต่างๆ และวิธีใช้!
การแก้ไขข้อบกพร่องที่รองรับการปรับขนาด
มาเริ่มกันต่อจากครั้งที่แล้วในโพสต์ปี 2020 กัน นี่คือตัวอย่างที่เรากำลังพูดถึงในช่วงนั้น
#include <SDL2/SDL.h>
#include <complex>
int main() {
// Init SDL.
int width = 600, height = 600;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window;
SDL_Renderer* renderer;
SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
&renderer);
// Generate a palette with random colors.
enum { MAX_ITER_COUNT = 256 };
SDL_Color palette[MAX_ITER_COUNT];
srand(time(0));
for (int i = 0; i < MAX_ITER_COUNT; ++i) {
palette[i] = {
.r = (uint8_t)rand(),
.g = (uint8_t)rand(),
.b = (uint8_t)rand(),
.a = 255,
};
}
// Calculate and draw the Mandelbrot set.
std::complex<double> center(0.5, 0.5);
double scale = 4.0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::complex<double> point((double)x / width, (double)y / height);
std::complex<double> c = (point - center) * scale;
std::complex<double> z(0, 0);
int i = 0;
for (; i < MAX_ITER_COUNT - 1; i++) {
z = z * z + c;
if (abs(z) > 2.0)
break;
}
SDL_Color color = palette[i];
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderDrawPoint(renderer, x, y);
}
}
// Render everything we've drawn to the canvas.
SDL_RenderPresent(renderer);
// SDL_Quit();
}
ตัวอย่างนี้ยังคงเป็นตัวอย่างที่ค่อนข้างเล็ก และคุณไม่น่าจะพบปัญหาจริงใดๆ ที่คุณจะเห็นในแอปพลิเคชันขนาดใหญ่มาก แต่เรายังสามารถแสดงให้คุณเห็นว่าฟีเจอร์ใหม่ๆ คืออะไร ทั้งตั้งค่าและลองใช้ด้วยตนเองได้ง่ายๆ
ในโพสต์ล่าสุด เราได้พูดถึงวิธีคอมไพล์และแก้ไขข้อบกพร่องของตัวอย่างนี้ เราจะลองแบบนั้นอีกครั้ง แต่มาดู //performance// กัน
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH
คำสั่งนี้จะสร้างไบนารี Wasm ขนาด 3 MB และที่คุณอาจคาดไว้ว่าเป็นข้อมูลการแก้ไขข้อบกพร่อง คุณสามารถยืนยันได้ด้วยเครื่องมือ llvm-objdump
[1] ตัวอย่างเช่น
$ llvm-objdump -h mandelbrot.wasm
mandelbrot.wasm: file format wasm
Sections:
Idx Name Size VMA Type
0 TYPE 0000026f 00000000
1 IMPORT 00001f03 00000000
2 FUNCTION 0000043e 00000000
3 TABLE 00000007 00000000
4 MEMORY 00000007 00000000
5 GLOBAL 00000021 00000000
6 EXPORT 0000014a 00000000
7 ELEM 00000457 00000000
8 CODE 0009308a 00000000 TEXT
9 DATA 0000e4cc 00000000 DATA
10 name 00007e58 00000000
11 .debug_info 000bb1c9 00000000
12 .debug_loc 0009b407 00000000
13 .debug_ranges 0000ad90 00000000
14 .debug_abbrev 000136e8 00000000
15 .debug_line 000bb3ab 00000000
16 .debug_str 000209bd 00000000
ผลลัพธ์นี้แสดงให้เราเห็นส่วนทั้งหมดที่อยู่ในไฟล์ Wasm ที่สร้างขึ้น ส่วนใหญ่เป็นส่วน WebAssembly แบบมาตรฐาน และยังมีส่วนที่กำหนดเองอีกหลายส่วนซึ่งชื่อขึ้นต้นด้วย .debug_
นี่คือที่ที่ไบนารีมีข้อมูลการแก้ไขข้อบกพร่องของเรา หากเรารวมไฟล์ทุกขนาดแล้ว เราจะเห็นว่าข้อมูลการแก้ไขข้อบกพร่องประมาณ 2.3 MB จากไฟล์ 3MB หากเราtime
คำสั่ง emcc
ด้วย เราจะเห็นว่าในเครื่องของเราใช้เวลาราว 1.5 วินาทีในการเรียกใช้ ตัวเลขเหล่านี้เป็นเกณฑ์พื้นฐานเล็กๆ น้อยๆ แต่จำนวนเหล่านี้ก็อาจน้อยจนไม่มีใครสนใจ แต่ในแอปพลิเคชันจริง ไบนารีในการแก้ไขข้อบกพร่องอาจมีขนาดเป็น GB และใช้เวลาสร้างเพียงไม่กี่นาที
ข้ามไบนารี
เมื่อสร้างแอปพลิเคชัน Wasm ด้วย Emscripten ขั้นตอนบิลด์สุดท้ายคือเรียกใช้เครื่องมือเพิ่มประสิทธิภาพ Binaryen Branchen เป็นชุดเครื่องมือคอมไพเลอร์ที่ทั้งเพิ่มประสิทธิภาพและทำให้ไบนารี WebAssembly (เหมือน) ถูกต้องตามกฎหมาย การใช้งาน Branchen ที่เป็นส่วนหนึ่งของบิลด์มีราคาค่อนข้างแพง แต่จำเป็นต้องใช้ภายใต้เงื่อนไขบางอย่างเท่านั้น สำหรับบิลด์การแก้ไขข้อบกพร่อง เราสามารถเร่งเวลาบิลด์ให้เร็วขึ้นได้มากหากหลีกเลี่ยงความจำเป็นที่จะต้องผ่านไบนารี บัตรผ่านไบนารีที่จำเป็นซึ่งพบได้บ่อยที่สุดคือการทำให้ลายเซ็นของฟังก์ชันถูกต้องตามกฎหมาย ซึ่งมีค่าจำนวนเต็ม 64 บิต การเลือกใช้การผสานรวม WebAssembly BigInt โดยใช้ -sWASM_BIGINT
ช่วยให้เราหลีกเลี่ยงปัญหานี้ได้
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
เราได้ทำเครื่องหมาย -sERROR_ON_WASM_CHANGES_AFTER_LINK
เป็นมาตรการที่ดี ช่วยในการตรวจจับเมื่อไบนารีกำลังทำงานและเขียนไบนารีใหม่โดยไม่คาดคิด วิธีนี้ทำให้มั่นใจว่าเรายังคงอยู่บนเส้นทางที่รวดเร็ว
แม้ว่าตัวอย่างของเราค่อนข้างเล็ก แต่เรายังเห็นผลลัพธ์จากการข้าม Branchen อยู่ จากข้อมูลของ time
คำสั่งนี้ทำงานโดยใช้เวลาไม่ถึง 1 วินาที ทำให้เร็วกว่าเดิมครึ่งวินาที!
การปรับแต่งขั้นสูง
กำลังข้ามการสแกนไฟล์อินพุต
โดยปกติเมื่อลิงก์โปรเจ็กต์ Emscripten emcc
จะสแกนไฟล์ออบเจ็กต์อินพุตและไลบรารีทั้งหมด ทั้งนี้ก็เพื่อใช้การพึ่งพากันอย่างแม่นยำระหว่างฟังก์ชันไลบรารี JavaScript และสัญลักษณ์แบบเนทีฟในโปรแกรมของคุณ สำหรับโปรเจ็กต์ขนาดใหญ่ การสแกนไฟล์อินพุตเพิ่มเติม (โดยใช้ llvm-nm
) อาจเพิ่มเวลาการลิงก์ได้อย่างมาก
โดยสามารถเรียกใช้ด้วย -sREVERSE_DEPS=all
แทน ซึ่งจะบอก emcc
ให้รวมทรัพยากร Dependency ทั้งหมดที่เป็นไปได้ของฟังก์ชัน JavaScript วิธีนี้มีโอเวอร์เฮดของโค้ดขนาดเล็ก แต่เพิ่มความเร็วในการลิงก์ได้และเป็นประโยชน์ต่อบิลด์การแก้ไขข้อบกพร่อง
สำหรับโครงการที่มีขนาดเล็กอย่างตัวอย่างของเรา การเปลี่ยนแปลงนี้จะไม่ส่งผลใดๆ อย่างแท้จริง แต่หากคุณมีไฟล์ออบเจ็กต์หลายร้อยหรือหลายพันไฟล์ในโครงการของคุณ ก็อาจช่วยปรับปรุงเวลาในการลิงก์ได้อย่างมาก
ลบส่วน "ชื่อ"
ในโปรเจ็กต์ขนาดใหญ่ โดยเฉพาะโปรเจ็กต์ที่มีการใช้งานเทมเพลต C++ จำนวนมาก ส่วน "ชื่อ" ของ WebAssembly อาจมีขนาดใหญ่มาก ในตัวอย่างของเรา นี่เป็นเพียงส่วนเล็กๆ ของขนาดไฟล์โดยรวม (ดูเอาต์พุตของ llvm-objdump
ด้านบน) แต่ในบางกรณีอาจมีนัยสำคัญอย่างมาก หากส่วน “name” ของแอปพลิเคชันมีขนาดใหญ่มาก และข้อมูลการแก้ไขข้อบกพร่องของคนแคระเพียงพอสำหรับความต้องการในการแก้ไขข้อบกพร่อง การลบส่วน “ชื่อ” อาจมีประโยชน์ดังนี้
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
การดำเนินการนี้จะตัดส่วน "name" ของ WebAssembly ออกโดยยังคงเก็บส่วนการแก้ไขข้อบกพร่อง DWARF ไว้
แก้ไขข้อบกพร่องในการแยกตัว
ไบนารีที่มีข้อมูลการแก้ไขข้อบกพร่องจำนวนมากไม่เพียงกดดันเวลาสร้างเท่านั้น แต่ยังรวมถึงเวลาในการแก้ไขข้อบกพร่องด้วย โปรแกรมแก้ไขข้อบกพร่องต้องโหลดข้อมูลและต้องสร้างดัชนีสำหรับข้อมูลนั้นๆ เพื่อให้ตอบสนองต่อคำค้นหาได้อย่างรวดเร็ว เช่น "ตัวแปร x ในเครื่องประเภทใด"
การฟิชชันการแก้ไขข้อบกพร่องช่วยให้เราแบ่งข้อมูลการแก้ไขข้อบกพร่องของไบนารีเป็น 2 ส่วน ได้แก่ ส่วนแรกที่ยังคงอยู่ในไบนารี และอีกส่วนจะอยู่ในไฟล์ออบเจ็กต์ DWARF (.dwo
) แยกต่างหาก คุณเปิดใช้ได้โดยส่ง Flag -gsplit-dwarf
ไปยัง Emscripten:
$ emcc -sUSE_SDL=2 -g -gsplit-dwarf -gdwarf-5 -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
ด้านล่าง เราจะแสดงคำสั่งต่างๆ และไฟล์ที่สร้างขึ้นจากการคอมไพล์โดยไม่มีข้อมูลการแก้ไขข้อบกพร่อง พร้อมข้อมูลการแก้ไขข้อบกพร่อง และสุดท้ายเป็นทั้งข้อมูลการแก้ไขข้อบกพร่องและฟิชชันการแก้ไขข้อบกพร่อง
เมื่อแยกข้อมูล DWARF ข้อมูลการแก้ไขข้อบกพร่องส่วนหนึ่งจะอยู่ในไบนารี ส่วนข้อมูลส่วนใหญ่จะอยู่ในไฟล์ mandelbrot.dwo
(ตามภาพประกอบด้านบน)
สำหรับ mandelbrot
เรามีไฟล์ต้นฉบับเพียงไฟล์เดียว แต่โดยทั่วไปโครงการจะมีขนาดใหญ่กว่าและมีไฟล์มากกว่า 1 ไฟล์ การรวมการแก้ไขข้อบกพร่องจะสร้างไฟล์ .dwo
สําหรับทุกๆ ไฟล์ เพื่อให้โปรแกรมแก้ไขข้อบกพร่องรุ่นเบต้าปัจจุบัน (0.1.6.1615) สามารถโหลดข้อมูลการแก้ไขข้อบกพร่องแบบแยกนี้ได้ เราจำเป็นต้องรวมกลุ่มข้อมูลเหล่านี้ทั้งหมดเข้าเป็นแพ็กเกจ DWARF ที่เรียกกันว่า (.dwp
) ดังนี้
$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp
การสร้างแพ็กเกจ DWARF จากแต่ละออบเจ็กต์มีข้อดีตรงที่คุณจำเป็นต้องแสดงไฟล์เพิ่มเติมเพียง 1 ไฟล์เท่านั้น ขณะนี้เรากำลังดำเนินการโหลดออบเจ็กต์แต่ละรายการทั้งหมดในรุ่นต่อๆ ไป
DWARF 5 ทำอะไรได้บ้าง
คุณอาจสังเกตเห็นว่าเราแทรกแฟล็กอีกรายการหนึ่งไว้ในคำสั่ง emcc
ด้านบน นั่นคือ -gdwarf-5
การเปิดใช้สัญลักษณ์ DWARF เวอร์ชัน 5 ซึ่งปัจจุบันไม่ใช่ค่าเริ่มต้นเป็นอีกหนึ่งเคล็ดลับที่ช่วยให้เราเริ่มแก้ไขข้อบกพร่องได้เร็วขึ้น เมื่อใช้ไฟล์ดังกล่าว ข้อมูลบางอย่างจะเก็บไว้ในไบนารีหลักที่เวอร์ชัน 4 เริ่มต้นระบุไว้ กล่าวอย่างเจาะจงคือ เราระบุชุดไฟล์ต้นฉบับทั้งหมดได้จากไบนารีหลักเท่านั้น วิธีนี้ช่วยให้โปรแกรมแก้ไขข้อบกพร่องสามารถดำเนินการแบบพื้นฐาน เช่น แสดงโครงสร้างซอร์สทั้งหมดและการตั้งค่าเบรกพอยท์โดยไม่ต้องโหลดและแยกวิเคราะห์ข้อมูลสัญลักษณ์แบบเต็ม ซึ่งทำให้แก้ไขข้อบกพร่องด้วยสัญลักษณ์แบบแยกได้เร็วขึ้นมาก เราจึงใช้แฟล็กบรรทัดคำสั่ง -gsplit-dwarf
และ -gdwarf-5
ร่วมกันเสมอ
เรายังได้เข้าถึงฟีเจอร์อื่นที่มีประโยชน์ด้วยรูปแบบการแก้ไขข้อบกพร่อง DWARF5 โดยเริ่มใช้ดัชนีชื่อในข้อมูลการแก้ไขข้อบกพร่องที่จะสร้างขึ้นเมื่อส่งแฟล็ก -gpubnames
ดังนี้
$ emcc -sUSE_SDL=2 -g -gdwarf-5 -gsplit-dwarf -gpubnames -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
ในระหว่างเซสชันการแก้ไขข้อบกพร่อง การค้นหาสัญลักษณ์มักเกิดขึ้นด้วยการค้นหาเอนทิตีตามชื่อ เช่น เมื่อมองหาตัวแปรหรือประเภท ดัชนีชื่อช่วยเร่งการค้นหานี้ด้วยการชี้ไปที่หน่วยคอมไพล์ที่ระบุชื่อนั้นโดยตรง หากไม่มีดัชนีชื่อ คุณจะต้องค้นหาข้อมูลการแก้ไขข้อบกพร่องทั้งหมดอย่างละเอียดเพื่อหาหน่วยการคอมไพล์ที่ถูกต้องซึ่งกำหนดเอนทิตีที่มีชื่อที่เรากำลังมองหา
หากมีข้อสงสัย: ดูข้อมูลการแก้ไขข้อบกพร่อง
คุณใช้ llvm-dwarfdump
เพื่อดูข้อมูล DWARF ได้ มาลองกันเลย
llvm-dwarfdump mandelbrot.wasm
วิธีนี้ช่วยให้เราเห็นภาพรวมเกี่ยวกับ "คอมไพล์หน่วย" (เรียกโดยคร่าวๆ ว่าไฟล์ต้นฉบับ) ที่เรามีข้อมูลการแก้ไขข้อบกพร่อง ในตัวอย่างนี้ เรามีเฉพาะข้อมูลการแก้ไขข้อบกพร่องสำหรับ mandelbrot.cc
ข้อมูลทั่วไปจะช่วยให้เราทราบว่าเรามีหน่วยโครงกระดูก ซึ่งหมายความว่าเรามีข้อมูลไม่สมบูรณ์ในไฟล์นี้ และมีไฟล์ .dwo
แยกต่างหากที่มีข้อมูลการแก้ไขข้อบกพร่องที่เหลือ
คุณยังดูตารางอื่นๆ ในไฟล์นี้ได้ด้วย เช่น ในตารางบรรทัดซึ่งแสดงการแมปไบต์โค้ด Wasm กับบรรทัด C++ (ลองใช้ llvm-dwarfdump -debug-line
)
นอกจากนี้เรายังดูข้อมูลการแก้ไขข้อบกพร่องที่อยู่ในไฟล์ .dwo
อีกไฟล์ได้ด้วย โดยทำดังนี้
llvm-dwarfdump mandelbrot.dwo
TL;DR: ประโยชน์ของการใช้ฟิชชันการแก้ไขข้อบกพร่องคืออะไร
การแยกข้อมูลการแก้ไขข้อบกพร่องมีข้อดีหลายอย่างในกรณีที่ทำงานกับแอปพลิเคชันขนาดใหญ่ได้ ดังนี้
การลิงก์ที่เร็วขึ้น: Linker ไม่จำเป็นต้องแยกวิเคราะห์ข้อมูลการแก้ไขข้อบกพร่องทั้งหมดอีกต่อไป โดยปกติแล้ว Linker จะต้องแยกวิเคราะห์ข้อมูล DWARF ทั้งหมดที่อยู่ในไบนารี การนำข้อมูลการแก้ไขข้อบกพร่องออกเป็นไฟล์ใหญ่ๆ แยกออกมาเป็นไฟล์ต่างหาก จะทำให้ Linker จัดการไฟล์ไบนารีขนาดเล็กได้ ซึ่งส่งผลให้การลิงก์เร็วขึ้น (โดยเฉพาะอย่างยิ่งสำหรับแอปพลิเคชันขนาดใหญ่)
การแก้ไขข้อบกพร่องที่เร็วขึ้น: โปรแกรมแก้ไขข้อบกพร่องสามารถข้ามการแยกวิเคราะห์สัญลักษณ์เพิ่มเติมในไฟล์
.dwo
/.dwp
สำหรับการค้นหาสัญลักษณ์บางรายการ สำหรับการค้นหาบางรายการ (เช่น คำขอในการแมปบรรทัดของไฟล์ Wasm-to-C++) เราไม่จำเป็นต้องดูข้อมูลการแก้ไขข้อบกพร่องเพิ่มเติม วิธีนี้ช่วยให้เราประหยัดเวลาได้โดยไม่ต้องโหลดและแยกวิเคราะห์ข้อมูลการแก้ไขข้อบกพร่องเพิ่มเติม
1: หากไม่มี llvm-objdump
เวอร์ชันล่าสุดในระบบ แต่ใช้ emsdk
อยู่ คุณสามารถค้นหาได้ในไดเรกทอรี emsdk/upstream/bin
ดาวน์โหลดเวอร์ชันตัวอย่าง
ลองใช้ Chrome Canary, Dev หรือ เบต้า เป็นเบราว์เซอร์เริ่มต้นสำหรับการพัฒนา ช่องทางพรีวิวเหล่านี้จะทำให้คุณเข้าถึงฟีเจอร์ล่าสุดของเครื่องมือสำหรับนักพัฒนาเว็บ ทดสอบ API แพลตฟอร์มเว็บที่ล้ำสมัย และพบปัญหาในเว็บไซต์ก่อนผู้ใช้
ติดต่อทีม Chrome DevTools
ใช้ตัวเลือกต่อไปนี้เพื่อพูดคุยเกี่ยวกับฟีเจอร์ใหม่และการเปลี่ยนแปลงในโพสต์ หรืออื่นๆ ที่เกี่ยวข้องกับเครื่องมือสำหรับนักพัฒนาเว็บ
- ส่งข้อเสนอแนะหรือความคิดเห็นถึงเราทาง crbug.com
- รายงานปัญหาเกี่ยวกับเครื่องมือสำหรับนักพัฒนาเว็บโดยใช้ตัวเลือกเพิ่มเติม > ความช่วยเหลือ > รายงานปัญหาเกี่ยวกับเครื่องมือสำหรับนักพัฒนาเว็บในเครื่องมือสำหรับนักพัฒนาเว็บ
- ทวีตที่ @ChromeDevTools
- แสดงความคิดเห็นว่ามีอะไรใหม่ในวิดีโอ YouTube เครื่องมือสำหรับนักพัฒนาเว็บ หรือวิดีโอ YouTube สำหรับเครื่องมือสำหรับนักพัฒนาเว็บ