Thủ thuật này phụ thuộc vào việc diễn giải lại các bit của số dấu phẩy động dưới dạng số nguyên và trở lại một lần nữa, điều này có thể có trong JavaScript bằng cách sử dụng tiện ích Mảng gõ , để tạo bộ đệm byte thô với nhiều chế độ xem số trên đó.
Đây là một chuyển đổi theo nghĩa đen của mã bạn đã đưa ra; lưu ý rằng nó không hoàn toàn giống nhau, vì tất cả các phép toán số học trong JavaScript là dấu phẩy động 64 bit, không phải 32 bit, do đó, đầu vào sẽ nhất thiết phải được chuyển đổi. Ngoài ra, giống như mã gốc, điều này phụ thuộc vào nền tảng ở chỗ nó sẽ cho kết quả vô nghĩa nếu kiến trúc bộ xử lý sử dụng một thứ tự byte khác nhau; nếu bạn phải làm những việc như thế này, tôi khuyên ứng dụng của bạn trước tiên phải thực hiện một trường hợp thử nghiệm để xác định rằng số nguyên và số float có các biểu diễn byte mà bạn mong đợi.
const bytes = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
const floatView = new Float32Array(bytes);
const intView = new Uint32Array(bytes);
const threehalfs = 1.5;
function Q_rsqrt(number) {
const x2 = number * 0.5;
floatView[0] = number;
intView[0] = 0x5f3759df - ( intView[0] >> 1 );
let y = floatView[0];
y = y * ( threehalfs - ( x2 * y * y ) );
return y;
}
Tôi đã xác nhận bằng cách đánh dấu đồ thị rằng điều này cho kết quả số hợp lý. Tuy nhiên, không rõ ràng rằng điều này sẽ cải thiện hiệu suất, vì chúng tôi đang thực hiện nhiều hoạt động JavaScript cấp cao hơn. Tôi đã chạy các điểm chuẩn trên các trình duyệt mà tôi có và thấy rằng Q_rsqrt(number)
mất 50% đến 80% thời gian dành cho 1/sqrt(number)
(Chrome, Firefox và Safari trên macOS, kể từ tháng 4 năm 2018). Đây là thiết lập thử nghiệm hoàn chỉnh của tôi:
const {sqrt, min, max} = Math;
const bytes = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
const floatView = new Float32Array(bytes);
const intView = new Uint32Array(bytes);
const threehalfs = 1.5;
function Q_rsqrt(number) {
const x2 = number * 0.5;
floatView[0] = number;
intView[0] = 0x5f3759df - ( intView[0] >> 1 );
let y = floatView[0];
y = y * ( threehalfs - ( x2 * y * y ) );
return y;
}
// benchmark
const junk = new Float32Array(1);
function time(f) {
const t0 = Date.now();
f();
const t1 = Date.now();
return t1 - t0;
}
const timenat = time(() => {
for (let i = 0; i < 5000000; i++) junk[0] = 1/sqrt(i)
});
const timeq = time(() => {
for (let i = 0; i < 5000000; i++) junk[0] = Q_rsqrt(i);
});
document.getElementById("info").textContent =
"Native square root: " + timenat + " ms\n" +
"Q_rsqrt: " + timeq + " ms\n" +
"Ratio Q/N: " + timeq/timenat;
// plot results
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function plot(f) {
ctx.beginPath();
const mid = canvas.height / 2;
for (let i = 0; i < canvas.width; i++) {
const x_f = i / canvas.width * 10;
const y_f = f(x_f);
const y_px = min(canvas.height - 1, max(0, mid - y_f * mid / 5));
ctx[i == 0 ? "moveTo" : "lineTo"](i, y_px);
}
ctx.stroke();
ctx.closePath();
}
ctx.strokeStyle = "black";
plot(x => 1/sqrt(x));
ctx.strokeStyle = "yellow";
plot(x => Q_rsqrt(x));
<pre id="info"></pre>
<canvas width="300" height="300" id="canvas"
style="border: 1px solid black;"></canvas>