2013-02-04 57 views
5

Tôi có một mã số giải phương trình f(x) = 0, trong đó tôi phải tăng x lên một điện p. Tôi giải quyết nó bằng cách sử dụng một loạt các thứ, nhưng cuối cùng tôi có phương pháp của Newton. Giải pháp này xảy ra bằng x = 1 và do đó là nguyên nhân của sự cố của tôi. Khi giải pháp được lặp lại gần với 1, giả sử x = 1 + 1e-13, thời gian cần thiết để tính toán std::pow(x, p) tăng lên rất nhiều, dễ dàng với hệ số 100, làm cho mã của tôi không sử dụng được.Rất chậm std :: pow() cho các căn cứ rất gần với 1

Máy chạy điều này là AMD64 (Opteron 6172) trên CentOS và lệnh đơn giản là . Hành vi tương tự xuất hiện trên tất cả các máy của tôi, tất cả x64. Theo tài liệu here, đây không chỉ là vấn đề của tôi (tức là, người khác cũng đang tức giận), chỉ xuất hiện trên x64 và chỉ cho x gần với 1.0. Điều tương tự cũng xảy ra với exp.

Giải quyết vấn đề này là rất quan trọng đối với tôi. Có ai biết nếu có bất kỳ cách nào để đi xung quanh sự chậm chạp này?

EDIT: John đã chỉ ra rằng điều này là do các biến thể. Câu hỏi là sau đó, làm thế nào để sửa lỗi này? Mã này là C++, được biên dịch với g++ để sử dụng trong phạm vi GNU Octave. Dường như, mặc dù tôi đã đặt CXXFLAGS để bao gồm -mtune=native-ffast-math, điều đó không giúp ích và mã chạy chậm.

PSEUDO-GIẢI PHÁP NGAY BÂY GIỜ: Đối với tất cả những ai quan tâm đến vấn đề này, các giải pháp được đề xuất bên dưới không hiệu quả đối với cá nhân tôi. Tôi thực sự cần tốc độ thông thường là std::pow(), nhưng không có sự chậm chạp xung quanh x = 1. Các giải pháp cho cá nhân tôi là sử dụng hack như sau:

inline double mpow(double x, double p) __attribute__ ((const)); 

inline double mpow(double x, double p) 
{ 
    double y(x - 1.0); 
    return (std::abs(y) > 1e-4) ? (std::pow(x, p)) : (1.0 + p * y * (1.0 + (p - 1.0) * y * (0.5 + (1.0/6.0) * (p - 2.0) * y))); 
} 

các ràng buộc có thể thay đổi, nhưng đối với -40 < p < 40 lỗi là nhỏ hơn khoảng 1e-11, đó là đủ tốt. Overhead là tối thiểu từ những gì tôi tìm thấy, do đó giải quyết vấn đề cho tôi.

+5

này có thể liên quan đến các vấn đề hiệu suất chung với [số subnormal] (http://en.wikipedia.org/wiki/Denormal_number). Tính toán với các giá trị dấu phẩy động rất gần với 0 có thể chậm hơn 100 lần so với bình thường. Xem http://stackoverflow.com/questions/9314534/why-does-changing-0-1f-to-0-slow-down-performance-by-10x. –

+0

Điểm tốt. Bất kỳ gợi ý nào về cách giải quyết vấn đề này? Sửa các con số chính xác 1 nếu chúng đủ gần? –

+0

@JohnKugelman: Nếu bạn đọc liên kết, điều này là do glibc sử dụng hàm chậm hơn nhiều (có tên '__slowpow') khi được cung cấp một số giá trị đầu vào nhất định. – interjay

Trả lời

0

Nó cũng có thể là thuật toán của bạn. Có lẽ chuyển sang một cái gì đó như BFGS thay vì phương pháp Newton sẽ giúp đỡ.

Bạn không nói gì về tiêu chí hội tụ của mình. Có lẽ những người cần điều chỉnh là tốt.

+0

Anh ấy không thực hiện 'pow', nhưng bằng cách sử dụng triển khai thư viện chuẩn. :) – jalf

+1

Không, đây thực sự là vấn đề chính xác. Tôi đã hẹn giờ mã và cố gắng tất cả mọi thứ cho đến khi tôi đóng đinh xuống nguyên nhân. –

+0

Tôi hiểu. BFGS phải làm theo cách bạn xây dựng ma trận, không nhất thiết phải tính toán pow. – duffymo

9

Cách giải quyết rõ ràng là lưu ý rằng trong thực tế, a ** b == exp(log(a) * b) và sử dụng biểu mẫu đó thay thế. Bạn sẽ cần phải kiểm tra xem nó không ảnh hưởng xấu đến độ chính xác của kết quả của bạn hay không. Edit: như đã thảo luận, điều này cũng bị chậm lại đến gần như là một mức độ lớn.

Sự cố không phải là biến thể, ít nhất là không trực tiếp; cố gắng tính toán exp(-2.4980018054066093e-15) bị suy giảm tương tự, và -2.4980018054066093e-15 chắc chắn không phải là bất thường.

Nếu bạn không quan tâm đến tính chính xác của kết quả của bạn, sau đó mở rộng quy mô một trong hai exponend hoặc số mũ sẽ giúp bạn ra ngoài vùng chậm:

sqrt(pow(a, b * 2)) 
pow(a * 2, b)/pow(2, b) 
... 

Lỗi này được biết đến bảo trì glibc: http://sourceware.org/bugzilla/show_bug.cgi?id=13932 - nếu bạn đang tìm kiếm một bản sửa lỗi trái ngược với một giải pháp thay thế, bạn sẽ muốn đưa ra một chuyên gia toán học dấu phẩy động với kinh nghiệm nguồn mở.

+0

Cái này nhanh hơn gấp 4 lần so với pow(), đã thử điều đó. –

+0

Chia tỷ lệ 'x' hoặc' p' không giúp ích gì trong các thử nghiệm của tôi. Sửa vấn đề trong glibc sẽ không giúp ích gì, bởi vì điều này phải chạy trên Mac OS và MATLAB, sử dụng GCC cổ đại để biên dịch các tệp MEX của nó. –