Câu hỏi này, mặc dù khá cũ, nhưng cần một số điểm chuẩn, vì nó yêu cầu không phải là cách thành ngữ nhất, hoặc cách có thể được viết với số dòng ít nhất, nhưng là cách nhanh nhất . Và thật ngớ ngẩn nếu trả lời câu hỏi đó mà không có một số thử nghiệm thực tế. Vì vậy, tôi đã so sánh bốn giải pháp, memset so với std :: fill so với ZERO của câu trả lời của AnT và giải pháp tôi đã thực hiện bằng cách sử dụng nội dung AVX.
Lưu ý rằng giải pháp này không chung chung, nó chỉ hoạt động trên dữ liệu 32 hoặc 64 bit. Vui lòng bình luận nếu mã này làm điều gì đó không chính xác.
#include<immintrin.h>
#define intrin_ZERO(a,n){\
size_t x = 0;\
const size_t inc = 32 / sizeof(*(a));/*size of 256 bit register over size of variable*/\
for (;x < n-inc;x+=inc)\
_mm256_storeu_ps((float *)((a)+x),_mm256_setzero_ps());\
if(4 == sizeof(*(a))){\
switch(n-x){\
case 3:\
(a)[x] = 0;x++;\
case 2:\
_mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
case 1:\
(a)[x] = 0;\
break;\
case 0:\
break;\
};\
}\
else if(8 == sizeof(*(a))){\
switch(n-x){\
case 7:\
(a)[x] = 0;x++;\
case 6:\
(a)[x] = 0;x++;\
case 5:\
(a)[x] = 0;x++;\
case 4:\
_mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\
case 3:\
(a)[x] = 0;x++;\
case 2:\
((long long *)(a))[x] = 0;break;\
case 1:\
(a)[x] = 0;\
break;\
case 0:\
break;\
};\
}\
}
Tôi sẽ không khẳng định rằng đây là phương pháp nhanh nhất, vì tôi không phải là chuyên gia tối ưu hóa cấp thấp. Thay vào đó, nó là một ví dụ về việc triển khai phụ thuộc vào kiến trúc chính xác nhanh hơn memset.
Bây giờ, vào kết quả. Tôi đã tính toán hiệu suất cho kích thước 100 int và các mảng dài dài, cả phân bổ tĩnh và động, nhưng ngoại trừ msvc, đã thực hiện loại bỏ mã chết trên các mảng tĩnh, kết quả cực kỳ so sánh, vì vậy tôi sẽ chỉ hiển thị hiệu suất mảng động. Dấu thời gian là ms cho 1 triệu lần lặp, sử dụng chức năng đồng hồ có độ chính xác thấp của time.h.
clang 3.8 (Sử dụng giao diện người dùng clang-cl, cờ tối ưu hóa = / OX / Arch: AVX / Oi / Ot)
int:
memset: 99
fill: 97
ZERO: 98
intrin_ZERO: 90
long long:
memset: 285
fill: 286
ZERO: 285
intrin_ZERO: 188
gcc 5.1.0 (cờ tối ưu hóa: -O3 -march = native -mtune = native -mavx):
int:
memset: 268
fill: 268
ZERO: 268
intrin_ZERO: 91
long long:
memset: 402
fill: 399
ZERO: 400
intrin_ZERO: 185
msvc 2015 (cờ tối ưu hóa: / OX / Arch: AVX / Oi / Ot):
int
memset: 196
fill: 613
ZERO: 221
intrin_ZERO: 95
long long:
memset: 273
fill: 559
ZERO: 376
intrin_ZERO: 188
Có rất nhiều điều thú vị đang diễn ra ở đây: llvm giết gcc, tối ưu hóa đốm sáng điển hình của MSVC (nó thực hiện loại bỏ mã chết ấn tượng trên các mảng tĩnh và sau đó có hiệu suất khủng khiếp cho việc lấp đầy). Mặc dù việc triển khai của tôi nhanh hơn đáng kể, nhưng điều này có thể chỉ là do nó nhận ra rằng việc xóa bit có chi phí ít hơn nhiều so với bất kỳ hoạt động cài đặt nào khác.
Việc triển khai của Clang đáng được xem xét nhiều hơn, vì nó nhanh hơn đáng kể. Một số thử nghiệm bổ sung cho thấy rằng memset của nó trên thực tế chuyên dùng cho các memset không - khác 0 cho mảng 400 byte chậm hơn nhiều (~ 220ms) và có thể so sánh với gcc. Tuy nhiên, việc thiết lập bộ nhớ nonzero với mảng 800 byte không tạo ra sự khác biệt về tốc độ, đó có thể là lý do tại sao trong trường hợp đó, bộ nhớ của họ có hiệu suất kém hơn so với cách thực hiện của tôi - chuyên môn hóa chỉ dành cho các mảng nhỏ và giới hạn đúng là khoảng 800 byte. Cũng lưu ý rằng gcc 'fill' và 'ZERO' không tối ưu hóa thành memset (xem mã được tạo), gcc chỉ đơn giản là tạo mã có các đặc điểm hiệu suất giống hệt nhau.
Kết luận: memset không thực sự được tối ưu hóa cho tác vụ này cũng như mọi người sẽ giả vờ như vậy (nếu không thì memset của gcc và msvc và llvm sẽ có cùng hiệu suất). Nếu vấn đề về hiệu suất thì memset không nên là giải pháp cuối cùng, đặc biệt là đối với các mảng có kích thước trung bình khó xử này, bởi vì nó không chuyên dụng để xóa bit và nó không được tối ưu hóa bằng tay tốt hơn so với việc trình biên dịch có thể tự làm.
new
là C ++ ...