2015-01-14 22 views
5

Tôi đang thực hiện một số phiên bản x64 với Visual C++ 2010 và masm (quy ước gọi điện nhanh).Cách điền một thanh ghi 64 bit với các giá trị byte trùng lặp

Vì vậy, chúng ta hãy nói rằng tôi có một hàm trong C++:

extern "C" void fillArray(unsigned char* byteArray, unsigned char value); 

Con trỏ đến mảng sẽ được ở RCX và giá trị char sẽ được ở DL

Làm thế nào tôi có thể điền rax với giá trị sử dụng DL như vậy rằng nếu tôi đã mov qword ptr [RCX], RAX và in byteArray, tất cả các giá trị sẽ bằng 'giá trị char'?

Xin lưu ý rằng tôi không cố gắng làm mã ngoài trình biên dịch của mình, tôi chỉ đang học.

+0

Nếu bạn muốn tìm hiểu một số MMX/SSE, có những hướng dẫn như vậy. Nhưng trong trường hợp này, nó có thể sẽ chậm hơn vì nó chỉ cho một giá trị. SSE hoạt động tốt hơn nhiều nếu bạn thực hiện các phép tính trên nhiều giá trị cùng một lúc. –

Trả lời

6

Vì bạn đã gọi thủ tục 'fillArray' của mình, tôi giả sử bạn muốn lấp đầy toàn bộ khối bộ nhớ bằng một giá trị byte. Vì vậy, tôi đã làm một so sánh về phương pháp tiếp cận khác nhau. Đó là mã masm 32 bit, nhưng kết quả sẽ giống nhau ở chế độ 64 bit. Mỗi phương pháp đều được thử nghiệm với cả bộ đệm liên kết và không thẳng hàng. Dưới đây là kết quả:

Simple REP STOSB - aligned....: 192 
Simple REP STOSB - not aligned: 192 
Simple REP STOSD - aligned....: 191 
Simple REP STOSD - not aligned: 222 
Simple while loop - aligned....: 267 
Simple while loop - not aligned: 261 
Simple while loop with different addressing - aligned....: 271 
Simple while loop with different addressing - not aligned: 262 
Loop with 16-byte SSE write - aligned....: 192 
Loop with 16-byte SSE write - not aligned: 205 
Loop with 16-byte SSE write non-temporal hint - aligned....: 126 (EDIT) 

Các biến thể ngây thơ nhất sử dụng đoạn mã sau dường như để thực hiện tốt nhất trong cả hai kịch bản và có kích thước mã nhỏ nhất cũng như:

cld 
mov al, 44h ; byte value 
mov edi, lpDst 
mov ecx, 256000*4 ; buf size 
rep stosb 

EDIT: Đây không phải là nhanh nhất cho dữ liệu được căn chỉnh. Đã thêm phiên bản MOVNTDQ hoạt động tốt nhất, xem bên dưới.

Vì lợi ích của sự hoàn chỉnh, đây là một đoạn trích từ các thói quen khác - giá trị được giả định là mở rộng sang EAX trước:

Rep Stosd:

mov edi, lpDst 
mov ecx, 256000 
rep stosd 

Simple Trong khi:

mov edi, lpDst 
mov ecx, 256000 
.while ecx>0 
    mov [edi],eax 
    add edi,4 
    dec ecx 
.endw 

Đơn giản khác nhau khi:

mov edi, lpDst 
xor ecx, ecx 
.while ecx<256000 
    mov [edi+ecx*4],eax 
    inc ecx 
.endw 

SSE (cả hai):

movd xmm0,eax 
punpckldq xmm0,xmm0 ; xxxxxxxxGGGGHHHH -> xxxxxxxxHHHHHHHH 
punpcklqdq xmm0,xmm0 ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH 
mov ecx, 256000/4 ; 16 byte 
mov edi, lpDst 
.while ecx>0 
    movdqa xmmword ptr [edi],xmm0 ; movdqu for unaligned 
    add edi,16 
    dec ecx 
.endw 

SSE (NT, thẳng hàng, EDIT):

movd xmm0,eax 
punpckldq xmm0,xmm0 ; xxxxxxxxGGGGHHHH -> xxxxxxxxHHHHHHHH 
punpcklqdq xmm0,xmm0 ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH 
mov ecx, 256000/4 ; 16 byte 
mov edi, lpDst 
.while ecx>0 
    movntdq xmmword ptr [edi],xmm0 
    add edi,16 
    dec ecx 
.endw 

Tôi đã tải lên toàn bộ mã ở đây http://pastie.org/9831404 --- gói MASM từ hutch là cần thiết cho lắp ráp .

+0

Bạn quan tâm đến việc bạn đã chạy các điểm chuẩn này trên CPU nào? Tôi quan tâm để biết liệu SB/IB/Haswell có thấy lợi ích tương tự khi sử dụng các cửa hàng phi thời gian không? –

+0

Tôi đã sử dụng CPU AMD x4 640 với DDR3-RAM tốc độ 1333. – zx485

+0

Cảm ơn - Tôi sẽ thử chạy mã của bạn trên Haswell và xem liệu nó có mang lại kết quả tương tự hay không. –

9

Bạn có thể nhân bằng 0x0101010101010101 để sao chép các byte thấp nhất vào tất cả các byte khác (giả định phần còn lại đều không để bắt đầu với), nó hơi khó chịu vì không có imul r64, r64, imm64 nhưng bạn có thể có thể làm điều này:

mov rax, 0x0101010101010101 
mul rdx 

Trên một số bộ vi xử lý, việc sử dụng imul rax, rdx nhanh hơn một chút thay vì mul rdx.

Nếu rdx không có dạng cần thiết (hay nói cách khác, nếu nó có một số bit thêm thiết lập), chỉ cần thêm một
movzx rdx, dl ở phía trước.

Nếu bạn không thích kích thước mã (mov r64, imm64 đã là 10 byte), chỉ cần dán hằng số đó vào phân đoạn dữ liệu của bạn.

+0

Rằng bit về phép nhân bằng hằng số chính xác là thứ tôi đang tìm kiếm. – Dziugas

+1

Đối với người đọc trong tương lai của q/a: phát sóng một byte vào một thanh ghi SSE có thể là một lựa chọn tốt hơn để thiết lập cho một memset (aka fillArray). Sử dụng các chỉ thị số nguyên để phát nó tới thanh ghi 32b trước (ví dụ với thủ thuật 'imul'), và sau đó thực hiện' movd' có thể có ý nghĩa, hoặc sử dụng pshufb với mặt nạ điều khiển khác 0 (bạn có thể tạo hiệu quả với pxor aka '_mm_setzero()'). –

2

cách Naive

xor rbx, rbx 
mov bl, dl 
mov bh, dl 
mov rax, rbx 
shl rbx, 16 
or rbx, rax 
mov rax, rbx 
shl rax, 32 
or rax, rbx 

Vì vậy, nó có thể là chậm hơn nhiều so với cách harold của

Bạn cũng có thể nhìn vào sản lượng lắp ráp của trình biên dịch cho đoạn mã sau

int64_t s; 
s = (s << 8) | s; 
s = (s << 16) | s; 
s = (s << 32) | s; 

gcc 4.9.0 tạo ra the following output với kết quả là rsi

mov rsi, rax 
sal rsi, 8 
or rsi, rax 
mov rax, rsi 
sal rax, 16 
or rsi, rax 
mov rax, rsi 
sal rax, 32 
or rsi, rax 
Các vấn đề liên quan