2010-10-21 43 views
6

Tôi có chức năng nút cổ chai sau.Làm cách nào để tối ưu hóa chu kỳ?

typedef unsigned char byte; 
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3) 
{ 
    const byte b1 = 128-30; 
    const byte b2 = 128+30; 
    for (const byte * p1 = p1Start; p1 != p1End; ++p1, ++p2, ++p3) { 
     *p3 = (*p1 < *p2) ? b1 : b2; 
    } 
} 

Tôi muốn thay thế C++ mã bằng SSE2 chức năng nội tại. Tôi đã thử _mm_cmpgt_epi8 nhưng nó đã sử dụng so sánh đã ký. Tôi cần so sánh không dấu.

Có thủ thuật nào (SSE, SSE2, SSSE3) để giải quyết sự cố của tôi không?

Lưu ý: Tôi không muốn sử dụng đa luồng trong trường hợp này.

+0

Bạn có biết kiến ​​trúc vi xử lý nào bạn đang nhắm mục tiêu không? Làm việc với một đoạn từ 64 bit tại một thời điểm (bit twiddling để thực hiện các so sánh trong đăng ký) có thể làm giảm ganh đua bus bộ nhớ một chút. Mã lắp ráp của trình biên dịch sẽ giúp cung cấp ý tưởng ... ... và không phải là SSE dành cho dấu phẩy động, không phải là số nguyên? –

+0

SSE có một số hướng dẫn số nguyên. – Crashworks

+1

Tại sao không làm cho họ ký? một XOR 0x80 đơn giản với mỗi phần tử trước khi so sánh sẽ thực hiện công việc. – ruslik

Trả lời

9

Thay vì bù đắp giá trị ký của bạn để làm cho họ unsigned, một cách nhẹ hiệu quả hơn sẽ được thực hiện như sau:

  • sử dụng _mm_min_epu8 để có được phút unsigned của p1, p2
  • so sánh phút này bình đẳng với p2 bằng _mm_cmpeq_epi8
  • nay là mặt nạ kết quả sẽ là 0x00 cho các yếu tố nơi p1 p2 < và 0xff cho các yếu tố nơi p1> = p2
  • bây giờ bạn có thể sử dụng khẩu trang này với _mm_or_si128_mm_andc_si128 để chọn các giá trị b1/b2 thích hợp

Lưu ý rằng đây là 4 hướng dẫn, so với 5 sử dụng phương pháp so sánh ký hiệu bù trừ +.

1

Có, SSE sẽ không hoạt động tại đây. Bạn có thể cải thiện hiệu suất mã này trên máy tính đa lõi bằng cách sử dụng OpenMP:

 
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3) 
{ 
    const byte b1 = 128-30; 
    const byte b2 = 128+30; 

    int n = p1End - p1Start; 
    #pragma omp parallel for 
    for (int i = 0; i < n; ++p1, ++i) 
    { 
     p3[i] = (p1[i] < p2[i]) ? b1 : b2; 
    } 
} 
+3

Điều này sẽ không hoạt động đối với lõi đơn hoặc CPU đơn –

+0

@ VJo - vâng, tất nhiên. Trên máy tính lõi đơn, mã này thực hiện chính xác như mã ban đầu từ câu hỏi. –

+1

@VJo nó sẽ hoạt động nhưng không cho bất kỳ tăng – Andrey

-3

sử dụng pcmpeqb và được sức mạnh với bạn.

+1

'pcmpeqb' là một kiểm tra cho bình đẳng. Tôi cần ít so sánh hơn. –

+0

ah vâng. sau đó pcmpgtb. vẫn sử dụng Power. nhưng một cách khôn ngoan. –

+2

OP cần so sánh chưa ký. –

2

Bạn có thể trừ 127 từ số của bạn, và sau đó sử dụng _mm_cmpgt_epi8

+2

Có vẻ như câu trả lời đúng. Nhưng tôi nghĩ rằng 127 của bạn phải được thay thế bằng 128. Hoặc xor với 128. –

+0

Vấn đề là tôi nghĩ rằng chỉ có một đóng gói thêm trong MMX, mà là một đăng ký khác nhau thiết lập hoàn toàn. – Crashworks

+0

vâng, bạn nói đúng. 128, không 127 –

2

Vâng, điều này có thể được thực hiện trong SIMD, nhưng nó sẽ mất một vài bước để làm mặt nạ.

Ruslik hiểu đúng, tôi nghĩ vậy. Bạn muốn xor mỗi thành phần với 0x80 để lật cảm giác so sánh đã ký và chưa ký. _mm_xor_si128 (PXOR) giúp bạn điều đó - bạn sẽ cần tạo mặt nạ làm mảng char tĩnh ở đâu đó trước khi tải nó vào thanh ghi SIMD. Sau đó, _mm_cmpgt_epi8 đưa cho bạn một mặt nạ và bạn có thể sử dụng bitwise AND (ví dụ: _mm_and_si128) để thực hiện thao tác đeo mặt nạ.

-1

Thật không may, nhiều câu trả lời ở trên không chính xác. Giả sử từ 3 bit:

chưa ký: 4 5 6 7 0 1 2 3 == đã ký: -4 -3 -2 -1 0 1 2 3 (bit: 100 101 110 111 000 001 010 011)

Phương pháp của Paul R không chính xác. Giả sử chúng ta muốn biết nếu 3> 2. min (3,2) == 2, điều này gợi ý có, do đó phương thức hoạt động ở đây. Bây giờ giả sử chúng ta muốn biết nếu 7> 2. Giá trị 7 là -1 trong biểu diễn đã ký, vì vậy min (-1,2) == -1, cho thấy sai rằng 7 không lớn hơn 2 unsigned.

Phương thức của Andrey cũng không chính xác. Giả sử chúng ta muốn biết nếu 7> 2, hay a = 7, và b = 2. Giá trị 7 là -1 trong biểu diễn đã ký, vì vậy thuật ngữ đầu tiên (a> b) không thành công, và phương pháp gợi ý rằng 7 không lớn hơn hơn 2.

Tuy nhiên, phương pháp của BJobnh, được sửa bởi Alexey, là chính xác. Chỉ cần trừ 2^(n-1) từ các giá trị, trong đó n là số bit. Trong trường hợp này, chúng tôi sẽ trừ 4 để có được các giá trị tương ứng mới:

ký cũ: -4 -3 -2 -1 0 1 2 3 => ký mới: 0 1 2 3 -4 -3 -2 -1 == new unsigned 0 1 2 3 4 5 6 7.

Nói cách khác, unsigned_greater_than (a, b) tương đương với signed_greater_than (a - 2^(n-1), b - 2^(n-1)).

+0

Nếu bạn nhìn kỹ vào câu trả lời của tôi, bạn sẽ thấy tôi đang sử dụng thao tác * unsigned * min. –