В чем разница между константным массивом и статическим константным массивом в C/С++
Компиляция следующего кода в 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
, компиляция заняла полсекунды
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
? и почему время компиляции так резко изменилось?
РЕДАКТИРОВАТЬ:
Фактический код можно найти здесь (компиляция была в режиме отладки)
Ответы
Ответ 1
const
или нет, static
функция non- local должна создаваться всякий раз, когда функция вводится и достигается объявление. Ваш компилятор тратит время на генерацию кода для выполнения этого действия во время выполнения, что может быть трудным, когда инициализатор слишком длинный.
С другой стороны, static
этой формы может просто иметь свое начальное значение где-то в исполняемом файле, без необходимости ускорения выполнения.
Это звучит как проблема QoI с вашим компилятором, если вы действительно видите большую разницу во времени сборки (особенно, если 1,2 МБ не так уж много данных), но эти две части кода принципиально различаются и имеют огромные инициализаторы поскольку вещи, предназначенные для жизни "в стеке", как правило, являются чем-то, чего следует избегать.
Ответ 2
Локальная переменная, объявленная как 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
[email protected][email protected]@[email protected] 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 [email protected][email protected]@[email protected][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 [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
PUBLIC [email protected]
...
EXTRN @[email protected]:PROC
EXTRN __chkstk:PROC
EXTRN _memset:PROC
EXTRN ___security_cookie:DWORD
EXTRN __fltused:DWORD
; COMDAT [email protected]
CONST SEGMENT
[email protected] DQ 0bff0000000000000r ; -1
CONST ENDS
; COMDAT [email protected]
CONST SEGMENT
[email protected] DQ 0bfefffffdfc9a9adr ; -1
CONST ENDS
; COMDAT [email protected]
CONST SEGMENT
[email protected] DQ 0bfefffffbf935359r ; -1
CONST ENDS
; COMDAT [email protected]
CONST SEGMENT
[email protected] DQ 0bfefffff9f5cfd06r ; -1
CONST ENDS
; COMDAT [email protected]
CONST SEGMENT
[email protected] DQ 0bfefffff7f26a6b3r ; -1
CONST ENDS
; COMDAT [email protected]
CONST SEGMENT
[email protected] 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 [email protected]
movsd QWORD PTR _Arr$[ebp], xmm0
movsd xmm0, QWORD PTR [email protected]
movsd QWORD PTR _Arr$[ebp+8], xmm0
movsd xmm0, QWORD PTR [email protected]
movsd QWORD PTR _Arr$[ebp+16], xmm0
movsd xmm0, QWORD PTR [email protected]
movsd QWORD PTR _Arr$[ebp+24], xmm0
movsd xmm0, QWORD PTR [email protected]
movsd QWORD PTR _Arr$[ebp+32], xmm0
movsd xmm0, QWORD PTR [email protected]
movsd QWORD PTR _Arr$[ebp+40], xmm0
movsd xmm0, QWORD PTR [email protected]
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 @[email protected]
mov esp, ebp
pop ebp
ret 0
_tfuuuuuuu ENDP
_TEXT ENDS
END
Инициализаторы все еще хранятся глобально. Однако обратите внимание, как каждому значению присваивается имя внутри, и что для каждого значения в массиве создаются две инструкции перемещения. Создание этих имен и явных ходов - вот почему так долго генерируется код.
А теперь версия 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.
Ответ 3
Одним из основных различий между static constant
и non static constant
является связь. Любой массив или переменная, объявленная как static
что означает static
сообщает компилятору, что эта переменная или массив используется только внутри файла или функции, и компилятор будет рассматривать его как внутреннюю связь. Это также причина того, что программа компилируется быстро.