jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

How Browsers Work · Part 12 — Capstone 1: Debugging Performance Through Internals

Turn the foundational arc into a debugging superpower: read a DevTools Performance trace through processes, threads, the rendering pipeline and the event loop, then map symptoms to root causes.

Sau mười một phần nền tảng, bạn biết trình duyệt không phải hộp đen — nó là tiến trình, luồng, pipeline, và chính sách với những nút thắt có thể quan sát. Capstone đầu tiên này biến kiến thức đó thành siêu năng debug: khi trang chậm hay giật, bạn ngừng đoán và bắt đầu đọc bằng chứng. Phần 13–22 sẽ tiếp tục đào sâu từng tầng trước khi khép series bằng một capstone profiling end-to-end.

Quy trình luôn giống nhau:

1. Reproduce  →  2. Measure (DevTools)  →  3. Map to internals  →  4. Fix at the right layer
     ↑                                                              │
     └──────────────── verify improvement ──────────────────────────┘

Ta đi qua trace Performance, bảng triệu chứng→nguyên nhân gắn mọi phần trước, fix mà internals gợi ý, và tóm tắt một trang cả series.


1. Trước khi profile: biết “chậm” nghĩa là gì

Hiệu năng không phải một con số. Định nghĩa vấn đề user thấy trước:

SymptomLikely metricLiên kết phần
Blank screen too longLCP, First Contentful PaintParts 2, 4, 9
Tap feels laggyINP, long tasksParts 6, 7
Scroll stuttersFrame drops, layout/paint costPart 5
Content jumpsCLSParts 2, 5
Tab eats RAM over timeHeap growthPart 8
Console security errorsSOP/CORS/CSPPart 11

Tái hiện với throttling gần thiết bị và mạng của user: giảm CPU, chọn profile mạng phù hợp, rồi ghi lại chính xác cấu hình đã dùng. Bug vô hình trên máy dev mạnh có thể chi phối trải nghiệm trên Android tầm trung.

Ghi trace Performance khi thực hiện đúng hành động user — scroll, click, gõ — không phải nhìn trang idle.


2. Đọc panel Performance: luồng chính trước

Mở DevTools → Performance → ghi → dừng. Flame chart trên luồng Main là lăng kính chính.

Performance trace (conceptual)
──────────────────────────────────────────────────────────────
 Main thread  ████████████░░░░░░░░  Long Task (>50ms) ← delays input
              │ Scripting │ Layout │ Paint │ ...
              └─ yellow ──┘ purple ┘ green ┘

 Compositor   ░░████░░████░░████    Often runs in parallel (Part 5)

 Raster       ░░░░██░░░░██░░░░██    software/GPU raster work
──────────────────────────────────────────────────────────────
 Timeline     │ FCP │ LCP │ INP event │

Mã màu thường gặp trong Chromium (UI có thể đổi giữa các phiên bản):

ColorCategoryNội bộ
YellowScriptingJS parse/compile/execute (Part 6), event handlers (Part 7)
PurpleRenderingStyle recalc, layout, update layer tree (Part 5)
GreenPaintingRecord paint, raster (Part 5)
GraySystem / idleBrowser overhead, waiting

Long task — task chiếm luồng chính quá 50 ms — là manh mối cho INP tệ và scroll giật. Phóng to khối vàng dài: một hàm khổng lồ, chuỗi handler, hay layout bị JS ép?

Xem pane Summary dưới cùng: Scripting chi phối thì tối ưu JS; Rendering hay Painting chi phối thì tối ưu pipeline (Phần 5).


3. Style, Layout, Paint, Composite xuất hiện ở đâu

Phần 5–7 nối pipeline render với luồng chính và event loop. Trong trace, bạn thấy chúng dưới dạng slice có tên:

  • Recalculate Style — khớp selector và cascade; kích hoạt bởi toggle class, đổi DOM (Phần 4).
  • Layout / Update Layout Tree — reflow; tốn kém, hiệu ứng lan (Phần 5).
  • Paint / Paint Image / Rasterize Paint — ghi và raster hóa thao tác vẽ (Phần 5).
  • Commit/composite events — có thể xuất hiện ở track compositor/GPU tuỳ kiểu trace; animation transform/opacity đủ điều kiện có thể tiếp tục mà không chạy layout/paint trên luồng chính (Phần 5, 13).

Layout thrashing trông như xen kẽ vàng (JS) và tím (Layout) trong vòng lặp chặt — đọc/ghi/đọc thuộc tính hình học kinh điển:

// ❌ Forces layout every iteration (Part 5)
for (const el of elements) {
  el.style.width = container.offsetWidth + 'px'; // read forces layout, then write invalidates
}

// ✅ Batch reads, then batch writes
const width = container.offsetWidth;
for (const el of elements) {
  el.style.width = width + 'px';
}

Bật Rendering → Paint flashingLayer borders khi debug để thấy vùng repaint và phần tử nào có compositor layer riêng.


4. Triệu chứng → nguyên nhân → phần: bảng tra cứu

Khi có gì đau, ánh vào internals bạn đã học:

SymptomLikely root causeFix directionSeries part
Janky scrollLayout thrashing; blocking input listener; paint/raster không kịpBatch DOM reads/writes; passive listener khi không huỷ scroll; kiểm tra compositor/raster5, 13, 14
Slow tap / high INPLong tasks blocking main thread; heavy sync work in click handlerChia task và await scheduler.yield() khi được hỗ trợ; hoãn việc không critical7, 15, 16
Growing memory / tab crashDetached DOM nodes, closures holding references, unbounded cachesSo sánh heap snapshot; cắt retainer không còn cần; giới hạn cache8, 17
Slow first loadRender-blocking CSS/JS; no HTTP cache; huge bundleDefer/async scripts; cache headers; code-split (Part 9)2, 3, 4, 9
Layout shift (CLS)Images/iframes without dimensions; fonts without fallback metrics; late-injected bannerswidth/height or aspect-ratio; font-display; reserve space2, 5
White screen after navigationSlow TTFB; parser blocked by sync script; large CSSServer/cache (Part 9); defer; critical CSS inline sparingly2, 3, 9
CORS / blocked request errorsMissing Access-Control-Allow-Origin; preflight failureFix server headers; don’t mistake for network failure11
CSP violations in consoleInline script without nonce; third-party script not allowlistedTighten CSP gradually; add nonce/hash11
Worker not helpingStructured clone dữ liệu lớn; main thread vẫn làm layoutTransfer ArrayBuffer/ImageBitmap; chỉ chuyển compute không cần DOM19, 20

Bảng không đầy đủ — là giả thuyết khởi đầu bạn xác nhận trong trace. Một triệu chứng thường nhiều nguyên nhân; flame chart cho biết cái nào chi phối phiên này.


5. Internals thay đổi cách fix của bạn thế nào

Biết kiến trúc nghĩa là chọn đúng lớp — không phải mọi vấn đề đều là “dùng useMemo”.

Đưa việc phù hợp ra khỏi luồng chính. Parse CSV, crypto, xử lý bitmap — Web Worker có thể chạy JS đồng thời với luồng chính (Phần 20). Transfer những tài nguyên hỗ trợ như ArrayBuffer/ImageBitmap; object JS thông thường vẫn đi qua structured clone.

Ưu tiên animation đủ điều kiện chạy trên compositor. transformopacity thường tránh layout/paint sau khi nội dung đã được raster, nhưng promotion không phải hợp đồng và vẫn cần kiểm tra trace (Phần 13). Animation set left mỗi frame thường kích hoạt layout.

Cache đúng lớp. HTTP cache (Phần 9) cho asset tĩnh; cache JS trong bộ nhớ cho dữ liệu đã tính; không localStorage cho blob lớn (Phần 10) — đồng bộ và chặn luồng chính.

Chỉ tối ưu shape khi profiler chỉ đúng điểm nóng. Shape ổn định giúp inline cache của V8, nhưng đừng bẻ thiết kế ứng dụng chỉ vì một “mẹo JIT”. Nếu property access thật sự chi phối, khởi tạo field nhất quán và tránh thêm/xoá key liên tục trong vòng nóng (Phần 16).

Tôn trọng event loop. Microtask (Promise.then) được drain trước khi browser có cơ hội render tiếp (Phần 7) — chuỗi microtask dài có thể làm đói render. Chia việc thành task nhỏ; dùng scheduler.yield()/scheduler.postTask() khi được hỗ trợ và có fallback (Phần 15).

An toàn theo mặc định. Khi tính năng “chạy Postman nhưng không chạy trình duyệt,” nghĩ SOP/CORS (Phần 11) trước khi viết lại logic fetch.


6. Tóm tắt công cụ: bộ debug của bạn

ToolWhat it showsWhen to use
PerformanceFlame chart, long tasks, FCP/LCP markers, main vs compositorJank, INP, scripting vs rendering split
MemoryHeap snapshots, allocation timeline, detached nodesLeaks, retained objects (Part 8)
NetworkWaterfall, TTFB, cache status, render-blockingSlow load, caching issues (Part 9)
Rendering (drawer)Paint flashing, layer borders, FPS meterRepaint scope, compositor layers (Part 5)
Task ManagerPer-process CPU/RAM (tabs, iframes, GPU)Memory hogs, Site Isolation visibility (Part 1)
CoverageUnused JS/CSS bytesBundle bloat, chi phí parse/compile/style (Parts 3, 4, 6)
ApplicationCookies, storage, service workersPersistence & quota (Part 10)
Console / IssuesCSP, mixed content, CORS errorsSecurity misconfig (Part 11)

Dùng kết hợp: Network giải thích vì sao load chậm; Performance vì sao tương tác chậm; Memory vì sao tab phình.


7. Mental model một trang: chặng nền tảng 1–12

HOW THE BROWSER WORKS — consolidated map
══════════════════════════════════════════════════════════════════

 Part 1  PROCESSES          Browser (trusted) ↔ Renderer (sandboxed) ↔ GPU ↔ Network
         Site Isolation     Cross-site documents → renderer boundaries when policy applies

 Part 2  NAVIGATION         URL → DNS → TCP/TLS → HTTP → response bytes
 Part 3  HTML → DOM          Tokenizer → tree builder → DOM; script can block parsing
 Part 4  CSS → CSSOM         Parse → cascade → computed styles → render tree
 Part 5  RENDER PIPELINE     Style → Layout → Paint → Composite (layer promotion)

 Part 6  V8                  Bytecode → Ignition/Sparkplug → Maglev/TurboFan; Maps, ICs
 Part 7  EVENT LOOP           Tasks → microtasks → render steps; long tasks block input
 Part 8  MEMORY               Generational GC; retainers, leaks, heap snapshots

 Part 9  NETWORK              HTTP/2-3, cache headers, CDN, render-blocking resources
 Part 10 STORAGE              Cookies, localStorage, IndexedDB, Cache API, service workers

 Part 11 SECURITY             SOP → CORS opt-in → CSP → cookie flags → sandbox/isolation
 Part 12 DEBUG (capstone 1)   Measure → map symptoms → fix at the correct layer

 THE THREAD YOU FIGHT FOR
 ────────────────────────
 Main thread: JS + style + layout + paint (record)
 Compositor:  eligible scroll/transform/opacity paths, frame assembly
 Workers:     JS off main thread (no DOM)

 THE DEFAULT QUESTION
 ────────────────────
 "Which process/thread/stage owns the delay — and what evidence proves it?"

Ghim sơ đồ này trong đầu. Mọi bug hiệu năng là câu chuyện tranh chấp trên luồng cụ thể ở giai đoạn cụ thể.


Tóm tắt

  • Định nghĩa triệu chứng user thấy trước, tái hiện với throttle CPU/mạng, rồi ghi trace Performance trong lúc hành động.
  • Đọc flame chart luồng Main: vàng = scripting (Phần 6–7), tím = rendering (Phần 5), xanh = paint; long task (>50 ms) có thể giải thích input delay và main-thread jank.
  • Dùng bảng triệu chứng→nguyên nhân→phần làm giả thuyết, rồi xác nhận trong trace — layout thrashing, tài nguyên chặn render, leak, và header bảo mật mỗi cái có dấu hiệu riêng.
  • Fix ở đúng lớp: worker cho tính toán, animation đủ điều kiện compositor, HTTP cache cho load, shape ổn định ở điểm nóng V8, CSP/CORS cho lỗi bảo mật.
  • Performance + Memory + Network + Rendering + Task Manager cùng nhau phủ full stack từ Phần 1 tới Phần 11.

Bạn đã đi hết chặng nền tảng và có một quy trình debug thay cho phỏng đoán. Lần tới production chậm, mở DevTools, ghi một trace ngắn, đọc flame chart và kiểm chứng giả thuyết. Từ Phần 13, ta mở sâu compositor, scrolling, scheduling, V8, memory và process model trước khi ghép tất cả lại ở capstone cuối.

Phần tiếp theo: GPU & compositor — layer/property tree, tile, raster và Viz biến paint records thành khung hình như thế nào.