So sánh hiệu năng giữa toán tử bitwise và phép toán thông thường

Trong quá trình tìm hiểu về JavaScript, tôi bắt gặp thông tin cho rằng các phép toán bitwise (toán tử bit) có tốc độ xử lý nhanh hơn các phép toán thông thường do chúng hoạt động ở tầng thấp hơn. Để kiểm chứng điều này, tôi đã tiến hành một thử nghiệm nhỏ.

Mã kiểm tra hiệu năng

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Kiểm tra tốc độ toán tử bitwise</title>
</head>
<body>
  <script>
    const soLanLapLon = 999999999; // Số lần lặp lớn
    const soLanChay = 50; // Số lần chạy phép đo để lấy trung bình

    function thucHienPhepDo(soLanLap) {
      // Đo thời gian cho phép toán bitwise NOT (~)
      const batDau1 = performance.now();
      for (let i = 0; i < soLanLap; i++) {
        let ketQua = ~25; // Phép toán bitwise NOT
      }
      const ketThuc1 = performance.now();

      // Đo thời gian cho phép toán trừ thông thường (-n - 1)
      const batDau2 = performance.now();
      for (let i = 0; i < soLanLap; i++) {
        let ketQua = -25 - 1; // Phép toán trừ tương đương
      }
      const ketThuc2 = performance.now();

      // Lưu ý: Trong hầu hết trường hợp, toán tử bitwise nhanh hơn một chút,
      // nhưng sự khác biệt là không đáng kể (chỉ vài chục mili giây, khoảng 3%).
      // Trừ khi ở điều kiện cực đoan, sự khác biệt này không ảnh hưởng đến việc phát triển thông thường.
      return {
        thoiGianBitwise: ketThuc1 - batDau1,
        thoiGianTru: ketThuc2 - batDau2
      };
    }

    const ketQuaChay = [];
    for (let i = 0; i < soLanChay; i++) {
      ketQuaChay.push(thucHienPhepDo(soLanLapLon));
    }

    // Tính tổng thời gian qua các lần chạy
    const tongKetQua = ketQuaChay.reduce((tong, lanChay) => {
      const ketQuaMoi = {
        thoiGianBitwise: tong.thoiGianBitwise + lanChay.thoiGianBitwise,
        thoiGianTru: tong.thoiGianTru + lanChay.thoiGianTru
      };
      return ketQuaMoi;
    }, { thoiGianBitwise: 0, thoiGianTru: 0 });

    console.log('Kết quả đo lường tổng:', tongKetQua);
    console.log('Giá trị trung bình:', tongKetQua.thoiGianBitwise / soLanChay, tongKetQua.thoiGianTru / soLanChay);
  </script>
</body>
</html>

Kết quả thử nghiệm

Các kết quả ban đầu là của phép toán bitwise, theo sau là kết quả của phép toán trừ thông thường. Với 99,999,999 lần lặp và lấy trung bình sau 50 lần chạy, có thể thấy sự khác biệt giữa hai phương pháp là rất nhỏ. Mặc dù trạng thái hoạt động của máy tính tại mỗi thời điểm là khác nhau và thử nghiệm này không đảm bảo giữ nguyên tất cả các biến số khác, kết quả thu được về cơ bản là chính xác. Sự chênh lệch rất nhỏ này cho thấy trong các ứng dụng thông thường, số lần thực hiện phép toán sẽ không tạo ra sự khác biệt đáng kể.

Ứng dụng của toán tử bitwise

Toán tử bitwise thường được sử dụng trong React để đánh dấu trạng thái hoặc kiểm tra kiểu dữ liệu.

Ưu điểm của toán tử bitwise:

  • Tiết kiệm bộ nhớ: Sử dụng biểu diễn nhị phân cho phép kết hợp nhiều trạng thái hoặc kiểu dữ liệu vào một số nguyên duy nhất, giảm lượng bộ nhớ cần thiết.
  • Tăng hiệu quả: Các phép toán bitwise thường hiệu quả hơn các phép toán logic khác, có thể hoàn thành việc kiểm tra và thiết lập nhiều trạng thái/kiểu dữ liệu chỉ trong một lệnh, do đó tăng tốc độ thực thi của chương trình.
  • Dễ dàng mở rộng: Kỹ thuật mặt nạ bit (bitmask) cho phép dễ dàng thêm các trạng thái hoặc kiểu dữ liệu mới chỉ bằng cách thao tác trên các bit hiện có, mà không cần định nghĩa lại cấu trúc dữ liệu.
  • Mã nguồn gọn gàng: Thông qua việc sử dụng toán tử bitwise, các thao tác trên nhiều trạng thái hoặc kiểu dữ liệu có thể được tập trung tại một điểm, làm cho mã nguồn rõ ràng và ngắn gọn hơn, giảm logic kiểm tra lặp đi lặp lại.

Ví dụ minh họa: Quản lý trạng thái bật/tắt của hai bóng đèn

Cách 1: Sử dụng hai biến riêng biệt

let den1 = true;
let den2 = false;

// Thay đổi trạng thái
den1 = false;
den2 = true;

Cách 2: Sử dụng một mảng

let den = [];
den.push('den1'); // Thêm 'den1' vào mảng để biểu thị trạng thái bật

// Nếu tên đèn không có trong mảng, nghĩa là đèn đang tắt
den = den.filter(item => item === 'den1');

Cách 3: Sử dụng toán tử bitwise

Bằng cách định nghĩa trước các hằng số tương ứng với từng trạng thái, ta có thể lưu trữ nhiều trạng thái chỉ với một biến số duy nhất.

// Bit thứ nhất (0) và bit thứ hai (1) đại diện cho đèn 1 và đèn 2
// 0b10 (nhị phân) đại diện cho đèn 1 bật, đèn 2 tắt
// 0b01 (nhị phân) đại diện cho đèn 1 tắt, đèn 2 bật
// 0b11 (nhị phân) đại diện cho cả hai đèn bật
const DEN_1 = 0b10; // Bit thứ 2 từ phải sang là 1
const DEN_2 = 0b01; // Bit thứ 1 từ phải sang là 1
const DEN_1_VA_2 = 0b11;

let trangThaiDen = 0b00; // Ban đầu cả hai đèn đều tắt

// Bật đèn 1
trangThaiDen = trangThaiDen | DEN_1; // Sử dụng toán tử OR để bật bit tương ứng
console.log(trangThaiDen.toString(2)); // Đầu ra: "10"

// Bật đèn 2
trangThaiDen = trangThaiDen | DEN_2; // Sử dụng toán tử OR để bật bit tương ứng
console.log(trangThaiDen.toString(2)); // Đầu ra: "11"

// Tắt đèn 1
trangThaiDen = trangThaiDen & ~DEN_1; // Sử dụng AND với NOT để tắt bit tương ứng
console.log(trangThaiDen.toString(2)); // Đầu ra: "1" (chỉ đèn 2 còn bật)

Thẻ: JavaScript toán tử bitwise hiệu năng tối ưu hóa react

Đăng vào ngày 12 tháng 6 lúc 08:24