2013-07-26 40 views
9

Tôi đã tự hỏi nếu exp() nhanh hơn tổng quát hơn pow(). Tôi chạy điểm chuẩn nhanh trên JsPerf http://jsperf.com/pow-vs-exp và nó cho thấy kết quả thú vị đối với tôi.Hiệu suất Pow() so với exp()

Math.exp(logBase * exponent); // fastest 
Math.exp(Math.log(base) * exponent); // middle 
Math.pow(base, exponent); // slowest 

Tôi biết rằng kết quả sẽ thay đổi rất nhiều về kiến ​​trúc và ngôn ngữ nhưng tôi cũng quan tâm đến quan điểm lý thuyết. Là pow(a, b) được triển khai dưới dạng exp(log(a) * b) hoặc có cách nào thông minh hơn về cách tính đồng quyền lực "trực tiếp" (trong C++, C# hoặc JavaScript). Có hướng dẫn CPU cho exp, đăng nhập hoặc pow trên một số kiến ​​trúc?

Theo như tôi biết, cả hai exp()log() được tính bằng cách sử dụng một số chuỗi Taylor và khá tốn kém để tính toán. Điều này làm cho tôi tin rằng đối với cơ sở liên tục của quyền lực, mã này

double logBase = log(123.456); 
for (int i = 0; i < 1024; ++i) { 
    exp(logBase * 654.321); 
} 

là tốt hơn thế này

for (int i = 0; i < 1024; ++i) { 
    pow(123.456, 654.321); 
} 

Đó là giả định có đúng không?

+4

Tôi sẽ không ngạc nhiên nếu một trong các tùy chọn đó chính xác hơn đáng kể so với các tùy chọn khác. – delnan

+1

Tôi có biên độ lỗi khoảng 2-5%. Hãy thử chạy thử nghiệm vài lần.Nhưng điểm chuẩn tất nhiên là xa hoàn hảo. Đó là lý do tại sao tôi quan tâm đến lý thuyết đằng sau điều này. Và cũng chính xác là câu hỏi thú vị. – NightElfik

+0

Điều này thực sự sẽ phụ thuộc vào chi tiết triển khai. Câu hỏi của bạn về JavaScript có đặc biệt không? –

Trả lời

9

Có, exp sẽ nhanh hơn pow nói chung.

Các chức năng explog sẽ được tối ưu hóa cho nền tảng đích; nhiều kỹ thuật có thể được sử dụng như Pade xấp xỉ, giảm tuyến tính hoặc nhị phân theo sau là xấp xỉ, v.v.

Chức năng pow thường được thực hiện là exp(log(a) * b) như vậy rõ ràng là chậm hơn exp một mình.Có rất nhiều trường hợp đặc biệt cho pow chẳng hạn như số mũ âm, số mũ không tách rời, số mũ bằng 1/2 hoặc 1/3, v.v. Điều này sẽ làm chậm thêm pow trong trường hợp chung vì các thử nghiệm này rất tốn kém.

Xem this SO question on pow.

1

Như một câu trả lời một phần, có hướng dẫn cho điểm kinh nghiệm, đăng nhập hoặc pow trên một số kiến ​​trúc có. Tuy nhiên, điều đó không nhất thiết có nghĩa là nhiều.

Ví dụ, trên x86 có

  • f2xm1 này sẽ tính 2 x-1
  • fscale này sẽ tính y * 2 (int) x
  • fyl2x này sẽ tính y * log x
  • fyl2xp1 tính toán y * log (x + 1) (có hạn chế về phạm vi nhập liệu)

Tuy nhiên, chúng không được sử dụng nhiều. Nó thay đổi từ kiến ​​trúc đến kiến ​​trúc, nhưng chúng không bao giờ nhanh. Như một ví dụ cực đoan, fyl2x có độ trễ 724 trên Sandy Bridge (khá gần đây!), Trong thời gian đó trên cùng một bộ xử lý, bạn có thể thực hiện khoảng 700 điểm bổ sung độc lập, hoặc khoảng 240 điểm bổ sung phụ, hoặc khoảng 2000 độc lập hoạt động số nguyên đơn giản.

Điều đó thật tồi tệ, nhưng chúng thường chậm. Đủ chậm để thực hiện thủ công có thể đánh bại họ hoặc ít nhất là không bị mất đáng kể.

Ngoài ra, mã FPU đang dần biến mất có lợi cho mã SSE. Không có SSE tương đương với các hướng dẫn đó.

+0

SSE là một FPU, và nó đã thay thế phần lớn * x87 *, đó là lý do tại sao Intel không nỗ lực làm cho 'fyl2x' nhanh. – Potatoswatter

+0

@Potatoswatter dường như bạn cho rằng gọi mã x87 "mã FPU" không đúng, tuy nhiên, thường được thực hiện. Đó là một sự phân biệt hợp lệ, SSE không sử dụng FPU cũ. Tất nhiên nó được thực hiện với các đơn vị chức năng tương tự trên CPU, nhưng về mặt logic chúng hoàn toàn tách biệt. – harold

+0

Có thể có hoặc không. Dù bằng cách nào, SSE là một tập lệnh cho một FPU (vector), vì vậy hãy gọi mã x87 "Mã FPU" để phân biệt mã SSE với SSE là gây hiểu lầm hoặc chỉ gây nhầm lẫn. Hơn thế nữa, x87 không liên quan vì nó lỗi thời. – Potatoswatter

1

Bất kể chi tiết kiến ​​trúc, Math.pow phải làm nhiều hơn về kiểm tra lỗi (ví dụ: điều gì sẽ xảy ra nếu cơ sở là số âm?). hơn Math.exp (và như vậy tôi mong đợi pow sẽ chậm hơn).

phần có liên quan của spec:

http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.8

15.8.2.8 exp (x)

Trả về một xấp xỉ thực hiện phụ thuộc vào hàm mũ hàm của x (e nâng lên sức mạnh của x, trong đó e là cơ số của logarit tự nhiên).

Nếu x là NaN, kết quả là NaN. Nếu x là +0, kết quả là 1. Nếu x là −0, kết quả là 1. Nếu x là + ∞, kết quả là + ∞. Nếu x là −∞, kết quả là +0.

http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.13

15.8.2.13 pow (x, y)

Trả về một xấp xỉ thực hiện phụ thuộc vào kết quả của nâng x với sức mạnh y.

Nếu y là NaN, kết quả là NaN. Nếu y là +0, kết quả là 1, ngay cả khi x là NaN. Nếu y là −0, kết quả là 1, ngay cả khi x là NaN. Nếu x là NaN và y không khác, kết quả là NaN. Nếu abs (x)> 1 và y là + ∞, kết quả là + ∞. Nếu abs (x)> 1 và y là −∞, kết quả là +0. Nếu abs (x) == 1 và y là + ∞, kết quả là NaN. Nếu abs (x) == 1 và y là −∞, kết quả là NaN. Nếu abs (x) < 1 và y là + ∞, kết quả là +0. Nếu abs (x) < 1 và y là −∞, kết quả là + ∞. Nếu x là + ∞ và y> 0, kết quả là + ∞. Nếu x là + ∞ và y < 0, kết quả là +0. Nếu x là −∞ và y> 0 và y là một số nguyên lẻ, kết quả là −∞. Nếu x là −∞ và y> 0 và y không phải là số nguyên lẻ, kết quả là + ∞. Nếu x là −∞ và y < 0 và y là một số nguyên lẻ, kết quả là −0. Nếu x là −∞ và y < 0 và y không phải là số nguyên lẻ, kết quả là +0. Nếu x là +0 và y> 0, kết quả là +0. Nếu x là +0 và y < 0, kết quả là + ∞. Nếu x là −0 và y> 0 và y là một số nguyên lẻ, kết quả là −0. Nếu x là −0 và y> 0 và y không phải là số nguyên lẻ, kết quả là +0. Nếu x là −0 và y < 0 và y là một số nguyên lẻ, kết quả là −∞. Nếu x là −0 và y < 0 và y không phải là số nguyên lẻ, kết quả là + ∞. Nếu x và x là hữu hạn và y là hữu hạn và y không phải là số nguyên, kết quả là NaN.

+0

Cảm ơn, điều này thật thú vị! Đó có thể là lý do tại sao 'Math.exp (Math.log (base) * exponent)' nhanh hơn 'Math.pow (base, exponent)'. Nó có thể không tính toán tất cả các trường hợp đặc biệt một cách chính xác nhưng tôi không quan tâm đến họ anyway. – NightElfik

+0

@NightElfik bất kể câu trả lời cho câu hỏi, trừ khi bạn dự định thực hiện công việc số nặng trong JS, tôi nghi ngờ rằng thời gian chạy 'exp' và' pow' thống trị thời gian thực thi tập lệnh của bạn. Hãy nhớ rằng báo giá knuth "tối ưu hóa sớm là gốc rễ của tất cả các điều ác" – SheetJS

+0

Tôi biết điều này rất tốt. Mặc dù đoạn mã này được sử dụng nhiều, nhưng tôi đã lang thang nhiều hơn về lý thuyết đằng sau hiện tượng này thay vì thực sự cố gắng tăng tốc mã của tôi. Trước khi thực hiện bất kỳ tối ưu hóa hiệu suất nào, trước tiên tôi định cấu hình. – NightElfik

Các vấn đề liên quan