Nó sẽ thực sự phụ thuộc vào bộ vi xử lý, và phạm vi của các số nguyên mà là tốt hơn (và sử dụng double
sẽ giải quyết hầu hết các vấn đề range)
Đối với CPU "lớn" hiện đại như x86-64 và ARM, phân chia số nguyên và phân chia dấu phẩy động là nỗ lực tương tự, và chuyển đổi một số nguyên thành phao hoặc ngược lại không phải là một nhiệm vụ "cứng" (và làm tròn chính xác trực tiếp trong chuyển đổi đó). là.
atmp = (float) a;
btmp = (float) b;
resfloat = divide atmp/btmp;
return = to_int_with_rounding(resfloat)
Giới thiệu về bốn hướng dẫn máy.
Mặt khác, mã của bạn sử dụng hai lần chia, một modulo và một số nhân, có khả năng dài hơn trên bộ xử lý như vậy.
tmp = a/b;
tmp1 = a % b;
tmp2 = tmp1 * 2;
tmp3 = tmp2/b;
tmp4 = tmp + tmp3;
Vì vậy lăm hướng dẫn, và ba trong số đó là những "chia" (trừ khi trình biên dịch là đủ thông minh để tái sử dụng a/b
cho a % b
- nhưng nó vẫn còn hai phân chia riêng biệt).
Tất nhiên, nếu bạn ở ngoài phạm vi số chữ số mà phao hoặc kép có thể giữ mà không làm mất chữ số (23 bit cho float, 53 bit cho double), thì phương pháp của bạn CÓ THỂ tốt hơn (giả sử không có tràn trong toán số nguyên).
Trên tất cả, vì biểu mẫu đầu tiên được sử dụng bởi "mọi người", đó là biểu mẫu mà trình biên dịch nhận ra và có thể tối ưu hóa. Rõ ràng, các kết quả phụ thuộc vào cả trình biên dịch đang được sử dụng và bộ xử lý nó chạy, nhưng đây là kết quả của tôi từ việc chạy mã được đăng ở trên, được biên dịch qua clang++
(v3.9-trước khi phát hành, khá gần để phát hành 3.8).
round_divide_by_float_casting(): 32.5 ns
round_divide_by_modulo(): 113 ns
divide_by_quotient_comparison(): 80.4 ns
Tuy nhiên, điều thú vị tôi thấy khi tôi nhìn vào mã được tạo:
xorps %xmm0, %xmm0
cvtsi2ssl 8016(%rsp,%rbp), %xmm0
xorps %xmm1, %xmm1
cvtsi2ssl 4016(%rsp,%rbp), %xmm1
divss %xmm1, %xmm0
callq roundf
cvttss2si %xmm0, %eax
movl %eax, 16(%rsp,%rbp)
addq $4, %rbp
cmpq $4000, %rbp # imm = 0xFA0
jne .LBB0_7
là round
thực sự là một cuộc gọi. Điều thực sự làm tôi ngạc nhiên, nhưng giải thích tại sao trên một số máy (đặc biệt là các bộ vi xử lý x86 gần đây), nó nhanh hơn.
g++
cho kết quả tốt hơn với -ffast-math
, mang đến cho xung quanh:
round_divide_by_float_casting(): 17.6 ns
round_divide_by_modulo(): 43.1 ns
divide_by_quotient_comparison(): 18.5 ns
(Đây là với tăng đếm đến 100k giá trị)
Đối với tôi, cái đầu tiên rõ ràng hơn nhiều. Tôi biết nó đang cố gắng làm gì. Tôi biết làm thế nào nó đang cố gắng để làm điều đó. Mà không cần chạy mặc dù nó trên giấy tôi không có ý tưởng những gì thứ hai đang làm. Tôi cũng nghiêng đầu tiên là nhanh hơn bởi vì thứ hai hiện một số hoạt động số học – John3136
Tôi đồng ý rõ ràng là quan trọng. Nhưng đối với phức tạp hoạt động (và tốc độ), tôi không chắc chắn. –