Cách nhanh nhất để tính thứ tự cường độ trong lắp ráp x86


12

Nhiệm vụ rất đơn giản: ghi lắp ráp tính toán thứ tự độ lớn của một số nguyên bằng cách sử dụng càng ít chu kỳ đồng hồ càng tốt.

  • Thứ tự cường độ được định nghĩa là log10, không log2.
  • Phạm vi của các đầu vào hợp lệ là 0để , toàn diện. Hành vi cho đầu vào bên ngoài phạm vi đó là không xác định.1012
  • Các giá trị phải được làm tròn xuống số nguyên gần nhất, ngoại trừ đầu vào đã cho đầu 0ra phải là 0. (Bạn có thể coi đây là đại diện tốt nhất cho vô cực âm có thể có trong các số nguyên không dấu).
  • Phải lắp ráp x86.
  • Số nguyên phải là giá trị thời gian chạy , không phải là số nguyên tĩnh / nội tuyến. Vì vậy, chúng tôi không biết nó là gì vào thời gian biên dịch.
  • Giả sử rằng bạn đã tải một số nguyên vào một thanh ghi. (Nhưng bao gồm thiết lập giá trị trong thanh ghi trong câu trả lời cho rõ ràng).
  • Không thể gọi bất kỳ thư viện hoặc chức năng bên ngoài.
  • Miễn phí sử dụng bất kỳ hướng dẫn có sẵn nào trong tài liệu Intel .
  • Không có C.
  • Bất kỳ kiến ​​trúc nào trong số 7 kiến ​​trúc Intel Core đều được chấp nhận (được liệt kê trên trang 10 ). Lý tưởng nhất là Nehalem (Intel Core i7).

Câu trả lời chiến thắng là câu trả lời sử dụng ít chu kỳ đồng hồ nhất có thể. Đó là, nó có thể có nhiều hoạt động nhất mỗi giây. Các bản tóm tắt chu kỳ đồng hồ gần đúng có tại đây: http://www.agner.org/optizes/in cản_tables.pdf . Tính chu kỳ đồng hồ có thể xảy ra sau khi câu trả lời được đăng.


'FYL2X' và các hướng dẫn khác của FPU có được phép không?
Chấn thương kỹ thuật số

1
Kết quả có nên là một số nguyên? Nếu vậy, nó nên được làm tròn như thế nào?
Chấn thương kỹ thuật số

3
Vì vậy, đầu vào và đầu ra là cả hai số nguyên, có? Nhưng đầu vào có thể lớn bằng 10 ^ 12, vì vậy nó quá lớn cho int 32 bit. Vì vậy, chúng ta giả sử đầu vào số nguyên 64 bit?
Paul R

3
Là tiêu chí chiến thắng dựa trên số chu kỳ tối đa hoặc trung bình? Và nếu nó ở mức trung bình, phân phối đầu vào là gì?
Runer112 ngày

2
Bộ xử lý nào được nhắm mục tiêu? Tài liệu được liên kết liệt kê hơn 20 quy trình khác nhau (AMD, Intel, các quy trình khác) và có sự khác biệt đáng kể về độ trễ.
Chấn thương kỹ thuật số

Câu trả lời:


8

7 chu kỳ, thời gian không đổi

Đây là một giải pháp dựa trên câu trả lời của tôi cho Câu hỏi SO này . Nó sử dụng BSR để đếm số lượng bit cần thiết để giữ số. Nó tìm kiếm có bao nhiêu chữ số thập phân cần thiết để đại diện cho số lớn nhất mà nhiều bit có thể giữ. Sau đó, nó trừ đi 1 nếu số thực tế nhỏ hơn công suất 10 gần nhất với nhiều chữ số đó.

    .intel_syntax noprefix
    .globl  main
main:
    mov rdi, 1000000000000              #;your value here
    bsr rax, rdi
    movzx   eax, BYTE PTR maxdigits[1+rax]
    cmp rdi, QWORD PTR powers[0+eax*8]
    sbb al, 0
    ret
maxdigits:
    .byte   0
    .byte   0
    .byte   0
    .byte   0
    .byte   1
    .byte   1
    .byte   1
    .byte   2
    .byte   2
    .byte   2
    .byte   3
    .byte   3
    .byte   3
    .byte   3
    .byte   4
    .byte   4
    .byte   4
    .byte   5
    .byte   5
    .byte   5
    .byte   6
    .byte   6
    .byte   6
    .byte   6
    .byte   7
    .byte   7
    .byte   7
    .byte   8
    .byte   8
    .byte   8
    .byte   9
    .byte   9
    .byte   9
    .byte   9
    .byte   10
    .byte   10
    .byte   10
    .byte   11
    .byte   11
    .byte   11
    .byte   12
powers:
    .quad   0
    .quad   10
    .quad   100
    .quad   1000
    .quad   10000
    .quad   100000
    .quad   1000000
    .quad   10000000
    .quad   100000000
    .quad   1000000000
    .quad   10000000000
    .quad   100000000000
    .quad   1000000000000

Biên dịch trên GCC 4.6.3 cho Ubuntu và trả về giá trị trong mã thoát.

Tôi không tự tin diễn giải rằng chu kỳ bảng cho bất kỳ bộ xử lý hiện đại nào, nhưng sử dụng phương pháp của @ DigitalTrauma, trên bộ xử lý Nehalim, tôi có được 7 không?

ins        uOps Latency
---        -    - 
BSR r,r  : 1    3
MOVZX r,m: 1    -
CMP m,r/i: 1    1 
SBB r,r/i: 2    2

Ồ - đã thấy DigitalTrauma sau khi tôi bắt đầu viết. Ý tưởng tương tự. Sử dụng phương pháp đếm của anh ấy tôi nghĩ nhận được 3,1,1,1 = 6 cho BSR, MOV, CMP, SBB
AShelly

Đúng, tôi nghĩ rằng nhịp đập của bạn. Chỉ cần đi để hiển thị a) Tôi không phải là lập trình viên lắp ráp và b) lắp ráp tốt nhất chỉ còn lại một mình chúng ta ;-)
Chấn thương kỹ thuật số

The integer must be a runtime value, not a static/inline integer. So we don't know what it is at compile time.
con mèo

1
bên phải và dòng tiếp theo nói: "Giả sử rằng bạn đã tải một số nguyên vào một thanh ghi. (Nhưng bao gồm thiết lập giá trị trong thanh ghi trong câu trả lời cho rõ ràng)." Đó là những gì tôi đã làm.
HỎI

thay thế Movzx eax bằng Mov al. 24 bit trên cùng của eax sẽ là 0, vì vậy zx là dự phòng (và nó đắt tiền).
perr ferrie

6

Trường hợp tốt nhất 8 chu kỳ, trường hợp xấu nhất 12 chu kỳ

Vì không rõ ràng trong câu hỏi, tôi dựa trên cơ sở độ trễ của cầu Ivy.

Cách tiếp cận ở đây là sử dụng bsrhướng dẫn (quét ngược bit) như log2 () của người nghèo. Kết quả được sử dụng như một chỉ mục trong bảng nhảy chứa các mục nhập cho các bit từ 0 đến 42. Tôi giả sử rằng hoạt động trên dữ liệu 64 bit là hoàn toàn bắt buộc, sau đó sử dụng bsrhướng dẫn là OK.

Trong trường hợp đầu vào tốt nhất, mục có thể jumptable có thể ánh xạ bsrkết quả trực tiếp đến độ lớn. ví dụ: Đối với các đầu vào trong phạm vi 32-63, bsrkết quả sẽ là 5, được ánh xạ tới cường độ 1. Trong trường hợp này, đường dẫn lệnh là:

Instruction    Latency

bsrq                 3
jmp                  2
movl                 1
jmp                  2

total                8

Trong trường hợp đầu vào tồi tệ nhất, bsrkết quả sẽ ánh xạ tới hai cường độ có thể, do đó, mục nhập có thể làm được thêm một lần nữa cmpđể kiểm tra xem đầu vào có> 10 n không . Ví dụ: đối với các đầu vào trong phạm vi 64-127, bsrkết quả sẽ là 6. Mục nhập có thể nhảy tương ứng sau đó kiểm tra xem đầu vào> 100 và đặt cường độ đầu ra tương ứng.

Ngoài đường dẫn trường hợp xấu nhất, chúng tôi có một lệnh Mov bổ sung để tải giá trị tức thời 64 bit để sử dụng trong cmp, vì vậy đường dẫn lệnh trường hợp xấu nhất là:

Instruction    Latency

bsrq                 3
jmp                  2
movabsq              1
cmpq                 1
ja                   2
movl                 1
jmp                  2

total               12

Đây là mã:

    /* Input is loaded in %rdi */
    bsrq    %rdi, %rax
    jmp *jumptable(,%rax,8)
.m0:
    movl    $0, %ecx
    jmp .end
.m0_1:
    cmpq    $9, %rdi
    ja  .m1
    movl    $0, %ecx
    jmp .end
.m1:
    movl    $1, %ecx
    jmp .end
.m1_2:
    cmpq    $99, %rdi
    ja  .m2
    movl    $1, %ecx
    jmp .end
.m2:
    movl    $2, %ecx
    jmp .end
.m2_3:
    cmpq    $999, %rdi
    ja  .m3
    movl    $2, %ecx
    jmp .end
.m3:
    movl    $3, %ecx
    jmp .end
.m3_4:
    cmpq    $9999, %rdi
    ja  .m4
    movl    $3, %ecx
    jmp .end
.m4:
    movl    $4, %ecx
    jmp .end
.m4_5:
    cmpq    $99999, %rdi
    ja  .m5
    movl    $4, %ecx
    jmp .end
.m5:
    movl    $5, %ecx
    jmp .end
.m5_6:
    cmpq    $999999, %rdi
    ja  .m6
    movl    $5, %ecx
    jmp .end
.m6:
    movl    $6, %ecx
    jmp .end
.m6_7:
    cmpq    $9999999, %rdi
    ja  .m7
    movl    $6, %ecx
    jmp .end
.m7:
    movl    $7, %ecx
    jmp .end
.m7_8:
    cmpq    $99999999, %rdi
    ja  .m8
    movl    $7, %ecx
    jmp .end
.m8:
    movl    $8, %ecx
    jmp .end
.m8_9:
    cmpq    $999999999, %rdi
    ja  .m9
    movl    $8, %ecx
    jmp .end
.m9:
    movl    $9, %ecx
    jmp .end
.m9_10:
    movabsq $9999999999, %rax
    cmpq    %rax, %rdi
    ja  .m10
    movl    $9, %ecx
    jmp .end
.m10:
    movl    $10, %ecx
    jmp .end
.m10_11:
    movabsq $99999999999, %rax
    cmpq    %rax, %rdi
    ja  .m11
    movl    $10, %ecx
    jmp .end
.m11:
    movl    $11, %ecx
    jmp .end
.m11_12:
    movabsq $999999999999, %rax
    cmpq    %rax, %rdi
    ja  .m12
    movl    $11, %ecx
    jmp .end
.m12:
    movl    $12, %ecx
    jmp .end

jumptable:
    .quad   .m0
    .quad   .m0
    .quad   .m0
    .quad   .m0_1
    .quad   .m1
    .quad   .m1
    .quad   .m1_2
    .quad   .m2
    .quad   .m2
    .quad   .m2_3
    .quad   .m3
    .quad   .m3
    .quad   .m3
    .quad   .m3_4
    .quad   .m4
    .quad   .m4
    .quad   .m4_5
    .quad   .m5
    .quad   .m5
    .quad   .m5_6
    .quad   .m6
    .quad   .m6
    .quad   .m6
    .quad   .m6_7
    .quad   .m7
    .quad   .m7
    .quad   .m7_8
    .quad   .m8
    .quad   .m8
    .quad   .m8_9
    .quad   .m9
    .quad   .m9
    .quad   .m9
    .quad   .m9_10
    .quad   .m10
    .quad   .m10
    .quad   .m10_11
    .quad   .m11
    .quad   .m11
    .quad   .m11_12
    .quad   .m12
    .quad   .m12
    .quad   .m12

.end:
/* output is given in %ecx */

Điều này chủ yếu được tạo ra từ đầu ra trình biên dịch mã gcc cho mã C bằng chứng khái niệm tôi đã viết . Lưu ý mã C sử dụng một goto có thể tính toán để thực hiện bảng nhảy. Nó cũng sử dụng __builtin_clzll()gcc dựng sẵn, biên dịch theo bsrhướng dẫn (cộng với một xor).


Tôi đã xem xét một số giải pháp trước khi đến với giải pháp này:

  • FYL2Xđể tính log tự nhiên, sau đó FMULbằng hằng số cần thiết. Điều này có lẽ sẽ giành chiến thắng này nếu nó là một cuộc thi [tag: hướng dẫn: golf]. Nhưng FYL2Xcó độ trễ là 90-106 cho cầu Ivy.

  • Tìm kiếm nhị phân mã hóa cứng. Điều này thực sự có thể cạnh tranh - Tôi sẽ để nó cho người khác thực hiện :).

  • Hoàn thành bảng tra cứu kết quả. Điều này tôi chắc chắn là nhanh nhất về mặt lý thuyết, nhưng sẽ yêu cầu bảng tra cứu 1TB - chưa thực tế - có thể trong vài năm nữa nếu Định luật Moore tiếp tục giữ.


Nếu cần tôi có thể tính độ trễ trung bình cho tất cả các đầu vào được phép.
Chấn thương kỹ thuật số

jmpjcckhông có độ trễ, chỉ có chi phí thông qua. Dự đoán chi nhánh + thực thi đầu cơ có nghĩa là phụ thuộc kiểm soát không phải là một phần của chuỗi phụ thuộc dữ liệu.
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.