Bài đăng này liên quan chặt chẽ đến một bài đăng khác mà tôi đã đăng some days ago. Lần này, tôi đã viết một mã đơn giản chỉ cần thêm một mảng các phần tử, nhân kết quả bằng các giá trị trong mảng khác và lưu nó trong một mảng thứ ra, tất cả các biến floating point double precision typed.Tối ưu hóa mã SSE của GCC
Tôi đã tạo hai phiên bản của mã đó: một phiên bản có hướng dẫn SSE, sử dụng cuộc gọi đến và một lệnh khác mà không có chúng, sau đó chúng được biên dịch bằng mức tối ưu hóa gcc và -O0. Tôi viết chúng dưới đây:
// SSE VERSION
#define N 10000
#define NTIMES 100000
#include <time.h>
#include <stdio.h>
#include <xmmintrin.h>
#include <pmmintrin.h>
double a[N] __attribute__((aligned(16)));
double b[N] __attribute__((aligned(16)));
double c[N] __attribute__((aligned(16)));
double r[N] __attribute__((aligned(16)));
int main(void){
int i, times;
for(times = 0; times < NTIMES; times++){
for(i = 0; i <N; i+= 2){
__m128d mm_a = _mm_load_pd(&a[i]);
_mm_prefetch(&a[i+4], _MM_HINT_T0);
__m128d mm_b = _mm_load_pd(&b[i]);
_mm_prefetch(&b[i+4] , _MM_HINT_T0);
__m128d mm_c = _mm_load_pd(&c[i]);
_mm_prefetch(&c[i+4] , _MM_HINT_T0);
__m128d mm_r;
mm_r = _mm_add_pd(mm_a, mm_b);
mm_a = _mm_mul_pd(mm_r , mm_c);
_mm_store_pd(&r[i], mm_a);
}
}
}
//NO SSE VERSION
//same definitions as before
int main(void){
int i, times;
for(times = 0; times < NTIMES; times++){
for(i = 0; i < N; i++){
r[i] = (a[i]+b[i])*c[i];
}
}
}
Khi biên dịch chúng với -O0, gcc tận dụng XMM/ghi MMX và SSE intstructions, nếu không được đưa ra cụ thể là tùy chọn -mno-SSE (và những người khác). Tôi đã kiểm tra mã lắp ráp được tạo cho mã thứ hai và tôi nhận thấy rằng mã này sử dụng các mã số movsd, addsd và mulsd. Vì vậy, nó làm cho việc sử dụng các lệnh SSE nhưng chỉ có những người sử dụng phần thấp nhất của thanh ghi, nếu tôi không sai. Mã lắp ráp được tạo cho mã C đầu tiên được sử dụng, như mong đợi, của các hướng dẫn thêm addp và mulpd, mặc dù mã lắp ráp lớn hơn đã được tạo.
Dù sao, mã đầu tiên sẽ nhận được lợi nhuận tốt hơn, theo như tôi biết, của mô hình SIMD, vì mỗi lần lặp lại hai giá trị kết quả được tính toán. Tuy nhiên, mã thứ hai thực hiện một cái gì đó nhanh hơn 25 phần trăm so với cái đầu tiên. Tôi cũng đã thực hiện một thử nghiệm với các giá trị độ chính xác đơn và nhận được kết quả tương tự. Lý do cho điều đó là gì?
So sánh hiệu suất khi biên dịch mà không tối ưu hóa là vô nghĩa. – interjay
Bạn đang làm 3 x tải và 1 x lưu trữ chỉ với 2 phép tính số học x, vì vậy bạn rất có thể sẽ bị giới hạn băng thông. –
Điều gì sẽ xảy ra khi bạn xóa lệnh gọi _mm_prefetch? Tôi nghĩ rằng họ có thể làm tổn thương bạn – TJD