2013-12-11 10 views
5

Tôi có biểu thức boolean mà tôi đã quản lý để triển khai trong SSE2. Bây giờ tôi đã có thể muốn thử thực hiện nó trong AVX khai thác một yếu tố bổ sung 2 trong sự gia tăng song song (từ 128 bit SIMD loại 256). Tuy nhiên, AVX không hỗ trợ hoạt động số nguyên (mà AVX2 làm, nhưng tôi đang làm việc trên một bộ xử lý Sandy Bridge vì vậy nó không phải là một tùy chọn hiện tại). Tuy nhiên, vì có AVX intrinsics for bitwise operations. Tôi figured tôi có thể làm cho một thử bằng cách chỉ cần chuyển đổi các loại số nguyên của tôi để các loại phao và xem nếu nó hoạt động.Có cách nào để mô phỏng các hoạt động bitwise số nguyên cho các loại _m256 trên AVX không?

Thử nghiệm đầu tiên là một thành công:

__m256 ones = _mm256_set_ps(1,1,1,1,1,1,1,1); 
__m256 twos = _mm256_set_ps(2,2,2,2,2,2,2,2); 
__m256 result = _mm256_and_ps(ones, twos); 

Tôi guetting tất cả 0 như tôi đang nghĩ đến. Simularly AND'ing twos thay vào đó tôi nhận được một kết quả của 2. Nhưng khi cố gắng 11 XOR 4 cho phù hợp:

__m256 elevens = _mm256_set_ps(11,11,11,11,11,11,11,11); 
__m256 fours = _mm256_set_ps(4,4,4,4,4,4,4,4); 
__m256 result2 = _mm256_xor_ps(elevens, fours); 

Kết quả là 6.46e-46 (tức là gần 0) chứ không phải 15. Simularly làm 11 HOẶC 4 cho tôi một giá trị 22 và không phải là 15 như nó phải được. Tôi không hiểu tại sao điều này lại xảy ra. Nó là một lỗi hoặc một số cấu hình tôi đang thiếu? Tôi đã thực sự mong đợi giả thuyết của tôi làm việc với phao như thể chúng là số nguyên không hoạt động vì số nguyên được khởi tạo cho một giá trị float có thể không thực sự là giá trị chính xác mà là xấp xỉ gần đúng. Nhưng ngay cả sau đó, tôi ngạc nhiên bởi kết quả tôi nhận được.

Có ai có giải pháp cho vấn đề này hay tôi phải nâng cấp CPU của mình để nhận hỗ trợ AVX2 cho phép điều này?

+0

Có vẻ như bạn đang in một số nguyên làm phao để nhận được 6.46e-46. Bạn có chắc các định dạng định dạng 'printf()' của bạn là chính xác không? –

+0

Tôi không in. Tôi vừa kiểm tra giá trị trong trình gỡ rối Visual Studio. – Toby999

Trả lời

7

Thử nghiệm đầu tiên được thực hiện do tai nạn.

1 dưới dạng phao là 0x3f800000, 2 là 0x40000000. Nói chung, nó sẽ không hoạt động theo cách đó.

Nhưng bạn hoàn toàn có thể làm điều đó, bạn chỉ cần đảm bảo rằng bạn đang làm việc với đúng mẫu bit. Không chuyển đổi số nguyên của bạn sang phao nổi - diễn giải lại chúng. Điều đó tương ứng với nội tại như _mm256_castsi256_ps, hoặc lưu trữ ints của bạn vào bộ nhớ và đọc chúng dưới dạng phao (không thay đổi chúng, nói chung chỉ hoạt động toán học quan tâm đến những gì nổi có nghĩa là, phần còn lại hoạt động với các mẫu bit thô, kiểm tra danh sách các ngoại lệ mà lệnh có thể thực hiện để đảm bảo).

+0

Aha. Cảm ơn. Điều đó có ý nghĩa. Tôi cung cấp cho nó một thử và đánh dấu câu trả lời của bạn là chính xác nếu nó hoạt động. – Toby999

+2

@ Toby999 Nhưng lưu ý rằng trên tất cả các bộ vi xử lý hiện tại của Intel, các phiên bản dấu chấm động của các hướng dẫn logic bitwise chỉ có 1/3 thông lượng là các phiên bản nguyên. Vì vậy, nếu bạn đang làm điều này cho hiệu suất, bạn có thể muốn suy nghĩ hai lần. Nó thậm chí có thể backfire trừ khi bạn bị giới hạn bởi băng thông bộ giải mã. – Mysticial

+2

Trên Sandy và Ivy Bridge, số nguyên SSE bitwise logic có thể đi đến bất kỳ cổng 0, 1 hoặc 5 tại một/chu kỳ. Đó là 3 mỗi chu kỳ.Nhưng logic bitwise SSE dấu phẩy động chỉ có thể chuyển đến cổng 5 tại một/chu kỳ. Vì vậy, nó được giới hạn 1 cho mỗi chu kỳ. Trên Haswell, nó giống nhau, nhưng nó có AVX2 - làm cho điểm moot. – Mysticial

4

Bạn không cần AVX2 để sử dụng tải số nguyên AVX và hoạt động lưu trữ: xem intel intrinsic guide. Vì vậy, bạn có thể tải các số nguyên của bạn bằng cách sử dụng AVX, reinterpret-cast để float, sử dụng các hoạt động bitwise float, và sau đó reinterpret-cast trở lại int. Các diễn giải lại-phôi không tạo ra bất kỳ hướng dẫn, họ chỉ làm cho trình biên dịch hạnh phúc. Hãy thử điều này:

//compiled and ran on an Ivy Bridge system with AVX but without AVX2 
#include <stdio.h> 
#include <immintrin.h> 
int main() { 
    int a[8] = {0, 2, 4, 6, 8, 10, 12, 14}; 
    int b[8] = {1, 1, 1, 1, 1, 1, 1, 1}; 
    int c[8]; 

    __m256i a8 = _mm256_loadu_si256((__m256i*)a); 
    __m256i b8 = _mm256_loadu_si256((__m256i*)b); 
    __m256i c8 = _mm256_castps_si256(
     _mm256_or_ps(_mm256_castsi256_ps(a8), _mm256_castsi256_ps(b8))); 
    _mm256_storeu_si256((__m256i*)c, c8); 
    for(int i=0; i<8; i++) printf("%d ", c[i]); printf("\n"); 
    //output: 1 3 5 7 9 11 13 15 
} 

Tất nhiên, như Mystical đã chỉ ra điều này có thể không đáng làm nhưng điều đó không có nghĩa là bạn không thể làm được.

+0

Cảm ơn bạn đã đầu vào của bạn. Nó rất hữu ích vì nó là tốn thời gian đào ra các phương pháp nội tại chính xác. – Toby999

+0

có các tùy chọn để căn chỉnh các biến vì vậy bạn không cần phải đối phó với các tải chưa xếp hàng –

+0

@ LưuVĩnhPhúc, tôi đã làm việc với Giả sử rằng nó không quan trọng nữa, thông lượng và độ trễ của các hướng dẫn tải/lưu trữ được căn chỉnh và không thẳng hàng là giống nhau trên bộ nhớ liên kết, đó là lý thuyết Nhưng trong thực tế tôi vẫn thấy sự khác biệt nên tôi đồng ý với bạn rằng hướng dẫn tải phù hợp nên được sử dụng. –

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