static_cast<volatile void> (foo())
은 컴파일러가 실제로 최적화를 사용하여 gcc/clang/MSVC/ICC에서 foo()
을 계산하도록 요구하는 방식으로 작동하지 않습니다.
#include <bitset>
void foo() {
for (int i = 0; i < 10000; ++i)
for (int j = 0; j < 10000; ++j) {
std::bitset<64> std_set(i + j);
//volatile const auto c = std_set.count(); // real work happens
static_cast<volatile void> (std_set.count()); // optimizes away
}
}
모든 4 개 86 컴파일러와 단지 ret
에 컴파일합니다. (MSVC는 std::bitset::count()
또는 무언가의 독립형 정의에 대한 ASM을 방출하지만, foo()
의 그 사소한 정의를 아래로 스크롤합니다.
(소스 +의 ASM의 이것에 대한 출력 Matt Godbolt's compiler explorer에 다음 예제)
을
static_cast<volatile void>()
이 어떤 일을하는 컴파일러가 있을지도 모릅니다. 결과를 메모리에 저장하는 지침을 쓰지 않고 반복 계산을 수행하는 가벼운 방법 일 수도 있습니다. 마이크로 벤치 마크에서 원하는 것)
결과를 tmp += foo()
(또는 tmp |=
)으로 누적하고 main()
에서 반환하거나 printf
과 함께 인쇄하면 volatile
변수에 저장하는 대신 유용 할 수 있습니다.또는 빈 인라인 asm
문을 사용하여 실제로 지시를 추가하지 않고 컴파일러의 기능을 최적화하는 것과 같은 다양한 컴파일러 관련 사항. 그가 optimizer-escape function for GNU C를 도시
는 Chandler Carruth's CppCon2015 talk on using perf
to investigate compiler optimizations를 참조하십시오. 그러나 그의 escape()
함수는 값이 메모리에 있어야하도록 작성되었습니다 (asm에 void*
을 전달하고 "memory"
불투명도로). 필요하지 않습니다. 레지스터 나 메모리에 값을 넣거나 심지어는 상수로만 컴파일러를 사용하면됩니다. (는 asm 문 제로 지침 것을 알고하지 않기 때문에 그것은 완전히 우리의 루프를 풀다 않을 수 있습니다.)
이 코드는 GCC에 단지 여분의 저장없이 popcnt에 컴파일합니다.
// just force the value to be in memory, register, or even immediate
// instead of empty inline asm, use the operand in a comment so we can see what the compiler chose. Absolutely no effect on optimization.
static void escape_integer(int a) {
asm volatile("# value = %0" : : "g"(a));
}
// simplified with just one inner loop
void test1() {
for (int i = 0; i < 10000; ++i) {
std::bitset<64> std_set(i);
int count = std_set.count();
escape_integer(count);
}
}
#gcc8.0 20171110 nightly -O3 -march=nehalem (for popcnt instruction):
test1():
# value = 0 # it peels the first iteration with an immediate 0 for the inline asm.
mov eax, 1
.L4:
popcnt rdx, rax
# value = edx # the inline-asm comment has the %0 filled in to show where gcc put the value
add rax, 1
cmp rax, 10000
jne .L4
ret
는 연타 꽤 벙어리 "g"
인 제약 조건을 만족하는 메모리 값을 넣어 선택한다. 그러나 clang은 메모리를 옵션으로 포함하는 인라인 asm 제약 조건을 줄 때이를 수행하는 경향이 있습니다. 따라서 Chandler의 escape
기능보다 낫지 않습니다. -march=haswell
와
# clang5.0 -O3 -march=nehalem
test1():
xor eax, eax
#DEBUG_VALUE: i <- 0
.LBB1_1: # =>This Inner Loop Header: Depth=1
popcnt rcx, rax
mov dword ptr [rsp - 4], ecx
# value = -4(%rsp) # inline asm gets a value in memory
inc rax
cmp rax, 10000
jne .LBB1_1
ret
ICC18이 작업을 수행합니다 : 이상한
test1():
xor eax, eax #30.16
..B2.2: # Preds ..B2.2 ..B2.1
# optimization report
# %s was not vectorized: ASM code cannot be vectorized
xor rdx, rdx # breaks popcnt's false dep on the destination
popcnt rdx, rax #475.16
inc rax #30.34
# value = edx
cmp rax, 10000 #30.25
jl ..B2.2 # Prob 99% #30.25
ret #35.1
는, ICC는 xor rdx,rdx
대신 xor eax,eax
사용. 이는 REX 접두사를 낭비하고 Silvermont/KNL에서 종속성을 깨뜨리는 것으로 인식되지 않습니다.
실제로 이것은 제대로 작성되지 않은 테스트 인 것 같습니다. 테스트는 실제 코드와 비교하여 테스트 결과의 잠재적 인 변경을 막기 위해 컴파일러를 혼동시키기 위해 '휘발성'또는 다른 트릭을 사용하지 않아야합니다. – VTT
정확히 마이크로 벤치 마크에서 '휘발성'이하는 일은 꽤 컴파일러에 의존적입니다. 'volatile void'에 캐스팅하면 특정 컴파일러가 값을 계산하지만 실제로 메모리에 저장하는 명령을 보내지 않습니다. 반복 루프에서 테스트하려는 함수의 처리량을 테스트하는 것일 수 있습니다. . –
한 반복의 출력을 다음 반복의 입력으로 보내면 대기 시간을 테스트 할 수 있습니다. * 다른 주변 코드에 미치는 영향을 계산하는 것은 또 다른 별개의 일입니다. (예를 들어'do_something()'이 FP 나 SQRT를 포함하고 있다면, 처리량이 떨어질 수 있지만 주변의 코드에 영향을 미치지는 않습니다. (https://stackoverflow.com/questions/) 4125033/부동 소수점 - 부동 소수점 곱셈/45899202 # 45899202). –