2009-07-18 28 views
25

Một hoặc hai thập kỷ trước, thật đáng giá để viết mã số để tránh sử dụng số nhân và chia và sử dụng phép cộng và trừ thay thế. Một ví dụ tốt là sử dụng forward differences để đánh giá một đường cong đa thức thay vì tính toán đa thức trực tiếp.Tốc độ tương đối của điểm trôi nổi so với điểm nổi nhân với

Đây có phải là trường hợp hay có kiến ​​trúc máy tính hiện đại được nâng cao tới điểm mà *,/không còn chậm hơn nhiều lần so với +, -?

Để cụ thể, tôi quan tâm đến mã C/C++ được biên dịch chạy trên chip x86 điển hình hiện đại với phần cứng điểm nổi trên bo mạch rộng, không phải vi nhỏ cố gắng thực hiện phần mềm FP. Tôi nhận ra pipelining và cải tiến kiến ​​trúc khác ngăn cản số chu kỳ cụ thể, nhưng tôi vẫn muốn có được một trực giác hữu ích.

Trả lời

20

Nó cũng phụ thuộc vào hỗn hợp lệnh. Bộ vi xử lý của bạn sẽ có một số đơn vị tính toán đứng bất cứ lúc nào, và bạn sẽ nhận được thông lượng tối đa nếu tất cả chúng được lấp đầy mọi lúc. Vì vậy, thực hiện một vòng lặp của mul chỉ là nhanh như thực hiện một vòng lặp hoặc thêm - nhưng cùng không giữ nếu biểu thức trở nên phức tạp hơn.

Ví dụ, đi vòng lặp này:

for(int j=0;j<NUMITER;j++) { 
    for(int i=1;i<NUMEL;i++) { 
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; 
    } 
} 

cho NUMITER = 10^7, NUMEL = 10^2, cả hai mảng khởi tạo các số dương nhỏ (NaN là chậm hơn nhiều), điều này mất 6,0 giây sử dụng tăng gấp đôi trên một proc 64-bit. Nếu tôi thay thế các vòng lặp với

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ; 

Nó chỉ mất 1,7 giây ... do đó, vì chúng ta "overdid" những bổ sung, các muls về cơ bản miễn phí; và việc giảm bổ sung đã giúp. Nó trở nên khó hiểu hơn:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; 

- cùng phân bổ/thêm phân phối, nhưng bây giờ hằng số được thêm vào thay vì nhân lên - mất 3,7 giây. Bộ xử lý của bạn có khả năng được tối ưu hóa để thực hiện các phép tính số điển hình hiệu quả hơn; Vì vậy, dot-sản phẩm như tổng của muls và tổng số tiền là về tốt như nó được; việc thêm các hằng số gần như không phổ biến, vì vậy chậm hơn ...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/ 

lại mất 1,7 giây.

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/ 

(giống như vòng lặp ban đầu, nhưng không có thêm liên tục đắt tiền: 2.1 giây)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/ 

(chủ yếu muls, nhưng một trong những bổ sung: 1,9 giây)

Vì vậy, về cơ bản; thật khó để nói nhanh hơn, nhưng nếu bạn muốn tránh tắc nghẽn, quan trọng hơn là phải có một hỗn hợp lành mạnh, tránh NaN hoặc INF, tránh thêm các hằng số. Dù bạn làm gì, hãy chắc chắn rằng bạn kiểm tra, và kiểm tra các thiết lập trình biên dịch khác nhau, vì những thay đổi nhỏ thường chỉ có thể tạo ra sự khác biệt.

Một số chi tiết các trường hợp:

bla *= someval; // someval very near 1.0; takes 2.1 seconds 
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds 
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86 
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86 
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86 
+1

Kết hợp hướng dẫn là một điểm tốt, tôi có những người tôi làm việc với người nhấn mạnh rằng một điểm nổi 200 DSP sẽ thực hiện một 600 điểm DSP cố định. Họ hoàn toàn không xử lý vòng lặp chặt chẽ, và dành nhiều thời gian xử lý I/O hơn làm calcuations. Một bộ xử lý điểm cố định nhanh hơn sẽ giành chiến thắng dựa trên hỗn hợp lệnh tổng thể, nhưng mọi người chỉ nghĩ rằng các đơn vị FP là ma thuật chứ không phải là việc thực hiện HW của một cơ sở dữ liệu. – NoMoreZealots

+0

Ah vâng, appproach ma thuật ;-) - thật không may. –

+1

giải thích tốt đẹp với các ví dụ trực quan! –

1

Tôi không thể tìm thấy tham chiếu chính xác, nhưng thử nghiệm mở rộng cho tôi biết rằng phép nhân nổi ngày nay chỉ bằng tốc độ cộng và trừ, trong khi phép chia không phải (nhưng cũng không chậm hơn "nhiều lần"). Bạn có thể lấy trực giác mà bạn mong muốn chỉ bằng cách chạy thử nghiệm của riêng bạn - hãy nhớ tạo ra các số ngẫu nhiên (hàng triệu) trước, bạn đọc chúng trước khi bắt đầu định thời gian và sử dụng bộ đếm hiệu năng của CPU (không có quá trình chạy khác) nhiều như bạn có thể ngăn chặn chúng) để đo chính xác!

-1

Có thể có rất ít sự khác biệt về thời gian giữa phép nhân và phép cộng. phân chia mặt khác vẫn còn chậm hơn đáng kể sau đó nhân vì tính chất đệ quy của nó. trên kiến ​​trúc x86 kiến ​​trúc sse hiện đại nên được xem xét khi thực hiện hoạt động điểm nổi thay vì sau đó sử dụng fpu.Though một trình biên dịch C/C++ tốt sẽ cung cấp cho bạn tùy chọn sử dụng sse thay vì fpu.

1

Sự khác biệt về tốc độ của */vs + - phụ thuộc vào kiến ​​trúc bộ xử lý của bạn. Nói chung và với x86 nói riêng, sự khác biệt về tốc độ đã trở nên ít hơn với các bộ vi xử lý hiện đại. * phải gần +, khi nghi ngờ: chỉ cần thử nghiệm. Nếu bạn có một vấn đề thực sự khó khăn với rất nhiều hoạt động FP cũng xem xét việc sử dụng GPU của bạn (GeForce, ...) hoạt động như một bộ xử lý vectơ.

7

Cách tốt nhất để trả lời câu hỏi này là thực sự viết điểm chuẩn/hồ sơ về quá trình xử lý bạn cần thực hiện. Thực nghiệm nên được sử dụng trên lý thuyết khi có thể. Đặc biệt là khi nó dễ dàng đạt được.

Nếu bạn đã biết các cách thực hiện Toán học khác nhau mà bạn cần thực hiện, bạn có thể viết một vài cách chuyển mã khác nhau của toán và xem hiệu suất của bạn đạt đến đâu. Điều này sẽ cho phép bộ vi xử lý/trình biên dịch tạo ra các luồng thực thi khác nhau để lấp đầy các đường ống xử lý và cung cấp cho bạn một câu trả lời cụ thể cho câu trả lời của bạn.

Nếu bạn quan tâm đặc biệt đến hiệu suất của các lệnh DIV/MUL/ADD/SUB, bạn thậm chí có thể ném trong một số lắp ráp nội tuyến để kiểm soát cụ thể các biến thể của lệnh này được thực hiện. Tuy nhiên, bạn cần phải chắc chắn rằng bạn đang giữ các đơn vị thực thi đa luồng bận rộn để có được một ý tưởng tốt về hiệu năng mà hệ thống có khả năng.

Cũng làm việc như thế này sẽ cho phép bạn so sánh hiệu suất trên nhiều biến thể của bộ vi xử lý đơn giản bằng cách chạy cùng một chương trình trên chúng và cũng có thể cho phép bạn tính đến các khác biệt của bo mạch chủ.

Chỉnh sửa:

Kiến trúc cơ bản của + - giống hệt nhau. Vì vậy, họ hợp lý cùng một lúc để tính toán. * mặt khác, yêu cầu nhiều lớp, thường được tạo từ "người bổ sung đầy đủ" để hoàn thành một thao tác đơn lẻ.Điều này garentees rằng trong khi một * có thể được cấp cho các đường ống mỗi chu kỳ nó sẽ có độ trễ cao hơn so với một mạch cộng/trừ. Một fp/hoạt động thường được thực hiện bằng cách sử dụng một phương pháp xấp xỉ mà lặp đi lặp lại hội tụ về phía câu trả lời đúng theo thời gian. Những loại xấp xỉ này thường được thực hiện thông qua phép nhân. Vì vậy, đối với điểm nổi, bạn thường có thể giả định sự phân chia sẽ mất nhiều thời gian hơn bởi vì nó không thực tế để "unroll" các phép nhân (mà đã là một mạch lớn trong và của nó) vào đường ống của vô số các mạch nhân. Tuy nhiên hiệu suất của một hệ thống nhất định được đo tốt nhất thông qua thử nghiệm.

16

Về lý thuyết thông tin là ở đây:

Intel®64 and IA-32 Architectures Optimization Reference Manual, APPENDIX C INSTRUCTION LATENCY AND THROUGHPUT

Đối với mỗi bộ vi xử lý họ liệt kê, độ trễ trên FMUL rất gần với của FADD hoặc FDIV. Trên một số bộ vi xử lý cũ hơn, FDIV chậm hơn 2-3 lần so với bộ vi xử lý mới hơn, nó tương tự như FMUL.

Hãy cẩn thận:

  1. Các tài liệu tôi liên kết thực sự nói rằng bạn không thể dựa vào những con số này trong cuộc sống thực từ bộ vi xử lý sẽ làm những gì nó muốn làm cho mọi việc nhanh hơn nếu nó là đúng.

  2. Có một cơ hội tốt mà trình biên dịch của bạn sẽ quyết định sử dụng một trong nhiều tập lệnh mới hơn có một dấu phảy động/phân chia có sẵn.

  3. Đây là một tài liệu phức tạp chỉ có nghĩa là được đọc bởi các nhà biên dịch trình biên dịch và tôi có thể đã hiểu sai. Giống như tôi không rõ tại sao số lượng độ trễ FDIV bị thiếu hoàn toàn đối với một số CPU.

+1

tài liệu Rất mát mẻ. Tôi nghĩ rằng một điều vẫn còn phù hợp (và tài liệu này cho thấy nó) là phân chia vẫn còn chậm hơn nhiều so với phép nhân, cộng và trừ. Từ giao diện của tài liệu này, độ trễ của phân chia chính xác gấp đôi chậm hơn 10 lần so với phép nhân. Vì vậy, ví dụ, tôi tin rằng gọi x = y * 0.5 nên nhanh hơn gọi x = y/2. –

+0

@SteveWortham Bạn có thể vui lòng trỏ đến trang nơi bạn tìm thấy thông tin về fdiv bị chậm hơn 10x so với fmul? – 0fnt

+0

@ user247077 - Tôi không nhớ. Đây là một vài năm trước đây. Tuy nhiên, có các biểu đồ trong tài liệu này tham khảo độ trễ của rất nhiều lệnh khác nhau. Và FMUL chắc chắn nhanh hơn FDIV trong các bảng xếp hạng này. Sau đó có DIV r64 và MUL r64 trên trang C-33 có khoảng cách lớn giữa chúng trong độ trễ. Năm ngoái tôi có thể đã đạt được những hướng dẫn này (hoặc tương đương với AMD) khi tôi đã tạo ra một ứng dụng 64-bit để đánh giá sự khác biệt về hiệu năng giữa phép nhân và phép chia ... http://swortham.blogspot.com/2011/10/how -much-faster-is-multiplication-than.html –

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