c++ c arrays static const

¿Cuál es la diferencia entre const array y static const array en C/C++?



arrays (2)

Una variable local declarada como static tiene una vida útil para todo el programa en ejecución, y normalmente se almacena en el segmento de datos. Los compiladores implementan esto al tener una sección que tiene los valores en ellos.

Las variables locales no declaradas como estáticas generalmente viven en la pila y deben inicializarse cada vez que se ingrese el alcance de la variable.

En cuanto al ensamblaje para el caso static , MSVC 2015 produce lo siguiente:

; 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

Mientras que gcc 4.8.5 produce lo siguiente:

.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

Así que ambos definen los datos globalmente y hacen referencia a esa matriz global directamente.

Ahora veamos el código no estático. Primero para 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

Los inicializadores todavía se almacenan globalmente. Sin embargo, observe cómo cada valor recibe un nombre internamente y que se generan 2 instrucciones de movimiento para cada valor en la matriz . Crear esos nombres y los movimientos explícitos es la razón por la que lleva tanto tiempo generar el código.

Y ahora la versión 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

En lugar de generar instrucciones explícitas para copiar cada valor, gcc simplemente llama a memcpy para copiar los valores de datos globales en la matriz local, por lo que generar el código de inicialización es mucho más rápido.

Entonces, la moraleja de la historia es que MSVC está siendo muy ineficiente en cuanto a cómo inicializa las variables locales.

Además, como se señaló en los comentarios, este es un developercommunity.visualstudio.com/content/problem/407999/… que se debe corregir en VS 2019.

La compilación del siguiente código en Visual Studio 2015 (Win7, x64, configuración de depuración) tomó mucho, mucho tiempo (es decir, más de 10 minutos)

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]; }

Pero cuando agregué la palabra clave static , la compilación tomó medio segundo.

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]; }

Sé que static significa que la variable mantendrá su valor entre invocaciones, pero si la matriz es const todos modos, ¿qué diferencia hace si agrego static ? ¿Y por qué se cambia tan dramáticamente el tiempo de compilación?

EDITAR :

El código real se puede encontrar here , (la compilación estaba en modo de depuración)


const o no, se debe construir una función local no static siempre que se ingrese a la función y se alcance la declaración. Su compilador está gastando tiempo en generar el código para realizar esa acción en tiempo de ejecución, lo que puede ser arduo cuando el inicializador es muy largo.

Por contraposición, una static de esta forma solo puede tener su valor inicial incrustado en el ejecutable en algún lugar, sin necesidad de giro en tiempo de ejecución.

Suena como un pequeño problema de QoI con su compilador si realmente está viendo una gran diferencia en los tiempos de compilación (especialmente cuando 1.2MB no son demasiados datos), pero los dos fragmentos de código son fundamentalmente diferentes y grandes inicializadores. porque las cosas destinadas a vivir "en la pila" suelen ser algo que se debe evitar.