C/C++의 const 어레이와 static const 어레이의 차이점은 무엇입니까?
Visual Studio 2015(Win7, x64, 디버깅 구성)에서 다음 코드를 컴파일하는 데 매우 오랜 시간(즉, 10분 이상)이 소요되었습니다.
double tfuuuuuuu(int Ind)
{
const double Arr[600 * 258] = {3.5453, 45.234234234, 234234.234,// extends to 258 values for each line
// 599 lines here.....
};
return Arr[Ind];
}
근데 제가 이 곡을 넣었을 때static
키워드, 컴파일에는 0.5초가 걸렸다.
double tfuuuuuuu(int Ind)
{
static const double Arr[600 * 258] = {3.5453, 45.234234234, 234234.234,// extends to 258 values for each line
// 599 lines here.....
};
return Arr[Ind];
}
나는 그것을 알고 있습니다.static
변수가 호출 간에 값을 유지하지만 배열이 다음과 같은 경우const
어쨌든, 내가 더한다고 뭐가 달라지겠어?static
컴파일 시간이 크게 바뀌는 이유는 무엇입니까?
편집:
실제 코드는 여기서 확인할 수 있습니다(컴파일은 디버깅모드입니다).
다음과 같이 선언된 로컬 변수static
에는 실행 중인 프로그램 전체의 수명이 있으며 일반적으로 데이터 세그먼트에 저장됩니다.컴파일러는 값을 포함하는 섹션을 사용하여 이를 구현합니다.
static으로 선언되지 않은 로컬 변수는 일반적으로 스택에 존재하며 변수의 범위를 입력할 때마다 초기화해야 합니다.
의 어셈블리를 보고 있습니다.static
MSVC 2015는 다음을 출력합니다.
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1
TITLE MyLBP.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
CONST SEGMENT
?Arr@?1??tfuuuuuuu@@9@9 DQ 04060c00000000000r ; 134 ; `tfuuuuuuu'::`2'::Arr
DQ 03fe15efd20a7955br ; 0.542845
DQ 03fdf59701e4b19afr ; 0.489834
DQ 0bfd8e38e9ab7fcb1r ; -0.388889
DQ 0bfe59f22c01e68a1r ; -0.675676
DQ 0bfeb13b15d5aa410r ; -0.846154
DQ 0bfe2c2355f07776er ; -0.586207
DQ 03fefffffbf935359r ; 1
...
ORG $+1036128
CONST ENDS
PUBLIC _tfuuuuuuu
EXTRN __fltused:DWORD
; Function compile flags: /Odtp
_TEXT SEGMENT
_Ind$ = 8 ; size = 4
_tfuuuuuuu PROC
; File c:\users\dennis bush\documents\x2.c
; Line 4
push ebp
mov ebp, esp
; Line 106
mov eax, DWORD PTR _Ind$[ebp]
fld QWORD PTR ?Arr@?1??tfuuuuuuu@@9@9[eax*8]
; Line 107
pop ebp
ret 0
_tfuuuuuuu ENDP
_TEXT ENDS
END
gcc 4.8.5는 다음을 출력합니다.
.file "MyLBP.c"
.text
.globl tfuuuuuuu
.type tfuuuuuuu, @function
tfuuuuuuu:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
cltq
movq Arr.1724(,%rax,8), %rax
movq %rax, -16(%rbp)
movsd -16(%rbp), %xmm0
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size tfuuuuuuu, .-tfuuuuuuu
.section .rodata
.align 32
.type Arr.1724, @object
.size Arr.1724, 1238400
Arr.1724:
.long 0
.long 1080082432
.long 547853659
.long 1071734525
.long 508238255
.long 1071602032
.long 2595749041
.long -1076305010
.long 3223218337
...
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-16)"
.section .note.GNU-stack,"",@progbits
따라서 둘 다 데이터를 글로벌하게 정의하고 해당 글로벌 어레이를 직접 참조합니다.
이제 비정적 코드를 살펴보겠습니다.VSMC2015 최초:
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1
TITLE MyLBP.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _tfuuuuuuu
PUBLIC __real@3e45798ee2308c3a
PUBLIC __real@3f40e1cf9350aa3c
PUBLIC __real@3f43b1f90beff84b
PUBLIC __real@3f4c6220dc6e8066
PUBLIC __real@3f4ea4c648794089
PUBLIC __real@3f50023666188dc0
PUBLIC __real@3f53957e56f300e9
PUBLIC __real@3f55235d7d33b25f
PUBLIC __real@3f5828f66e5bd33a
PUBLIC __real@3f5c044284dfce31
PUBLIC __real@3f5c87c05341c674
...
EXTRN @__security_check_cookie@4:PROC
EXTRN __chkstk:PROC
EXTRN _memset:PROC
EXTRN ___security_cookie:DWORD
EXTRN __fltused:DWORD
; COMDAT __real@bff0000000000000
CONST SEGMENT
__real@bff0000000000000 DQ 0bff0000000000000r ; -1
CONST ENDS
; COMDAT __real@bfefffffdfc9a9ad
CONST SEGMENT
__real@bfefffffdfc9a9ad DQ 0bfefffffdfc9a9adr ; -1
CONST ENDS
; COMDAT __real@bfefffffbf935359
CONST SEGMENT
__real@bfefffffbf935359 DQ 0bfefffffbf935359r ; -1
CONST ENDS
; COMDAT __real@bfefffff9f5cfd06
CONST SEGMENT
__real@bfefffff9f5cfd06 DQ 0bfefffff9f5cfd06r ; -1
CONST ENDS
; COMDAT __real@bfefffff7f26a6b3
CONST SEGMENT
__real@bfefffff7f26a6b3 DQ 0bfefffff7f26a6b3r ; -1
CONST ENDS
; COMDAT __real@bfefffff5ef05060
CONST SEGMENT
__real@bfefffff5ef05060 DQ 0bfefffff5ef05060r ; -1
CONST ENDS
...
; Function compile flags: /Odtp
_TEXT SEGMENT
_Arr$ = -1238404 ; size = 1238400
__$ArrayPad$ = -4 ; size = 4
_Ind$ = 8 ; size = 4
_tfuuuuuuu PROC
; File c:\users\dennis bush\documents\x2.c
; Line 4
push ebp
mov ebp, esp
mov eax, 1238404 ; 0012e584H
call __chkstk
mov eax, DWORD PTR ___security_cookie
xor eax, ebp
mov DWORD PTR __$ArrayPad$[ebp], eax
; Line 5
movsd xmm0, QWORD PTR __real@4060c00000000000
movsd QWORD PTR _Arr$[ebp], xmm0
movsd xmm0, QWORD PTR __real@3fe15efd20a7955b
movsd QWORD PTR _Arr$[ebp+8], xmm0
movsd xmm0, QWORD PTR __real@3fdf59701e4b19af
movsd QWORD PTR _Arr$[ebp+16], xmm0
movsd xmm0, QWORD PTR __real@bfd8e38e9ab7fcb1
movsd QWORD PTR _Arr$[ebp+24], xmm0
movsd xmm0, QWORD PTR __real@bfe59f22c01e68a1
movsd QWORD PTR _Arr$[ebp+32], xmm0
movsd xmm0, QWORD PTR __real@bfeb13b15d5aa410
movsd QWORD PTR _Arr$[ebp+40], xmm0
movsd xmm0, QWORD PTR __real@bfe2c2355f07776e
movsd QWORD PTR _Arr$[ebp+48], xmm0
...
push 1036128 ; 000fcf60H
push 0
lea eax, DWORD PTR _Arr$[ebp+202272]
push eax
call _memset
add esp, 12 ; 0000000cH
; Line 106
mov ecx, DWORD PTR _Ind$[ebp]
fld QWORD PTR _Arr$[ebp+ecx*8]
; Line 107
mov ecx, DWORD PTR __$ArrayPad$[ebp]
xor ecx, ebp
call @__security_check_cookie@4
mov esp, ebp
pop ebp
ret 0
_tfuuuuuuu ENDP
_TEXT ENDS
END
이니셜라이저는 아직 글로벌하게 저장됩니다.단, 각 값에는 내부 이름이 지정되며 어레이 내의 각 값에 대해 2개의 이동 명령이 생성됩니다.이러한 이름과 명시적인 이동을 생성하면 코드를 생성하는 데 시간이 오래 걸립니다.
gcc 4.8.5 버전은 다음과 같습니다.
.file "MyLBP.c"
.section .rodata
.align 32
.LC0:
.long 0
.long 1080082432
.long 547853659
.long 1071734525
.long 508238255
.long 1071602032
.long 2595749041
.long -1076305010
.long 3223218337
.long -1075470558
...
.text
.globl tfuuuuuuu
.type tfuuuuuuu, @function
tfuuuuuuu:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $1238416, %rsp
movl %edi, -1238404(%rbp)
leaq -1238400(%rbp), %rax
movl $.LC0, %ecx
movl $1238400, %edx
movq %rcx, %rsi
movq %rax, %rdi
call memcpy ; <-------------- call to memcpy
movl -1238404(%rbp), %eax
cltq
movq -1238400(%rbp,%rax,8), %rax
movq %rax, -1238416(%rbp)
movsd -1238416(%rbp), %xmm0
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size tfuuuuuuu, .-tfuuuuuuu
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-16)"
.section .note.GNU-stack,"",@progbits
gcc는 각 값을 복사하기 위한 명시적인 명령을 생성하는 것이 아니라 호출만 합니다.memcpy
글로벌 데이터에서 로컬 어레이로 값을 복사하기 때문에 초기화 코드를 생성하는 속도가 훨씬 빠릅니다.
이 이야기의 교훈은 MSVC가 지역 변수를 초기화하는 데 있어 매우 비효율적이라는 것입니다.
또, 코멘트에도 기재되어 있듯이, VS 2019에 수정 예정인 확인 버그입니다.
const
아니, 아니, 비-static
함수가 입력되고 선언에 도달할 때마다 함수 local을 생성해야 합니다.컴파일러는 실행 시 이 작업을 수행하기 위해 코드를 생성하는 데 시간을 소비하고 있으며, 이니셜라이저가 매우 길면 매우 힘들 수 있습니다.
반대로 말하면,static
실행 시 스핀업할 필요 없이 이 폼의 초기 값을 실행 파일에 삽입할 수 있습니다.
실제로 빌드 시간(특히 1.2)에 큰 차이가 있는 경우 컴파일러의 QOI 문제처럼 들릴 수 있습니다.MB는 그렇게 많은 데이터는 아니지만, 두 개의 코드 조각은 근본적으로 다르며, "스택 위에" 존재하는 것을 위한 거대한 이니셜라이저는 일반적으로 피해야 할 것입니다.
언급URL : https://stackoverflow.com/questions/56057662/whats-the-difference-between-const-array-and-static-const-array-in-c-c
'programing' 카테고리의 다른 글
메모리 리크 디텍터의 동작 원리 (0) | 2022.07.02 |
---|---|
int를 4바이트 문자 배열로 변환(C) (0) | 2022.07.02 |
vuelidate에 sameAs를 사용하면 왜 작동하지 않는 거죠? (0) | 2022.07.02 |
Vue.js 2에서 소품을 초기 데이터로 전달하는 올바른 방법은 무엇입니까? (0) | 2022.07.02 |
What are the best (portable) cross-platform arbitrary-precision math libraries? (0) | 2022.07.02 |