thread_local
에 의존하는 타사 라이브러리를 사용하고 있습니다. 결과적으로 thread_local
변수가 무조건 동일한 함수 내에서 이전에 다른 호출에 의해 초기화되었다는 사실에도 불구하고 일부 사이클의 각 반복에서 (심지어는 모두 체크하지 못했습니다) 내 프로그램에서 __tls_init()
을 반복적으로 호출합니다. 전체 프로그램 시작). 내 x86_64
에 __tls_init()
에서__tls_init 호출을 줄이거 나 제거하는 방법은 무엇입니까?
첫 번째 지침은 그래서 이것은 각 스레드 당이라고 처음으로, %fs:[email protected]
에서 값이 1
로 설정되어
cmpb $0, %fs:[email protected]
je .L530
ret
.L530:
pushq %rbp
pushq %rbx
subq (some stack space), %rsp
movb $1, %fs:[email protected]
하고 더 호출은 즉시 반환. 그러나 여전히 thread_local
변수에 액세스 할 때마다 call
의 모든 오버 헤드를 의미합니다.
정적으로 링크 된 (실제로 생성 된!) 함수이므로 컴파일러는이 조건으로 시작한다는 것을 "알고"있으며이 함수를 더 많이 호출 할 필요가 없다는 것을 흐름 분석에서 알 수 있습니다 한번 이상. 그러나 그렇지 않습니다.
불필요한 call __tls_init
명령어를 없애거나 적어도 컴파일러가 시간이 중요한 섹션에서이를 방출하지 않도록 할 수 있습니까? 실제 컴파일에서
예 상황 : (-03)
pushq %r13
movq %rdi, %r13
pushq %r12
pushq %rbp
pushq %rbx
movq %rsi, %rbx
subq $88, %rsp
call __tls_init // always gets called
movq (%rbx), %rdi
call <some local function>
movq 8(%rax), %r12
subq (%rax), %r12
movq %rax, %rbp
sarq $4, %r12
cmpq $1, %r12
jbe .L6512
leaq -2(%r12), %rax
movq $0, (%rsp)
leaq 48(%rsp), %rbx
movq %rax, 8(%rsp)
.L6506:
call __tls_init // needless and called potentially very many times!
movq %rsp, %rsi
movq %rsp, %rdi
addq $8, %rbx
call <some other local function>
movq %rax, -8(%rbx)
leaq 80(%rsp), %rax
cmpq %rbx, %rax
jne .L6506 // cycle
업데이트 : 위의 소스 코드는 지나치게 복잡하다. 이 컴파일러 탐색기에서 최대 최적화 설정 (link to this particular example) 분석을 참조하면
void external(int);
struct X {
volatile int a; // to prevent optimizing to a constexpr
X() { a = 5; } // to enforce calling a c-tor for thread_local
void f() { external(a); } // to prevent disregarding the value of a
};
thread_local X x;
void f() {
x.f();
for(int j = 0; j < 10; j++)
x.f(); // x is totally initialized now
}
, 당신은 중복 장착 한 후 루프 의 모든 반복에 0
에 대한 fs:[email protected]
을 확인하는 동일한 현상을 알 수 있습니다 : 여기 MWE입니다 a __tls_init
이이 매우 단순한 경우에 인라이 율화 되더라도, 이 인 레이블, 즉 .L4
에 있습니다 (출력은 동일하게 유지된다고 가정).
이 질문은 G ++에 관한 것이지만 CLang (see in Compiler Explorer)은이 점을 더욱 분명하게 보여줍니다.
외부 함수 호출이이 예제에서 저장된 값을 덮어 쓸 수 있다고 말할 수 있습니다. 그러나 무엇이 보증 될 것입니까? 그렇다면 호출 규칙을 무시할 수도 있습니다. 이러한 점에서 컴파일러는 단지 그것이 훌륭하게 작동 할 것이라고 가정해야합니다. 게다가 위의 메인 코드에는 외부 함수가 없었고 단일 번역 단위가있었습니다. MWE와 같은 작은 예제에서는 컴파일러 이을 감지하고 제거하여 어떻게 든 가능해야 함을 나타냅니다. .
당신은을 보여 주어야을 C++의 관련 소스 코드 –
@BasileStarynkevitch 추가되었습니다. –