2011-08-03 25 views
8

Tôi không chắc chắn cụm từ chính xác cho những gì tôi đang cố gắng làm. Tôi có một khối 8x8 của bits được lưu trữ trong 8 bytes, mỗi byte lưu trữ một hàng. Khi tôi hoàn thành, tôi muốn mỗi byte lưu trữ một cột.Cách nhanh nhất để xoay các bit trong khối 8x8 trên bit là gì?

Ví dụ, khi tôi đang hoàn thành:

Byte0out = Byte0inBit0 + Byte1inBit0 + Byte2inBit0 + Byte3inBit0 + ... 
Byte1out = Byte0inBit1 + Byte1inBit1 + Byte2inBit1 + Byte3inBit1 + ... 

các dễ nhất cách để làm điều này trong C mà hoạt động tốt như thế nào?

+2

Vì vậy, câu trả lời phải là * nhanh nhất * hoặc * dễ nhất *? –

+2

Tôi giả sử bạn muốn Byte0Out = Byte0inBit0 + Byte1inBit0 * 2 + ... – whoplisp

+3

Thuật ngữ mà bạn đang tìm kiếm là "chuyển vị". – Damon

Trả lời

15

Mã này được cribbed trực tiếp từ "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix, Tôi sẽ không có tín dụng cho nó:

void transpose8(unsigned char A[8], int m, int n, 
       unsigned char B[8]) { 
    unsigned x, y, t; 

    // Load the array and pack it into x and y. 

    x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m]; 
    y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m]; 

    t = (x^(x >> 7)) & 0x00AA00AA; x = x^t^(t << 7); 
    t = (y^(y >> 7)) & 0x00AA00AA; y = y^t^(t << 7); 

    t = (x^(x >>14)) & 0x0000CCCC; x = x^t^(t <<14); 
    t = (y^(y >>14)) & 0x0000CCCC; y = y^t^(t <<14); 

    t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); 
    y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); 
    x = t; 

    B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x; 
    B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y; 
} 

tôi đã không kiểm tra nếu điều này quay theo hướng mà bạn cần, nếu không bạn có thể cần phải điều chỉnh mã.

Ngoài ra, hãy nhớ các kiểu dữ liệu & kích thước - int & unsigned (int) có thể không có 32 bit trên nền tảng của bạn.

BTW, tôi nghi ngờ cuốn sách (Delight Delight) là điều cần thiết cho các loại công việc bạn đang làm ... kiểm tra xem nó ra, rất nhiều thứ tuyệt vời trong đó.

+3

+1 cho câu trả lời đầu tiên tôi đã nhìn thấy đó là có liên quan đến câu hỏi của OP (nhúng). Lisp, x86 asm, và triển khai chậm chạp như là địa ngục là tất cả thay vì vô ích cho nhúng ... –

+2

Và tất nhiên để giới thiệu Delight của hacker! :-) –

+2

'm' và' n' là gì? – est

2

Điều này nghe có vẻ giống như thói quen "Chunky to planar" được sử dụng trên màn hình sử dụng bitplanes. Các liên kết sau đây sử dụng MC68K lắp ráp cho mã của nó, nhưng cung cấp một cái nhìn tổng quan tốt đẹp của vấn đề (giả sử tôi hiểu câu hỏi một cách chính xác):

http://membres.multimania.fr/amycoders/sources/c2ptut.html

1

Bạn thực sự muốn làm một cái gì đó như thế này với hướng dẫn SIMD với một cái gì đó như sự hỗ trợ GCC vector vector: http://ds9a.nl/gcc-simd/example.html

+2

Điều đó sẽ tốt đẹp, nhưng điều này cần phải chạy trên một vi điều khiển dsPIC. –

3

Lisp nguyên mẫu:

(declaim (optimize (speed 3) (safety 0))) 
(defun bit-transpose (a) 
    (declare (type (simple-array unsigned-byte 1) a)) 
    (let ((b (make-array 8 :element-type '(unsigned-byte 8)))) 
    (dotimes (j 8) 
     (dotimes (i 8) 
    (setf (ldb (byte 1 i) (aref b j)) 
      (ldb (byte 1 j) (aref a i))))) 
    b)) 

Đây là cách bạn có thể chạy mã:

#+nil 
(bit-transpose (make-array 8 :element-type 'unsigned-byte 
       :initial-contents '(1 2 3 4 5 6 7 8))) 
;; => #(85 102 120 128 0 0 0 0) 

Thỉnh thoảng tôi tháo rời mã để kiểm tra xem không có cuộc gọi không cần thiết nào với các chức năng an toàn.

#+nil 
(disassemble #'bit-transpose) 

Đây là điểm chuẩn. Chạy chức năng thường xuyên đủ để xử lý hình ảnh HDTV (nhị phân).

#+nil 
(time 
(let ((a (make-array 8 :element-type 'unsigned-byte 
       :initial-contents '(1 2 3 4 5 6 7 8))) 
     (b (make-array 8 :element-type 'unsigned-byte 
       :initial-contents '(1 2 3 4 5 6 7 8)))) 
    (dotimes (i (* (/ 1920 8) (/ 1080 8))) 
    (bit-transpose a)))) 

Điều đó chỉ mất 51ms. Lưu ý rằng tôi đang bảo vệ khá nhiều bởi vì hàm này phân bổ các mảng 8 byte mới tất cả thời gian. Tôi chắc rằng một triển khai trong C có thể được tinh chỉnh nhiều hơn nữa.

Evaluation took: 
    0.051 seconds of real time 
    0.052004 seconds of total run time (0.052004 user, 0.000000 system) 
    101.96% CPU 
    122,179,503 processor cycles 
    1,048,576 bytes consed 

Dưới đây là một số trường hợp thử nghiệm hơn:

#+nil 
(loop for j below 12 collect 
    (let ((l (loop for i below 8 collect (random 255)))) 
    (list l (bit-transpose (make-array 8 :element-type 'unsigned-byte 
       :initial-contents l))))) 
;; => (((111 97 195 202 47 124 113 164) #(87 29 177 57 96 243 111 140)) 
;;  ((180 192 70 173 167 41 30 127) #(184 212 221 232 193 185 134 27)) 
;;  ((244 86 149 57 191 65 129 178) #(124 146 23 24 159 153 35 213)) 
;;  ((227 244 139 35 38 65 214 64) #(45 93 82 4 66 27 227 71)) 
;;  ((207 62 236 89 50 64 157 120) #(73 19 71 207 218 150 173 69)) 
;;  ((89 211 149 140 233 72 193 192) #(87 2 12 57 7 16 243 222)) 
;;  ((97 144 19 13 135 198 238 33) #(157 116 120 72 6 193 97 114)) 
;;  ((145 119 3 85 41 202 79 134) #(95 230 202 112 11 18 106 161)) 
;;  ((42 153 67 166 175 190 114 21) #(150 125 184 51 226 121 68 58)) 
;;  ((58 232 38 210 137 254 19 112) #(80 109 36 51 233 167 170 58)) 
;;  ((27 245 1 197 208 221 21 101) #(239 1 234 33 115 130 186 58)) 
;;  ((66 204 110 232 46 67 37 34) #(96 181 86 30 0 220 47 10))) 

Bây giờ tôi thực sự muốn xem cách mã của tôi so với Andrejs Cainikovs' giải pháp C (Chỉnh sửa: Tôi nghĩ rằng nó sai):

#include <string.h> 

unsigned char bytes_in[8]={1,2,3,4,5,6,7,8}; 
unsigned char bytes_out[8]; 

/* please fill bytes_in[] here with some pixel-crap */ 
void bit_transpose(){ 
    memset(bytes_out, 0, 8); 
    int i,j; 
    for(i = 0; i < 8; i++) 
    for(j = 0; j < 8; j++) 
     bytes_out[i] = (bytes_out[i] << 1) | ((bytes_in[j] >> (7 - i)) & 0x01); 
} 

int 
main() 
{ 
    int j,i; 
    for(j=0;j<100;j++) 
    for(i=0;i<(1920/8*1080/8);i++) 
     bit_transpose(); 
    return 0; 
} 

Và đo điểm chuẩn:

[email protected]:~/0803/so$ gcc -O3 trans.c 
[email protected]:~/0803/so$ time ./a.out 

real 0m0.249s 
user 0m0.232s 
sys  0m0.000s 

Mỗi vòng lặp trên hình ảnh HDTV mất 2,5 mili giây. Đó là khá nhanh hơn rất nhiều so với Lisp không được tối ưu hóa của tôi.

Đáng tiếc là mã C không cho kết quả tương tự như lisp của tôi:

#include <stdio.h> 
int 
main() 
{ 
    int j,i; 
    bit_transpose(); 
    for(i=0;i<8;i++) 
    printf("%d ",(int)bytes_out[i]); 
    return 0; 
} 
[email protected]:~/0803/so$ ./a.out 
0 0 0 0 1 30 102 170 
+0

+1 cho những nỗ lực rất lớn của bạn và một lisp. Luôn luôn muốn tìm hiểu ngôn ngữ đó nhưng không bao giờ đi qua tùy biến emacs :) –

+0

Cảm ơn bạn. Một số Lisp giải trí luôn luôn là tốt đẹp như là một break từ công việc thực tế. Ngay bây giờ tôi phải đồng bộ hóa phần cứng, mà tôi bất tiện không thể thiết kế để đồng bộ hóa. May mắn thay tôi có thể sử dụng Lisp trong công việc chính của tôi là tốt :-) – whoplisp

+0

Cảm ơn những nỗ lực của bạn! Tôi đã cập nhật mã của tôi - bạn có thể vui lòng cập nhật cũng câu trả lời của bạn với sau: bytes_out [i] = (bytes_out [i] << 1) | ((bytes_in [j] >> (7 - i)) & 0x01); –

5

Nếu bạn đang tìm kiếm các giải pháp đơn giản nhất:

/* not tested, not even compiled */ 

char bytes_in[8]; 
char bytes_out[8]; 

/* please fill bytes_in[] here with some pixel-crap */ 

memset(bytes_out, 0, 8); 
for(int i = 0; i < 8; i++) { 
    for(int j = 0; j < 8; j++) { 
     bytes_out[i] = (bytes_out[i] << 1) | ((bytes_in[j] >> (7 - i)) & 0x01); 
    } 
} 

Nếu bạn đang tìm kiếm giải pháp nhanh nhất:

How to transpose a bit matrix in the assembly by utilizing SSE2.

+0

Tôi không nghĩ rằng mã của bạn thực hiện chuyển vị. Có lẽ bạn cần phải viết whoplisp

+5

Xem xét bài đăng được gắn thẻ "nhúng" và "C" và một số thứ như 99% bộ xử lý trên hành tinh KHÔNG phải là CPU x86 Pentium4 +, giải pháp ngôn ngữ lắp ráp SSE2 x86 của bạn không hữu ích nhất. Nhưng xem xét có bao nhiêu người trả lời ở đây đề cập đến SIMD, x86 ASM hoặc bất cứ điều gì, có lẽ tôi sẽ chỉ thu thập dữ liệu trở lại vào lỗ của tôi ... – Dan

+0

@ whoplist: Cảm ơn, mã cố định bằng cách thay thế

1

Nếu bạn muốn một giải pháp tối ưu hóa, bạn sẽ sử dụng các phần mở rộng SSE trong x86. Bạn cần phải sử dụng 4 trong số các mã vạch SIMD này. MOVQ - di chuyển 8 byte PSLLW - đóng gói chuyển trái từ logic PMOVMSKB - đóng gói di chuyển mặt nạ byte Và 2 thường xuyên x86 opcodes LEA - tải địa chỉ hiệu quả MOV - di chuyển

byte[] m = byte[8]; //input 
byte[] o = byte[8]; //output 
LEA ecx, [o] 
// ecx = the address of the output array/matrix 
MOVQ xmm0, [m] 
// xmm0 = 0|0|0|0|0|0|0|0|m[7]|m[6]|m[5]|m[4]|m[3]|m[2]|m[1]|m[0] 
PMOVMSKB eax, xmm0 
// eax = m[7][7]...m[0][7] the high bit of each byte 
MOV [ecx+7], al 
// o[7] is now the last column 
PSLLW xmm0, 1 
// shift 1 bit to the left 
PMOVMSKB eax, xmm0 
MOV [ecx+6], al 
PSLLW xmm0, 1 
PMOVMSKB eax, xmm0 
MOV [ecx+5], al 
PSLLW xmm0, 1 
PMOVMSKB eax, xmm0 
MOV [ecx+4], al 
PSLLW xmm0, 1 
PMOVMSKB eax, xmm0 
MOV [ecx+3], al 
PSLLW xmm0, 1 
PMOVMSKB eax, xmm0 
MOV [ecx+2], al 
PSLLW xmm0, 1 
PMOVMSKB eax, xmm0 
MOV [ecx+1], al 
PSLLW xmm0, 1 
PMOVMSKB eax, xmm0 
MOV [ecx], al 

25 x86 opcodes/hướng dẫn như trái ngược với giải pháp vòng lặp xếp chồng lên nhau ... với 64 lần lặp. Xin lỗi, ký pháp không phải là cú pháp kiểu ATT mà trình biên dịch c/C++ chấp nhận.

+0

Câu hỏi được gắn thẻ là nhúng c, có khả năng là anh ấy không làm việc trên x86 chút nào. (OTOH anh ta có thể.) –

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