Giả sử chúng tôi đang cố gắng sử dụng tsc để giám sát hiệu suất và chúng tôi muốn ngăn chặn việc sắp xếp lại lệnh.Sự khác biệt giữa rdtscp, rdtsc: bộ nhớ và cpuid/rdtsc?
Đây là những lựa chọn của chúng tôi:
1:rdtscp
là lời kêu gọi serializing. Nó ngăn cản sắp xếp lại xung quanh cuộc gọi đến rdtscp.
__asm__ __volatile__("rdtscp; " // serializing read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc variable
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
Tuy nhiên, rdtscp
chỉ khả dụng trên các CPU mới hơn. Vì vậy, trong trường hợp này chúng ta phải sử dụng rdtsc
. Nhưng rdtsc
không được tuần tự hóa, vì vậy việc sử dụng nó một mình sẽ không ngăn CPU sắp xếp lại nó.
Vì vậy, chúng ta có thể sử dụng một trong hai tùy chọn này để ngăn chặn sắp xếp lại:
2: Đây là một lời kêu gọi cpuid
và sau đó rdtsc
. cpuid
là một cuộc gọi nối tiếp.
volatile int dont_remove __attribute__((unused)); // volatile to stop optimizing
unsigned tmp;
__cpuid(0, tmp, tmp, tmp, tmp); // cpuid is a serialising call
dont_remove = tmp; // prevent optimizing out cpuid
__asm__ __volatile__("rdtsc; " // read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
3: Đây là một lời kêu gọi rdtsc
với memory
trong danh sách clobber, mà ngăn sắp xếp lại
__asm__ __volatile__("rdtsc; " // read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc
:
: "%rcx", "%rdx", "memory"); // rcx and rdx are clobbered
// memory to prevent reordering
hiểu biết của tôi cho các tùy chọn thứ 3 là như sau:
Lập hãy gọi __volatile__
ngăn trình tối ưu hóa loại bỏ asm hoặc di chuyển nó qua bất kỳ hướng dẫn nào có thể cần kết quả (hoặc thay đổi đầu vào) của asm. Tuy nhiên nó vẫn có thể di chuyển nó đối với các hoạt động không liên quan. Vì vậy, __volatile__
là không đủ.
Thông báo cho bộ nhớ trình biên dịch đang bị ghi đè: : "memory")
. Các "memory"
clobber có nghĩa là GCC không thể thực hiện bất kỳ giả định về nội dung bộ nhớ còn lại trên cùng một asm, và do đó sẽ không sắp xếp lại xung quanh nó.
Vì vậy, câu hỏi của tôi là:
- 1: Liệu sự hiểu biết của tôi về
__volatile__
và"memory"
có đúng không? - 2: Thực hiện hai cuộc gọi thứ hai có thực hiện tương tự không?
- 3: Sử dụng
"memory"
trông đơn giản hơn nhiều so với sử dụng hướng dẫn tuần tự hóa khác. Tại sao mọi người sử dụng tùy chọn thứ 3 trong tùy chọn thứ 2?
Bạn có thể nhầm lẫn việc sắp xếp lại các lệnh do trình biên dịch tạo ra, bạn có thể tránh bằng cách sử dụng 'volatile' và' memory' và sắp xếp lại các lệnh được thực hiện bởi bộ xử lý (aka _out of order execution_). cpuid'. – hirschhornsalz
@hirschhornsalz nhưng sẽ không có 'bộ nhớ' trong danh sách clobber ngăn bộ xử lý sắp xếp lại hướng dẫn? Không 'bộ nhớ' hoạt động như một hàng rào bộ nhớ? –
hoặc có lẽ 'bộ nhớ' trong danh sách các bộ ghi đè chỉ được phát ra gcc và mã máy kết quả không hiển thị bộ xử lý này? –