2012-01-19 34 views
7

Tôi đang thực hiện chức năng nhiễu kết hợp và ngạc nhiên khi thấy rằng sử dụng nhiễu gradient (tức là tiếng ồn Perlin) thực sự nhanh hơn một chút so với nhiễu giá trị. Hồ sơ cho thấy rằng lý do cho điều này là bộ phận cần thiết để chuyển đổi các giá trị int ngẫu nhiên thành một đôi phạm vi -1.0 đến 1.0:Phân chia hiệu quả gấp đôi theo công suất 2

static double noiseValueDouble(int seed, int x, int y, int z) { 
    return 1.0 - ((double)noiseValueInt(seed, x, y, z)/1073741824.0); 
} 

tiếng ồn Gradient đòi hỏi một vài nhân lên nhiều hơn, nhưng do việc sử dụng bảng precomputed dốc số noiseValueInt trực tiếp để tính chỉ mục vào bảng và không yêu cầu bất kỳ bộ phận nào. Vì vậy, câu hỏi của tôi là, làm thế nào tôi có thể làm cho các bộ phận trên hiệu quả hơn, xem xét việc phân chia là bởi một sức mạnh của 2 (2^30). Về mặt lý thuyết, tất cả những gì cần làm là trừ 30 từ số mũ kép, nhưng làm điều đó bằng vũ lực (tức là thao tác bit) sẽ dẫn đến tất cả các trường hợp góc (INF, NAN, tràn số mũ, v.v ...).). Một giải pháp lắp ráp x86 sẽ là ok.

+2

Bạn có chắc chắn rằng điều đó quan trọng không? Sử dụng thao tác bit cụ thể của máy không mang tính di động và thậm chí có thể đạt hiệu suất do các vấn đề về bộ nhớ đệm hoặc lên lịch ... –

+0

@BasileStarynkevitch Đó là ý tôi với đoạn cuối của tôi. Tôi không muốn làm thao tác bit giả sử IEEE 754, nhưng sẽ là tốt với một giải pháp lắp ráp x86 cụ thể. – zennehoy

+0

Tôi sẽ không bận tâm về điều đó. Bạn sẽ không giành chiến thắng nhiều, và bạn có thể mất một số hiệu suất. –

Trả lời

6

Khai báo một biến (hoặc không đổi) với giá trị nghịch đảo và nhân với nó, thay đổi một cách hiệu quả bộ phận cho một phép nhân:

static const double div_2_pow_30 = 1.0/1073741824.0; 

Một cách khác (sử dụng thuộc tính rằng số lượng là một sức mạnh của 2) là sửa đổi số mũ bằng các phép toán bit. Làm như vậy sẽ làm cho mã phụ thuộc vào đôi được lưu trữ bằng cách sử dụng tiêu chuẩn IEEE mà có thể ít cầm tay hơn.

+1

Tôi tin rằng trình biên dịch đã thực hiện tối ưu hóa đó. –

+2

Tôi sẽ nói rằng trình biên dịch có thể tìm ra điều đó, nhưng thực sự không, [nó có thể dẫn đến các kết quả số khác nhau] (http://ridiculousfish.com/blog/posts/will-it-optimize.html) (trừ khi bạn sử dụng ví dụ như -ffast-math cho gcc). – spraff

+1

@spraff: Trong trường hợp số chia là một sức mạnh chính xác của hai, và nghịch đảo của nó không tràn hoặc tràn, kết quả là luôn luôn giống nhau. Hầu hết các trình biên dịch đều nhận thức được điều này và thực hiện tối ưu hóa này. –

2

Tôi không chắc bạn có thể tin tưởng vào hồ sơ ở đây không. Đối với các hàm nhỏ hơn, nhanh hơn, hiệu ứng của chính mã profiling bắt đầu làm lệch kết quả.

Chạy noiseValueDouble và cách thay thế tương ứng trong vòng lặp để có con số tốt hơn.

Giải pháp lắp ráp x86 giải pháp bit-fiddling, bạn cũng có thể thực hiện thao tác bit trong C. Hướng dẫn phân chia nguồn điện nhanh (bit shift) chỉ tồn tại cho số nguyên.

Nếu bạn thực sự muốn sử dụng hướng dẫn đặc biệt, hãy bật MMX hoặc làm gì đó.

+0

Vâng, tôi cũng đã làm điều này. Hiệu ứng, trong khi ít hơn, vẫn còn đó. Đối với nhiễu gradient, cuộc gọi là 'grad (noiseValueInt (seed, x0, y0), x, y)', với giá trị noise, 'noiseValueDouble (seed, x0, y0)'. Ngoài ra, hai thuật toán là giống hệt nhau. – zennehoy

0

tôi đã cố gắng biên soạn này với gcc:

double divide(int i) { 
    return 1.0 - (double)i/1073741824.0; 
} 

với -O3 nó được mã hóa như một FMULS -instruction, với -O3 -mfpmath=sse -march=core2 nó sử dụng SSE-hướng dẫn cài đặt và mã hóa nó như MULSD. Tôi không biết tốc độ nhanh nhất là gì, nhưng chính cuộc gọi hàm có lẽ là các đơn đặt hàng có cường độ chậm hơn so với phân chia thực tế.

0

Bạn có thể sửa đổi số mũ trực tiếp sử dụng các hàm frexpldexp. Tôi không chắc chắn nếu điều này sẽ được nhanh hơn mặc dù.

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