2015-10-17 14 views
6

Khi biên soạn với GCC 5.2 sử dụng -std=c99, -O3, và -mavx2, các sau mẫu mã tự động vectorizes (assembly here):Làm thế nào để tự động-vectorize strided viết với GCC?

#include <stdint.h> 

void test(uint32_t *restrict a, 
      uint32_t *restrict b) { 
    uint32_t *a_aligned = __builtin_assume_aligned(a, 32); 
    uint32_t *b_aligned = __builtin_assume_aligned(b, 32); 

    for (int i = 0; i < (1L << 10); i += 2) { 
    a_aligned[i] = 42 * b_aligned[i]; 
    a_aligned[i+1] = 3 * a_aligned[i+1]; 
    } 
} 

Nhưng mẫu mã sau đây không tự động vectorize (assembly here):

#include <stdint.h> 

void test(uint32_t *restrict a, 
      uint32_t *restrict b) { 
    uint32_t *a_aligned = __builtin_assume_aligned(a, 32); 
    uint32_t *b_aligned = __builtin_assume_aligned(b, 32); 

    for (int i = 0; i < (1L << 10); i += 2) { 
    a_aligned[i] = 42 * b_aligned[i]; 
    a_aligned[i+1] = a_aligned[i+1]; 
    } 
} 

Sự khác biệt duy nhất giữa các mẫu là hệ số chia tỷ lệ là a_aligned[i+1].

Đây cũng là trường hợp của GCC 4.8, 4.9 và 5.1. Việc thêm tuyên bố volatile vào a_aligned sẽ ngăn chặn hoàn toàn tự động vector. Mẫu đầu tiên luôn chạy nhanh hơn lần thứ hai đối với chúng tôi, với tốc độ tăng lên rõ rệt hơn cho các loại nhỏ hơn (ví dụ: uint8_t thay vì uint32_t).

Có cách nào để làm mẫu mã thứ hai tự động vector hóa với GCC không?

+0

Vì vậy, sự khác biệt duy nhất là yếu tố mở rộng (3 so với không có gì)? Thử thêm 1 làm hệ số chia tỷ lệ một cách rõ ràng. Nếu nó giải quyết được, đó là lỗi trình biên dịch. – Jeff

+0

Hoặc thử nhận xét câu lệnh 'a_aligned [i + 1] = a_aligned [i + 1] ', hoặc viết lại nó thành' a_aligned [i + 1] * = 1'. Trình biên dịch có thể không biết phải làm gì với việc tự gán không-op của bạn ngoài việc làm chính xác những gì bạn đã nói để làm với nó. –

+0

@ Jeff Thật vậy, sự khác biệt duy nhất là yếu tố mở rộng quy mô. Việc thêm một đoạn mã 1 không làm cho mẫu mã thứ hai tự động-vector hóa ([assembly tại đây] (https://goo.gl/dnjSaQ)). –

Trả lời

1

Các vectorises phiên bản tiếp theo, nhưng đó là xấu xí nếu bạn hỏi tôi ...

#include <stdint.h> 

void test(uint32_t *a, uint32_t *aa, 
      uint32_t *restrict b) { 
    #pragma omp simd aligned(a,aa,b:32) 
    for (int i = 0; i < (1L << 10); i += 2) { 
    a[i] = 2 * b[i]; 
    a[i+1] = aa[i+1]; 
    } 
} 

Để biên dịch với -fopenmp và gọi với test(a, a, b).

+0

Có hai cách để tiếp cận phương pháp này. Đầu tiên là bạn mất từ ​​khóa 'limits' trên' a' và 'aa', điều này có thể khiến bạn đọc lại' aa' nhiều hơn mức bạn cần. Nó là tốt cho mã mẫu được cung cấp (trong trường hợp này GCC chi nhánh mã không vectorized nếu 'a' và' aa' bí danh một cách phiền hà), nhưng nói chung nó có thể dẫn đến nhiều lần đọc hơn cần thiết. Ví dụ, hãy xem xét nếu 'b' được thay thế bằng' a'; trong trường hợp này, có nhiều lệnh bổ sung 'vmovdq' được tạo ra không cần thiết. –

+0

Thứ hai là tại '-O3', GCC sẽ tự động inline' test() '. Có nghĩa là tại các địa điểm mà 'test()' inlines, nó sẽ nhận ra 'a' là' aa' và không tự động vector hóa. Điều này có thể được cố định với '__attribute__ ((noinline)) của GCC, nhưng điều này vẫn không cần thiết phải chịu trách nhiệm gọi hàm trên. –

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