cuốn sách gpt4 ai đã làm

Xấp xỉ nhanh hơn căn bậc hai nghịch đảo của một mảng

In lại Tác giả: Vũ trụ không gian Thời gian cập nhật: 2023-11-03 10:25:07 25 4
mua khóa gpt4 Nike

Làm cách nào để tính căn bậc hai nghịch đảo gần đúng của một mảng nhanh hơn trên CPU với popcnt và SSE4.2?

Đầu vào là một số nguyên dương (từ 0 đến xấp xỉ 200.000) được lưu trữ trong một nhóm float.

Đầu ra là một mảng float.

Cả hai mảng đều được căn chỉnh bộ nhớ phù hợp cho sse.

Đoạn mã sau chỉ sử dụng thanh ghi 1 xmm và chạy trên linux.gcc -O3 code.cpp -lrt -msse4.2biên soạn

Cảm ơn.

#include 
#include
#include

using namespace std;
void print_xmm(__m128 xmm){
trôi ra ngoài[4];
_mm_storeu_ps(out,xmm);
int i;
for (i = 0; i < 4; ++i) std::cout << out[i] << " ";
std::cout << std::endl;
}

void print_arr(float* ptr, size_t size){
size_t tôi;
for(i = 0; i < size; ++i){
cout << ptr[i] << " ";
}
cout << endl;
}

int chính(void){
size_t kích thước = 25000 * 4;
// cái này phải là bội số của 4
size_t lặp lại = 10000;
// kiểm tra 10000 chu kỳ của mã
float* ar_in = (float*)aligned_alloc(16, size*sizeof(float));
float* ar_out = (float*)aligned_alloc(16, size*sizeof(float));
// điền dữ liệu thử nghiệm vào mảng đầu vào
//dữ liệu là một mảng số dương.
size_t tôi;
for (i = 0; i < size; ++i){
ar_in[i] = (i+1) * (i+1);
}
// chuẩn bị cho căn bậc hai nghịch đảo.
__m128 xmm0;
size_t size_fix = size*sizeof(float)/sizeof(__m128);
float* ar_in_end = ar_in + size_fix;
float* ar_out_now;
float* ar_in_now;
// thời gian
cấu trúc timespec tp_start, tp_end;
tôi = lặp lại;
clock_gettime(CLOCK_MONOTONIC, &tp_start);
//bắt đầu tính thời gian
trong khi(--i){
ar_out_now = ar_out;
for(ar_in_now = ar_in;
ar_in_now != ar_in_end;
ar_in_now += 4, ar_out_now+=4){
//4 = sizeof(__m128)/sizeof(float);
xmm0 = _mm_load_ps(ar_in_now);
//cout << "tải xmm: ";
//print_xmm(xmm0);
xmm0 = _mm_rsqrt_ps(xmm0);
//cout << "rsqrt xmm: ";
//print_xmm(xmm0);
_mm_store_ps(ar_out_now,xmm0);
}
}
// thời điểm kết thúc
clock_gettime(CLOCK_MONOTONIC, &tp_end);
thời gian gấp đôi;
const kép nano = 0,000000001;

thời gian = ((double)(tp_end.tv_sec - tp_start.tv_sec )
+ (tp_end.tv_nsec - tp_start.tv_nsec) * nano)/lặp lại;

cout << " thời gian trên mỗi chu kỳ: " << thời gian << endl;
/*
cout << "mảng đầu vào: ";
print_arr(ar_in, size);
cout << "mảng đầu ra: ";
print_arr(ar_out,size);
*/
//miễn phí cho tôi
miễn phí(ar_in);
miễn phí(ar_out);
return 0;
}

câu trả lời hay nhất

Nhóm nổi của bạn lớn đến mức nào? Nếu nó đã nóng ở L1 (hoặc có thể là L2), thì đầu ra gcc5.3 của mã này sẽ trở thành nút cổ chai đối với thông lượng uop trên các CPU Intel hiện đại, vì nó sử dụng 6 uops miền hợp nhất cho vòng lặp, được thực thi trên mỗi lần lặp một vectơ. (Vì vậy nó sẽ chạy với tốc độ 1 vector cứ sau 2 chu kỳ).

Để đạt được thông lượng 1 vectơ trên mỗi xung nhịp trên các CPU Intel hiện đại,Bạn cần hủy bỏ vòng lặp(Xem bên dưới để biết lý do tại sao asm chưa được mở rộng không hoạt động). Sẽ rất tốt nếu có trình biên dịch làm điều đó cho bạn (thay vì thực hiện thủ công trong mã nguồn C++). Ví dụ: sử dụng tệp cấu hình để hướng dẫn tối ưu hóa ( gcc -fprofile-sử dụng ), hoặc chỉ sử dụng một cách mù quáng -funroll-vòng lặp .


Về lý thuyết, 16 byte mỗi xung nhịp là đủ để bão hòa băng thông bộ nhớ chính của lõi. Tuy nhiên, IIRC Z Boson nhận thấy băng thông tốt hơn khi sử dụng nhiều lõi, có thể là do nhiều lõi chứa nhiều yêu cầu chưa xử lý hơn và tình trạng treo trên một lõi không khiến bộ nhớ trống. Tuy nhiên, nếu đầu vào ở L2 của lõi bị nóng thì tốt hơn nên sử dụng lõi đó để xử lý dữ liệu.

Trên Haswell trở lên, 16 byte mỗi lần tải và lưu trữ xung nhịp chỉ bằng một nửa băng thông bộ đệm L1, vì vậy bạn cần có phiên bản AVX để tối đa hóa băng thông trên mỗi lõi.

Nếu có tắc nghẽn bộ nhớ,bạn có thể muốn thực hiện phép lặp Newton-Raphson để có được độ chính xác gần như đầy đủ 1/sqrt(x) , đặc biệt khi bạn sử dụng nhiều luồng cho một mảng lớn.(Bởi vì sẽ không có vấn đề gì nếu một luồng không thể duy trì một lần tải + lưu trữ trên mỗi đồng hồ.)

Hoặc có thể chỉ cần sử dụng câu trả lờiNgay lập tức khi tải dữ liệu này sau. NóRấtGiá rẻ, thông lượng cao nhưng vẫn có độ trễ tương tự như FP được thêm vào. Tương tự như vậy, nếu đó là một mảng lớn không vừa với bộ đệm, thì việc tăng cường độ tính toán bằng cách giảm các lần truyền dữ liệu riêng lẻ là một vấn đề lớn. (Chặn bộ đệm hay còn gọi là ốp lát vòng lặp Đó cũng là một ý tưởng hay nếu bạn có thể: chạy nhiều bước của thuật toán trên các khối dữ liệu có kích thước bộ đệm. )

Chỉ sử dụng bộ lưu trữ NT bỏ qua bộ đệm như là phương sách cuối cùng nếu bạn không thể tìm ra cách sử dụng bộ đệm một cách hiệu quả. Sẽ tốt hơn nếu bạn có thể chuyển đổi một số dữ liệu bạn sắp sử dụng để nó nằm trong bộ nhớ đệm vào lần sử dụng tiếp theo.


Đối với các CPU dòng Intel SnB, vòng lặp chính (từ .L31 ĐẾN jne .L31 trên trình biên dịch Godbolt Explorer) là 6 vi lệnh vì các chế độ địa chỉ được lập chỉ mục không vi phạm (Thật không may, điều này chưa được ghi lại trong . Bản pdf vi mô của Agner Fog ở giữa. )

Có 4 vi lệnh miền tổng hợp trên Nehalem và chỉ có 3 vi lệnh ALU, vì vậy Nehalem nên chạy nó ở tốc độ 1 vi lệnh trên mỗi đồng hồ.

.L31: # vòng lặp chính: 6 uops trên SnB-family, 4 uops trên Nehalem
rsqrtps xmm0, XMMWORD PTR [rbx+rax] # tmp127, MEM[base: ar_in_now_10, chỉ mục: ivtmp.51_61, offset: 0B]
movaps XMMWORD PTR [rbp+0+rax], xmm0 # MEM[base: ar_out_now_12, chỉ mục: ivtmp.51_61, offset: 0B], tmp127
thêm rax, 16#ivtmp.51,
cmp rax, 100000 #ivtmp.51,
jne .L31 #,

Bạn không thể giảm vòng lặp xuống còn 4 vi lệnh miền hợp nhất vì bạn muốn viết một đích riêng để nó có thể chạy trên một vectơ trên mỗi đồng hồ mà không cần hủy đăng ký. (Cả tải và lưu trữ đều yêu cầu chế độ địa chỉ thanh ghi đơn, vì vậy hãy sử dụng src-dst Đã lập chỉ mục hiện tại_dst thay vì tăng src thủ thuật không có tác dụng).

Sửa đổi C++ của bạn để gcc sử dụng mức tăng con trỏ sẽ chỉ lưu một uop vì bạn phải tăng src và dst. Ngay lập tức float *endp = bắt đầu + chiều dài;vì (p = bắt đầu; p < endp; p+=4) {}sẽ lặp lại như thế này

.loop:
rsqrtps xmm0, [rsi]
thêm rsi, 16
movaps [rdi], xmm0
thêm rdi, 16
cmp rdi, rbx
jne.loop

Muốn gcc làm điều gì đó như thế này khi mở rộng,nếu không thì câu trả lời + chuyển độngNếu chúng vẫn sử dụng chế độ đánh địa chỉ được lập chỉ mục, chúng sẽ là 4 vi lệnh miền được hợp nhấtvà không có số lần hủy cuộn nào sẽ làm cho vòng lặp của bạn chạy ở một vectơ trên mỗi đồng hồ.

Về c++ - Xấp xỉ nhanh hơn căn bậc hai nghịch đảo của một mảng, chúng tôi đã tìm thấy một câu hỏi tương tự trên Stack Overflow: https://stackoverflow.com/questions/38622534/

25 4 0
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress