Cơ chế tối ưu switch...case bằng bảng nhảy

1. Cú pháp cơ bản của switch...case

Câu lệnh switch trong C/C++ yêu cầu biểu thức điều khiển phải là kiểu nguyên (như int, char, enum, v.v.), không hỗ trợ kiểu thực (float, double) hay chuỗi ký tự.

switch (biểu_thức) {
    case hằng_số_1:
        // khối lệnh
        break;
    case hằng_số_2:
        // khối lệnh
        break;
    default:
        // khối lệnh mặc định
        break;
}

2. Sự khác biệt giữa C và C++

Trong C++, việc khai báo biến trong khối case mà không có dấu ngoặc nhọn bao quanh sẽ gây lỗi biên dịch do vi phạm quy tắc khởi tạo biến. Trong khi đó, C cho phép điều này nhưng vẫn không khuyến khích.

3. Phân tích mã máy của switch...case

Khi số lượng case ít, trình biên dịch thường chuyển đổi switch thành chuỗi các lệnh if-else if. Nếu thiếu break, luồng thực thi sẽ "rơi xuyên" (fall-through) sang các case tiếp theo — điều này xảy ra vì các khối lệnh được sắp xếp liên tiếp trong bộ nhớ, và không có rào cản điều khiển luồng nào ngăn việc thực thi tiếp theo.

4. Tối ưu bằng bảng nhảy (jump table)

Khi số lượng case đủ lớn và giá trị các nhãn case gần kề nhau, trình biên dịch áp dụng kỹ thuật "bảng nhảy" để tăng tốc độ thực thi bằng cách đánh đổi không gian bộ nhớ.

Cơ chế hoạt động như sau:

  1. Tạo một mảng liên tục trong bộ nhớ, mỗi phần tử lưu địa chỉ bắt đầu của một khối case.
  2. Tại thời điểm chạy, tính hiệu số giữa giá trị switch và giá trị nhỏ nhất trong các case.
  3. Nếu hiệu số nằm ngoài khoảng hợp lệ (lớn hơn số lượng case - 1), nhảy đến default.
  4. Nếu hợp lệ, dùng hiệu số làm chỉ mục để truy cập trực tiếp vào bảng nhảy và nhảy đến khối lệnh tương ứng.

Ví dụ mã assembly minh họa:

mov eax, [a]          ; nạp giá trị biến a
sub eax, 100          ; trừ đi giá trị case nhỏ nhất (100)
cmp eax, 3            ; so sánh với số case - 1 (ở đây có 4 case: 100–103)
ja  default_label     ; nếu vượt quá, nhảy đến default
jmp [jump_table + eax*4]  ; nhảy gián tiếp qua bảng nhảy

Bảng nhảy jump_table chứa các địa chỉ như:

dd offset case_100
dd offset case_101
dd offset case_102
dd offset case_103

5. Khi nào không dùng bảng nhảy?

Nếu các giá trị case phân tán quá rộng (ví dụ: 1, 1000, 50000), việc tạo bảng nhảy sẽ lãng phí bộ nhớ do phải lấp đầy các khe trống bằng địa chỉ của default hoặc mã không hợp lệ. Trong trường hợp này, trình biên dịch sẽ quay lại sử dụng chuỗi so sánh tuần tự hoặc cây nhị phân tìm kiếm thay vì bảng nhảy.

Thẻ: C C++ switch-case jump-table compiler-optimization

Đăng vào ngày 30 tháng 5 lúc 13:12