Dữ liệu & CPU/Branch prediction — vì sao xử lý mảng đã sắp xếp nhanh hơn
18/23
Bài 18 / 23~18 phútCPU hiện đạiMiễn phí lượt xem

Branch prediction — vì sao xử lý mảng đã sắp xếp nhanh hơn

CPU đoán hướng đi của câu if để giữ pipeline đầy. Đoán sai phải xả pipeline, rất đắt. Giải mã ví dụ kinh điển: cùng dữ liệu, mảng sắp xếp chạy nhanh gấp nhiều lần.

TL;DR: CPU không chờ biết kết quả điều kiện if mới chạy lệnh tiếp — nó đoán nhánh nào sẽ chạy và thực thi trước (speculative execution). Đoán đúng thì pipeline đầy, không mất chu kỳ. Đoán sai thì phải xả (flush) toàn bộ lệnh đã chạy nhầm và nạp lại từ đầu — phạt 10–20 chu kỳ. Mảng đã sắp xếp nhanh hơn mảng chưa sắp xếp không phải vì ít phép tính hơn, mà vì điều kiện lọc tạo thành pattern dễ đoán: predictor đúng gần 100%. Mảng ngẫu nhiên thì 50/50 — đoán sai liên tục, pipeline xả đi nạp lại liên tục, chậm gấp nhiều lần.

Năm 2012, một câu hỏi trên Stack Overflow thu hút hơn 27.000 upvote — con số kỷ lục lúc bấy giờ. Người đặt câu hỏi để ý thấy điều kỳ lạ: cùng một vòng lặp Java cộng dồn các phần tử lớn hơn 128 trong một mảng 32.768 phần tử, nhưng mảng đã sắp xếp chạy nhanh hơn mảng chưa sắp xếp đến 6 lần — dù cả hai có đúng cùng số phần tử, cùng điều kiện lọc, và cùng số phép tính.

// Cung mot vong lap, cung ket qua, nhung toc do khac nhau rất lon
int sum = 0;
for (int value : data) {
    if (value >= 128) {
        sum += value;
    }
}

Nếu mảng data đã sắp xếp tăng dần, vòng lặp mất khoảng 3 mili-giây. Nếu data là ngẫu nhiên: khoảng 18 mili-giây — chậm hơn 6 lần. Cùng dữ liệu, cùng thuật toán. Vì sao?

Bài này giải thích cơ chế branch prediction — cách CPU đoán nhánh điều kiện để giữ pipeline luôn đầy — và vì sao pattern trong dữ liệu lại quyết định trực tiếp đến hiệu năng chương trình của bạn.

1. Analogy — người gác chắn tàu

Hình dung một trạm gác chắn đường sắt có hai ngả rẽ: hướng Bắc và hướng Nam. Tàu đến, người gác cần mở barrier cho tàu đi đúng hướng. Nếu phải chờ tàu đến đầu ghi mới quyết định, tàu sẽ phải giảm tốc rồi dừng lại — mất thời gian.

Người gác thông minh quan sát lịch sử: tàu buổi sáng 9 giờ luôn đi hướng Bắc, tàu 14 giờ luôn hướng Nam. Anh ta mở sẵn barrier theo dự đoán đó. Khi đoán đúng, tàu chạy thẳng không dừng. Khi đoán sai, tàu phải phanh gấp, lùi lại, đổi hướng — tốn nhiều thời gian hơn nhiều so với nếu cứ chờ từ đầu.

CPU branch predictor là người gác chắn đó.

Gác chắn tàuCPU branch predictor
Hai ngả rẽ: Bắc / NamHai nhánh: true / false của if
Mở barrier theo dự đoánNạp và chạy lệnh của nhánh dự đoán (speculative execution)
Đoán đúng → tàu qua không dừngMisprediction = 0: pipeline đầy
Đoán sai → tàu lùi, đổi hướngMisprediction: flush pipeline, nạp lại từ nhánh đúng
Học lịch sử giờ tàuPredictor học pattern nhánh qua lịch sử các lần trước
💡 Cách nhớ

Branch predictor học lịch sử như người gác chắn học giờ tàu. Nhánh dễ đoán (tàu giờ cố định) → predictor gần như không bao giờ sai. Nhánh ngẫu nhiên 50/50 (tàu đến bất kỳ hướng nào) → đoán sai liên tục, xả đi nạp lại liên tục.

2. Vì sao CPU cần đoán nhánh

Bài 02 đã mô tả pipeline 5 tầng: Fetch → Decode → Execute → Memory → Writeback. Mỗi tầng xử lý một lệnh khác nhau trong cùng một chu kỳ clock, nên ideally CPU hoàn thành một lệnh mỗi chu kỳ dù mỗi lệnh mất 5 tầng.

Vấn đề xảy ra khi gặp lệnh if:

Chu ky 1: Fetch lenh IF
Chu ky 2: Decode lenh IF | Fetch lenh ??
Chu ky 3: Execute (tinh dieu kien) | Decode ?? | Fetch ???

Ở chu kỳ 2, CPU cần fetch lệnh tiếp theo — nhưng lệnh tiếp theo là gì? Phụ thuộc vào điều kiện if, mà điều kiện đó chưa được tính xong cho đến chu kỳ 3. CPU có hai lựa chọn:

  1. Chờ (stall): dừng pipeline tại chỗ đến khi biết kết quả điều kiện. Phí 2–3 chu kỳ mỗi lần gặp branch.
  2. Đoán (speculative execution): chọn một nhánh, fetch và bắt đầu chạy lệnh của nhánh đó ngay. Nếu đúng: không mất gì. Nếu sai: xả pipeline và nạp lại từ nhánh đúng.

CPU hiện đại chọn giải pháp 2 vì giải pháp 1 quá đắt: một vòng lặp có if bên trong, chạy hàng triệu lần, sẽ dừng pipeline hàng triệu lần nếu không có branch prediction.

flowchart TD
  A["Fetch lenh IF"] --> B["Giai ma dieu kien"]
  B --> C{"Branch predictor\ndoan nhanh nao?"}
  C -->|"Doan TRUE"| D["Speculative: chay lenh nhanh TRUE"]
  C -->|"Doan FALSE"| E["Speculative: chay lenh nhanh FALSE"]
  D --> F{"Ket qua thuc te?"}
  E --> F
  F -->|"Doan dung"| G["Pipeline tiep tuc\n(khong phat)"]
  F -->|"Doan sai"| H["FLUSH pipeline\nNap lai tu nhanh dung\nPhat 10-20 chu ky"]

3. Branch predictor học pattern như thế nào

Branch predictor không đoán ngẫu nhiên — nó học từ lịch sử các lần thực thi trước. Cơ chế phổ biến nhất là bộ đếm bão hòa 2-bit (2-bit saturating counter).

Mỗi branch address có một bộ đếm 2-bit, nhận giá trị từ 0 đến 3:

Giá trịTrạng tháiDự đoán
3Strongly TakenNhánh TRUE
2Weakly TakenNhánh TRUE
1Weakly Not TakenNhánh FALSE
0Strongly Not TakenNhánh FALSE

Mỗi lần branch thực sự đi nhánh TRUE, bộ đếm tăng 1 (tối đa 3). Mỗi lần đi nhánh FALSE, bộ đếm giảm 1 (tối thiểu 0). Dự đoán dựa trên bit cao nhất: bằng 2 hoặc 3 thì đoán TRUE, bằng 0 hoặc 1 thì đoán FALSE.

Ưu điểm của bộ đếm 2-bit: một lần đoán sai không lập tức đảo dự đoán. Phải sai hai lần liên tiếp mới chuyển state. Điều này giúp xử lý tốt pattern như vòng lặp: cuối vòng lặp luôn đi false (thoát), nhưng 99 lần trước đó là true (tiếp tục) — bộ đếm đủ bền để không bị nhiễu bởi một lần thoát hiếm gặp.

CPU hiện đại phức tạp hơn nhiều: dùng Branch Target Buffer (BTB) để nhớ địa chỉ đích, global history register để nhận biết pattern dài, và các thuật toán như TAGE predictor có thể nhớ pattern lịch sử hàng chục bước. Intel Skylake và AMD Zen 3 có tỉ lệ dự đoán đúng trên 99% với code thực tế.

4. Giải mã ví dụ kinh điển

Bây giờ hãy quay lại câu hỏi Stack Overflow. Điều kiện là value >= 128.

Trường hợp mảng ngẫu nhiên:

Các phần tử trải đều từ 0 đến 255. Khoảng 50% phần tử nhỏ hơn 128 (điều kiện false), 50% từ 128 trở lên (điều kiện true). Thứ tự hoàn toàn ngẫu nhiên:

false, true, false, false, true, true, false, true, ...

Branch predictor không tìm thấy pattern. Bộ đếm 2-bit dao động liên tục quanh trạng thái "weakly taken / weakly not taken" — đoán sai xấp xỉ 50% lần. Mỗi lần sai: flush pipeline, phạt khoảng 15 chu kỳ. Với 32.768 phần tử, khoảng 16.000 lần misprediction.

Trường hợp mảng đã sắp xếp tăng dần:

0, 1, 2, ..., 127, 128, 129, ..., 255

Nửa đầu vòng lặp, điều kiện value >= 128 luôn false. Branch predictor nhanh chóng đạt trạng thái "Strongly Not Taken" và duy trì ở đó — đoán đúng gần 100%.

Đến phần tử 128, điều kiện lật sang true. Predictor sai đúng một lần (lần lật), rồi nhanh chóng học sang "Strongly Taken" cho nửa sau. Tổng misprediction: khoảng 2 lần — một lần lật sang true, một lần lặp lại sau khi lật.

// Minh hoa pattern nhanh: mang da sap xep tang dan
// [0, 1, 2, ..., 127, 128, 129, ..., 255]
//
// Phan truoc nguong: dieu kien luon false -> predictor "Strongly Not Taken"
// Sat nguong 128: dieu kien lat sang true -> 1 misprediction
// Phan sau nguong: dieu kien luon true -> predictor "Strongly Taken"
// => Tong misprediction: ~2 lan tren 32768 phan tu

int sum = 0;
for (int value : sortedData) {   // sortedData = 0..255 tang dan
    if (value >= 128) {
        sum += value;
    }
}
// Thoi gian: ~3ms (pipeline luon day)

So sánh với mảng ngẫu nhiên:

// Mang ngau nhien: dieu kien true/false xen ke khong co pattern
// Predictor dao dong, sai ~50% = ~16000 misprediction
// Moi misprediction: flush + nap lai = ~15 chu ky
// => ~240000 chu ky phi vi misprediction (ngoai tinh toan chinh)

int sum = 0;
for (int value : randomData) {   // randomData = 0..255 ngau nhien
    if (value >= 128) {
        sum += value;
    }
}
// Thoi gian: ~18ms (~6x cham hon sortedData)

Hiệu năng khác nhau 6 lần không phải do thuật toán, không phải do trình biên dịch, không phải do bộ nhớ cache — mà hoàn toàn do branch misprediction.

⚠️ Chú ý với JVM và JIT

Trên JVM, JIT compiler đôi khi tự tối ưu vòng lặp nếu nó phát hiện pattern trong quá trình warmup. Kết quả benchmark có thể thay đổi giữa các lần chạy hoặc giữa các phiên bản JVM. Để đo chính xác, cần warmup đủ (ít nhất 10.000 lần lặp) và dùng JMH — bài 04 sẽ hướng dẫn chi tiết.

5. Đào sâu (tuỳ chọn)

📚 Đào sâu (tuỳ chọn)

Speculative execution và lỗ hổng Spectre (2018)

Branch prediction là nền tảng của speculative execution: CPU chạy lệnh trước khi biết có nên chạy hay không. Năm 2018, các nhà nghiên cứu phát hiện rằng khi CPU thực thi đầu cơ (speculate) sai nhánh, nó đã để lại dấu vết trong cache — và attacker có thể đọc dấu vết đó để lấy dữ liệu nhạy cảm từ vùng nhớ khác tiến trình. Đây là gốc của lỗ hổng Spectre (CVE-2017-5753). Bài 06 sẽ mổ xẻ chi tiết cơ chế tấn công và tại sao nó khó vá triệt để đến vậy.

Các loại branch predictor

  • 2-bit saturating counter: cơ chế cơ bản mô tả trong bài — 4 trạng thái, chịu được 1 ngoại lệ mà không đảo dự đoán.
  • Branch Target Buffer (BTB): bảng nhớ địa chỉ đích của các branch đã thấy — giúp CPU biết không chỉ "đi nhánh nào" mà còn "nhảy đến địa chỉ nào".
  • TAGE predictor (Tagged Geometric History Length): predictor hiện đại nhất, sử dụng nhiều bảng lịch sử với chiều dài lịch sử tăng theo cấp số nhân. Intel Skylake, AMD Zen và Apple M-series đều dùng kiến trúc gần với TAGE — đạt tỉ lệ đúng trên 99% với workload thực tế.

Liên hệ JIT compiler

JVM JIT compiler đọc profile thực thi (số lần nhánh true/false) để ra quyết định biên dịch. Nếu một nhánh hiếm khi xảy ra, JIT có thể đặt nó vào "slow path" và tối ưu "fast path". Kết quả là JIT và branch predictor cùng làm việc ở hai tầng khác nhau — JIT ở mức lệnh máy, predictor ở mức pipeline. Xem thêm trong course java-internals khi bạn đào sâu vào JVM.

6. Áp dụng vào code của bạn

Biết cơ chế branch prediction, bạn có thể viết code thân thiện hơn với predictor — nhưng phải đo trước, tối ưu sau.

6.1 Sắp xếp dữ liệu để nhánh dễ đoán

Khi hợp lý về nghiệp vụ, sắp xếp dữ liệu trước khi vòng lặp có lọc điều kiện. Pattern sau khi sắp xếp thường là một đoạn false rồi một đoạn true (hoặc ngược lại) — predictor học ngay lập tức.

// Co the cai thien neu vong lap chay nhieu lan voi cung dataset
Arrays.sort(data);   // O(n log n) mot lan
long sum = 0;
for (int value : data) {
    if (value >= 128) sum += value;
}
// Predictor chi sai ~2 lan tren toan bo mang

Lưu ý: nếu vòng lặp chỉ chạy một lần, chi phí sắp xếp O(n log n) có thể lớn hơn tiết kiệm từ branch prediction. Đo trước.

6.2 Viết code branchless khi nhánh khó đoán

Khi không thể sắp xếp dữ liệu và điều kiện genuinely 50/50, có thể loại bỏ hoàn toàn nhánh bằng phép toán số học.

// Co branch (kho doan voi du lieu ngau nhien):
if (value >= 128) {
    sum += value;
}

// Branchless tuong duong (khong co if, khong co misprediction):
int mask = -(value >= 128 ? 1 : 0);   // mask = -1 (0xFFFF...) hoac 0
sum += value & mask;                   // gia tri giu nguyen neu mask=-1, = 0 neu mask=0

Biểu thức -(condition ? 1 : 0) trong Java: khi condition đúng ra -1 (tất cả bit = 1), khi sai ra 0. AND với mask này giữ nguyên value hoặc cho về 0 — không cần branch.

Nhiều compiler hiện đại (GCC, Clang, javac + JIT) tự phát hiện pattern này và tạo ra lệnh cmov (conditional move) thay vì jump — branchless mà code vẫn đọc dễ.

// Phien ban dung bitmask ro rang hon (pho bien trong thu vien xu ly hieu nang cao):
// Gia su value la int duong, nguong la 128
int aboveThreshold = (value - 128) >> 31;   // -1 neu value < 128, 0 neu value >= 128
int addend = value & ~aboveThreshold;        // 0 neu duoi nguong, value neu tren nguong
sum += addend;

6.3 Khi nào KHÔNG nên tối ưu

Đây là điểm quan trọng nhất trong bài: đừng tối ưu mù.

// SAI: toi uu truoc khi do
// Code kho doc, kho debug, kho test
int mask = -(value >= 128 ? 1 : 0);
sum += value & mask;

// DUNG: giu code ro rang truoc
// Chi chuyen sang branchless neu profiler cho thay day la hotspot
if (value >= 128) {
    sum += value;
}

Phần lớn branch trong code thực tế dễ đoán: kiểm tra null thường không null, kiểm tra lỗi thường không có lỗi, vòng lặp tiếp tục thường đúng nhiều hơn sai. Branch predictor hiện đại xử lý những trường hợp này rất tốt — bạn không cần làm gì.

Chỉ cân nhắc tối ưu khi:

  1. Profiler chỉ ra hàm / vòng lặp cụ thể chiếm phần lớn runtime.
  2. Benchmark đo được misprediction rate cao (dùng perf stat trên Linux hoặc Intel VTune).
  3. Thay đổi cải thiện số liệu đo được, không phải "trực giác" nhanh hơn.
⚠️ Đừng hi sinh tính dễ đọc

Code branchless đúng là nhanh hơn khi branch genuinely khó đoán và là hotspot. Nhưng nó khó đọc, khó debug, và khó review. Ưu tiên: đo → tìm hotspot → tối ưu chỗ cần → đo lại để confirm. Đừng branchless toàn bộ codebase vì "nghe có vẻ nhanh hơn".

7. Liên hệ các bài khác

  • Bài 02 — Pipeline và hazard: branch misprediction là dạng control hazard nghiêm trọng nhất. Bài 02 giải thích tại sao pipeline cần được "xả" khi đoán sai và chi phí flush tính như thế nào.
  • Bài 04 — Đừng đoán, hãy đo: kỹ năng benchmark đúng — cách warmup JVM, tránh dead code elimination, và đọc output perf stat để thấy misprediction counter trực tiếp.
  • Bài 05 — Out-of-order execution và SIMD: CPU hiện đại không chỉ đoán nhánh mà còn sắp xếp lại thứ tự lệnh để tránh stall — out-of-order execution là bước tiếp theo sau pipeline.
  • Bài 06 — Spectre và Meltdown: speculative execution (hệ quả trực tiếp của branch prediction) là gốc rễ của Spectre — lỗ hổng ảnh hưởng hàng tỉ chip. Bài 06 liên kết cơ chế hardware với bảo mật.

8. Tóm tắt

  • CPU đoán nhánh (if/else/vòng lặp) để giữ pipeline luôn đầy — không phải chờ biết kết quả điều kiện mới chạy lệnh tiếp.
  • Đoán đúng: pipeline đầy, không mất chu kỳ. Đoán sai (misprediction): phải flush toàn bộ lệnh đang chạy sai nhánh, nạp lại từ nhánh đúng — phạt 10–20 chu kỳ.
  • Branch predictor dùng lịch sử nhánh (bộ đếm 2-bit, BTB, TAGE) để học pattern. Nhánh luôn true hoặc luôn false → dễ đoán. Nhánh ngẫu nhiên 50/50 → misprediction gần 50%.
  • Ví dụ kinh điển: mảng đã sắp xếp tạo pattern điều kiện rõ ràng → predictor đúng gần 100% → nhanh gấp 6 lần so với mảng ngẫu nhiên cùng nội dung.
  • Để viết code branch-friendly: sắp xếp dữ liệu khi hợp lý, hoặc dùng kỹ thuật branchless (bitmask, conditional move) khi nhánh genuinely khó đoán.
  • Đo trước, tối ưu sau: phần lớn branch dễ đoán và không cần can thiệp. Chỉ tối ưu khi profiler chỉ ra hotspot cụ thể.
  • Speculative execution — nền tảng của branch prediction — cũng là gốc của lỗ hổng Spectre (2018). Bài 06 sẽ mổ xẻ liên kết này.

9. Tự kiểm tra

Tự kiểm tra
Q1
CPU cần đoán nhánh vì lý do gì? Nếu không đoán, chi phí thay thế là gì?
CPU phải fetch lệnh tiếp theo trong khi đang decode lệnh if — nhưng chưa biết nhánh nào sẽ chạy. Nếu không đoán, CPU phải stall pipeline (dừng fetch) cho đến khi tính xong điều kiện — mất 2–3 chu kỳ mỗi branch. Với vòng lặp có if bên trong chạy hàng triệu lần, stall mỗi lần là thảm họa hiệu năng. Branch prediction cho phép pipeline đầy gần như mọi lúc — chỉ phạt khi đoán sai.
Q2
Chi phí của một branch misprediction là gì? Vì sao con số đó phụ thuộc vào độ sâu pipeline?
Khi đoán sai, CPU phải flush toàn bộ lệnh đang ở trong pipeline (đã fetch và decode nhưng chưa commit kết quả) và nạp lại từ nhánh đúng. Chi phí bằng số tầng pipeline đã bị "ô nhiễm" bởi lệnh sai nhánh — thường 10–20 chu kỳ trên CPU hiện đại. Pipeline càng sâu (càng nhiều tầng), càng nhiều lệnh phải xả, chi phí càng cao. Đây là lý do Pentium 4 với pipeline 31 tầng bị chỉ trích nặng về branch misprediction penalty — nó phải xả nhiều hơn.
Q3
Giải thích vì sao mảng đã sắp xếp chạy nhanh hơn mảng ngẫu nhiên trong ví dụ lọc phần tử vượt ngưỡng.
Mảng đã sắp xếp tạo ra pattern điều kiện có thể học được: phần đầu luôn false (phần tử dưới ngưỡng), phần sau luôn true (phần tử từ ngưỡng trở lên) — chỉ một lần chuyển đổi duy nhất. Branch predictor học pattern này nhanh chóng và duy trì tỉ lệ đúng gần 100%. Mảng ngẫu nhiên không có pattern: truefalse xen kẽ không thể đoán trước, predictor sai xấp xỉ 50% — tức khoảng 16.000 misprediction trên 32.768 phần tử. Mỗi misprediction phạt ~15 chu kỳ → tổng phí rất lớn dù số phép tính là như nhau.
Q4
Bộ đếm 2-bit (2-bit saturating counter) hoạt động thế nào? Tại sao dùng 2 bit thay vì 1 bit?
Bộ đếm 2-bit có 4 trạng thái: Strongly Not Taken (0), Weakly Not Taken (1), Weakly Taken (2), Strongly Taken (3). Branch đi true thì tăng 1 (tối đa 3), đi false thì giảm 1 (tối thiểu 0). Dự đoán dựa vào bit cao: 2–3 → dự đoán true, 0–1 → dự đoán false. Lý do dùng 2 bit thay 1 bit: với 1 bit, chỉ cần một lần khác biệt so với pattern là đảo ngay dự đoán. Vòng lặp luôn lặp lại trừ lần thoát cuối — nếu 1 bit, lần thoát đó làm predictor đổi sang "not taken", rồi bắt đầu lần lặp mới lại sai ngay lần đầu. Bộ đếm 2-bit chịu được 1 ngoại lệ mà không đảo dự đoán, xử lý đúng với pattern vòng lặp điển hình.
Q5
Khi nào nên dùng kỹ thuật branchless, và khi nào không nên?
Nên dùng branchless chỉ khi: (1) profiler xác nhận đây là hotspot chiếm đáng kể runtime, (2) điều kiện genuinely gần 50/50 (không thể sắp xếp dữ liệu để tạo pattern), và (3) benchmark đo được cải thiện thực sự sau thay đổi. Không nên dùng branchless khi: branch dễ đoán (null check, vòng lặp thông thường), khi code khó đọc hơn mà chưa có bằng chứng hiệu năng, hoặc khi sắp xếp dữ liệu trước đơn giản hơn. Nguyên tắc: viết code rõ ràng trước, đo, tìm bottleneck, rồi mới tối ưu điểm cụ thể có bằng chứng.
Q6
Speculative execution liên hệ với branch prediction như thế nào? Vì sao nó tạo ra rủi ro bảo mật?
Speculative execution là tên gọi chung cho hành vi CPU thực thi lệnh trước khi biết chắc có nên thực thi hay không — branch prediction là một dạng speculative execution (CPU chạy lệnh của nhánh dự đoán trước khi xác nhận). Rủi ro bảo mật: khi CPU đầu cơ sai và phải flush, các lệnh đã chạy sai nhánh vẫn để lại dấu vết trong cache (cache side-channel). Attacker có thể đo thời gian truy cập cache để suy ngược ra dữ liệu từ vùng nhớ mà attacker không có quyền đọc trực tiếp — đây là cơ chế của Spectre. Bài 06 sẽ mổ xẻ chuỗi tấn công đầy đủ.
Q7
Một nhánh luôn đúng 99% thời gian bị đoán sai đột ngột 5 lần liên tiếp. Bộ đếm 2-bit xử lý tình huống này như thế nào?
Ban đầu bộ đếm ở trạng thái Strongly Taken (3). Lần sai thứ nhất: giảm xuống 2 (Weakly Taken) — vẫn đoán true. Lần sai thứ hai: giảm xuống 1 (Weakly Not Taken) — đổi sang đoán false. Lần sai thứ ba: giảm xuống 0 (Strongly Not Taken) — vẫn đoán false. Vậy từ lần thứ ba trở đi, nếu branch thực sự là false, predictor đúng. Nhưng nếu đây là nhiễu tạm thời và branch quay lại true: lần quay lại đầu tiên predictor sai (vì đang ở 0), rồi tăng lên 1, 2, 3 dần. Bộ đếm 2-bit bền vững hơn 1 bit nhưng vẫn mất vài bước để học lại sau chuỗi ngoại lệ.

Bài tiếp theo: Đừng đoán, hãy đo

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên