SQRT * và DIV * là hai hướng dẫn ALU "đơn giản" duy nhất (uop đơn lẻ, không phân nhánh / vòng lặp được mã hóa) có thông lượng hoặc độ trễ phụ thuộc dữ liệu trên CPU Intel / AMD hiện đại. (Không tính microcode hỗ trợ cho các giá trị FP không bình thường hay không bình thường trong add / Multiply / fma). Mọi thứ khác đều được sửa chữa khá nhiều, vì vậy máy móc lập lịch uop không theo thứ tự không cần chờ xác nhận rằng kết quả đã sẵn sàng một số chu kỳ, nó chỉ biết rằng nó sẽ được.
Như thường lệ, hướng dẫn nội tại của Intel đưa ra một bức tranh đơn giản hóa về hiệu suất. Độ trễ thực tế không phải là 18 chu kỳ cố định cho độ chính xác kép trên Skylake. (Dựa trên những con số bạn chọn để trích dẫn, tôi giả sử bạn có Skylake.)
div / sqrt khó thực hiện; ngay cả trong phần cứng, điều tốt nhất chúng ta có thể làm là một quá trình sàng lọc lặp lại. Tinh chỉnh nhiều bit cùng một lúc (bộ chia radix-1024 kể từ Broadwell) tăng tốc độ (xem phần Hỏi & Đáp về phần cứng này ). Nhưng nó vẫn đủ chậm để sử dụng sớm để tăng tốc các trường hợp đơn giản (Hoặc có thể cơ chế tăng tốc chỉ bỏ qua bước thiết lập cho các mantissas hoàn toàn không trên các CPU hiện đại với các đơn vị div / sqrt được đường ống một phần. = độ trễ cho FP div / sqrt; đơn vị thực thi đó khó đường ống hơn.)
https://www.uops.info/html-instr/VSQRTSD_XMM_XMM_XMM.html cho thấy Skylake SQRTSD có thể thay đổi từ 13 đến 19 độ trễ chu kỳ. Các số SKL (máy khách) chỉ hiển thị độ trễ 13 chu kỳ, nhưng chúng ta có thể thấy từ trang SKL vsqrtsd chi tiết mà chúng chỉ kiểm tra với đầu vào = 0. Số SKX (máy chủ) hiển thị độ trễ 13-19 chu kỳ. ( Trang này có phân tích chi tiết về mã kiểm tra mà họ đã sử dụng, bao gồm các mẫu bit nhị phân cho các thử nghiệm.) Thử nghiệm tương tự (chỉ có 0 đối với lõi máy khách) đã được thực hiện trên trang không phải VEXsqrtsd xmm, xmm
. : /
Kết quả InstLatx64 cho thấy độ trễ trường hợp tốt nhất / xấu nhất từ 13 đến 18 chu kỳ trên Skylake-X (sử dụng cùng lõi với Skylake-client, nhưng đã bật AVX512).
Các bảng hướng dẫn của Agner Fog cho thấy độ trễ chu kỳ 15-16 trên Skylake. (Agner thường kiểm tra với một loạt các giá trị đầu vào khác nhau.) Các thử nghiệm của anh ta ít tự động hơn và đôi khi không khớp chính xác với các kết quả khác.
Điều gì làm cho một số trường hợp nhanh chóng?
Lưu ý rằng hầu hết các ISA (bao gồm x86) sử dụng dấu phẩy động nhị phân :
các bit biểu thị các giá trị dưới dạng ý nghĩa tuyến tính (hay còn gọi là mantissa) lần 2 exp và một bit dấu.
Có vẻ như chỉ có 2 tốc độ trên Intel hiện đại (ít nhất là Haswell) (Xem thảo luận với @harold trong các nhận xét.), Ví dụ như các quyền hạn của 2 đều nhanh, như 0,25, 1, 4 và 16. Những thứ này có tầm thường mantissa = 0x0 đại diện cho 1.0. https://www.h-schmidt.net/FloatConverter/IEEE754.html có một công cụ chuyển đổi mô hình bit <-> thập phân tương tác đẹp cho độ chính xác đơn, với các hộp kiểm cho các bit được đặt và chú thích về những gì mantissa và số mũ biểu thị.
Trên Skylake, các trường hợp nhanh duy nhất tôi tìm thấy trong một kiểm tra nhanh thậm chí là các quyền hạn của 2 như 4.0 nhưng không phải là 2.0. Những con số này có kết quả sqrt chính xác với cả đầu vào và đầu ra có 1.0 mantissa (chỉ bộ 1 bit ẩn). 9.0
không nhanh, mặc dù nó chính xác có thể đại diện và 3.0
kết quả cũng vậy. 3.0 có mantissa = 1.5 chỉ với bit đáng kể nhất của mantissa được đặt trong biểu diễn nhị phân. Giá trị của 9.0 là 1.125 (0b00100 ...). Vì vậy, các bit khác không rất gần với đỉnh, nhưng dường như điều đó đủ để loại bỏ nó.
( +-Inf
Và NaN
. Đang nhanh chóng, quá Vậy là số âm bình thường: kết quả = -NaN . Tôi đo 13 chu kỳ độ trễ cho những ngày i7-6700k, tương tự như đối 4.0
vs 18 chu kỳ độ trễ đối với trường hợp chậm..)
x = sqrt(x)
chắc chắn là rất nhanh với x = 1.0
(mantissa hoàn toàn bằng không ngoại trừ 1 bit dẫn đầu ngầm). Nó có một đầu vào đơn giản và đầu ra đơn giản.
Với 2.0, đầu vào cũng đơn giản (mantissa hoàn toàn bằng 0 và số mũ 1 cao hơn) nhưng đầu ra không phải là số tròn. sqrt (2) là không hợp lý và do đó có các bit khác không vô hạn trong bất kỳ cơ sở nào. Điều này rõ ràng làm cho nó chậm trên Skylake.
Các bảng hướng dẫn của Agner Fog nói rằng div
hiệu suất lệnh số nguyên của AMD K10 phụ thuộc vào số lượng bit đáng kể trong cổ tức (đầu vào), chứ không phải thương số, nhưng tìm kiếm bảng microarch pdf và bảng hướng dẫn của Agner không tìm thấy bất kỳ chú thích hay thông tin nào về cách thức cụ thể của sqrt phụ thuộc dữ liệu.
Trên các CPU cũ hơn với FP sqrt chậm hơn, có thể có nhiều chỗ hơn cho một loạt tốc độ. Tôi nghĩ rằng số lượng bit đáng kể trong lớp phủ của đầu vào có thể sẽ có liên quan. Ít bit đáng kể hơn (nhiều số 0 ở cuối có nghĩa) làm cho nó nhanh hơn, nếu điều này là chính xác. Nhưng một lần nữa, trên Haswell / Skylake, các trường hợp nhanh duy nhất dường như thậm chí là sức mạnh của 2.
Bạn có thể kiểm tra điều này với thứ gì đó kết hợp đầu ra trở lại đầu vào mà không phá vỡ sự phụ thuộc dữ liệu, ví dụ andps xmm0, xmm1
/ orps xmm0, xmm2
để đặt giá trị cố định trong xmm0 phụ thuộc vào đầu ra sqrtsd.
Hoặc một cách đơn giản hơn để kiểm tra độ trễ là tận dụng "lợi thế" của sự phụ thuộc đầu ra sai củasqrtsd xmm0, xmm1
- nó và sqrtss
để lại 64/32 bit trên (tương ứng) của đích không được sửa đổi, do đó thanh ghi đầu ra cũng là đầu vào cho việc hợp nhất đó. Tôi giả sử đây là cách mà nỗ lực nội tuyến ngây thơ của bạn đã kết thúc việc tắc nghẽn về độ trễ thay vì thông lượng với trình biên dịch chọn một thanh ghi khác cho đầu ra để nó có thể đọc lại cùng một đầu vào trong một vòng lặp. Mã asm nội tuyến mà bạn đã thêm vào câu hỏi của mình hoàn toàn bị hỏng và thậm chí sẽ không biên dịch, nhưng có lẽ mã thực của bạn được sử dụng "x"
(thanh ghi xmm) ràng buộc thay vì "i"
(ngay lập tức)?
Nguồn NASM này cho một vòng kiểm tra thực thi tĩnh (để chạy bên dưới perf stat
) sử dụng sự phụ thuộc sai đó với mã hóa không phải VEX của sqrtsd
.
Mụn cóc thiết kế này là nhờ Intel tối ưu hóa trong thời gian ngắn với SSE1 trên Pentium III. P3 xử lý các thanh ghi 128 bit bên trong như hai nửa 64 bit. Để nửa trên không thay đổi, hãy để các hướng dẫn vô hướng giải mã thành một uop duy nhất. (Nhưng điều đó vẫn mang lại cho PIII sqrtss
một sự phụ thuộc sai). AVX cuối cùng cũng cho phép chúng tôi tránh điều này với vsqrtsd dst, src,src
ít nhất là đối với các nguồn đăng ký, và tương tự vcvtsi2sd dst, cold_reg, eax
đối với các hướng dẫn vô hướng được thiết kế gần tương tự int-> fp chuyển đổi. (GCC bỏ lỡ tối ưu hóa các báo cáo: 80.586 , 89.071 , 80.571 ).
Trên nhiều CPU trước đó, thông lượng thậm chí có thể thay đổi, nhưng Skylake đã tăng cường các bộ chia đủ để bộ lập lịch luôn biết rằng nó có thể bắt đầu một chu kỳ div / sqrt 3 mới sau lần nhập chính xác đơn cuối cùng.
Ngay cả thông lượng chính xác kép của Skylake cũng có thể thay đổi: 4 đến 6 chu kỳ sau lần đầu vào chính xác kép cuối cùng, nếu bảng hướng dẫn của Agner Fog là đúng.
https://uops.info/ cho thấy một 6c phẳng đối ứng thông. (Hoặc gấp đôi thời gian cho các vectơ 256 bit; 128 bit và vô hướng có thể sử dụng một nửa các dải phân cách SIMD rộng để có thông lượng cao hơn nhưng cùng độ trễ.) Xem thêm Phân chia điểm nổi so với phép nhân điểm nổi cho một số số thông lượng / độ trễ được trích xuất từ bảng hướng dẫn của Agner Fog.