У меня вопрос об инициализации довольно больших наборов статических данных.
Ниже приведены три приведенных ниже примера инициализации наборов статических данных. Я хотел бы понять значения времени загрузки программы и объема памяти для методов, показанных ниже. Я действительно не знаю, как оценивать это самостоятельно в данный момент. Моя среда сборки все еще находится на рабочем столе с использованием Visual Studio, однако встроенные цели будут скомпилированы для VxWorks с использованием GCC.
Традиционно я использовал базовые C-структуры для такого рода вещей, хотя есть веские основания переместить эти данные в классы C++, продвигаясь вперед. Динамическое распределение памяти не рекомендуется во встроенном приложении и избегается там, где это возможно.
Насколько я знаю, единственный способ инициализировать класс C++ - через его конструктор, показанный ниже в методе 2. Мне интересно, как это сравнивается с методом 1. Есть ли заметные дополнительные накладные расходы с точки зрения ПЗУ (площадь программы), RAM (Memory Footprint) или время загрузки программы? Мне кажется, что компилятор может оптимизировать довольно тривиальный конструктор, но я не уверен, что это обычное поведение.
Я перечислил метод 3, как я его рассматривал, хотя это просто плохая идея. Есть ли что-то еще, что мне здесь не хватает? Кто-нибудь еще инициализирует данные аналогичным образом?
/* C-Style Struct Storage */
typedef struct{
int a;
int b;
}DATA_C;
/* CPP Style Class Storage */
class DATA_CPP{
public:
int a;
int b;
DATA_CPP(int,int);
};
DATA_CPP::DATA_CPP(int aIn, int bIn){
a = aIn;
b = bIn;
}
/* METHOD 1: Direct C-Style Static Initialization */
DATA_C MyCData[5] = { {1,2},
{3,4},
{5,6},
{7,8},
{9,10}
};
/* METHOD 2: Direct CPP-Style Initialization */
DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
DATA_CPP(3,4),
DATA_CPP(5,6),
DATA_CPP(7,8),
DATA_CPP(9,10),
};
/* METHOD 3: Cast C-Struct to CPP class */
DATA_CPP* pMyCppData2 = (DATA_CPP*) MyCData;
Я сделал несколько исследований в этом, думал, что опубликую результаты. Здесь я использовал Visual Studio 2008 во всех случаях.
Здесь представлен вид дизассемблирования кода из Visual Studio в режиме отладки:
/* METHOD 1: Direct C-Style Static Initialization */
DATA_C MyCData[5] = { {1,2},
{3,4},
{5,6},
{7,8},
{9,10},
};
/* METHOD 2: Direct CPP-Style Initialization */
DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
010345C0 push ebp
010345C1 mov ebp,esp
010345C3 sub esp,0C0h
010345C9 push ebx
010345CA push esi
010345CB push edi
010345CC lea edi,[ebp-0C0h]
010345D2 mov ecx,30h
010345D7 mov eax,0CCCCCCCCh
010345DC rep stos dword ptr es:[edi]
010345DE push 2
010345E0 push 1
010345E2 mov ecx,offset MyCppData (1038184h)
010345E7 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(3,4),
010345EC push 4
010345EE push 3
010345F0 mov ecx,offset MyCppData+8 (103818Ch)
010345F5 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(5,6),
010345FA push 6
010345FC push 5
010345FE mov ecx,offset MyCppData+10h (1038194h)
01034603 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(7,8),
01034608 push 8
0103460A push 7
0103460C mov ecx,offset MyCppData+18h (103819Ch)
01034611 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(9,10),
01034616 push 0Ah
01034618 push 9
0103461A mov ecx,offset MyCppData+20h (10381A4h)
0103461F call DATA_CPP::DATA_CPP (103119Ah)
};
01034624 pop edi
01034625 pop esi
01034626 pop ebx
01034627 add esp,0C0h
0103462D cmp ebp,esp
0103462F call @ILT+325(__RTC_CheckEsp) (103114Ah)
01034634 mov esp,ebp
01034636 pop ebp
Интересно отметить, что есть определенная часть накладных расходов на использование памяти программы и время загрузки, по крайней мере, в не оптимизированном режиме отладки. Обратите внимание, что метод 1 имеет нулевые инструкции по сборке, а метод два имеет около 44 инструкций.
Я также выполнил скомпилированную программу в режиме Release с включенной оптимизацией, здесь выведена сокращенная сборка:
?MyCData@@3PAUDATA_C@@A DD 01H ; MyCData
DD 02H
DD 03H
DD 04H
DD 05H
DD 06H
DD 07H
DD 08H
DD 09H
DD 0aH
?MyCppData@@3PAVDATA_CPP@@A DD 01H ; MyCppData
DD 02H
DD 03H
DD 04H
DD 05H
DD 06H
DD 07H
DD 08H
DD 09H
DD 0aH
END
Похоже, что компилятор действительно оптимизировал запросы к конструктору C++. Я не мог найти никаких доказательств того, что конструктор когда-либо называется в коде сборки.
Я думал, что попробую что-нибудь еще. Я сменил конструктор на:
DATA_CPP::DATA_CPP(int aIn, int bIn){
a = aIn + bIn;
b = bIn;
}
Опять же, компилятор оптимизировал это, в результате чего появился статический набор данных:
?MyCppData@@3PAVDATA_CPP@@A DD 03H ; MyCppData
DD 02H
DD 07H
DD 04H
DD 0bH
DD 06H
DD 0fH
DD 08H
DD 013H
DD 0aH
END
Интересно, что компилятор смог оценить код конструктора на всех статических данных во время компиляции и создать статический набор данных, все еще не вызывающий конструктор.
Я думал, что попробую что-то еще немного, оперирую глобальную переменную в конструкторе:
int globalvar;
DATA_CPP::DATA_CPP(int aIn, int bIn){
a = aIn + globalvar;
globalvar += a;
b = bIn;
}
И в этом случае компилятор теперь сгенерировал код сборки, чтобы вызвать конструктор во время инициализации:
??__EMyCppData@@YAXXZ PROC ; 'dynamic initializer for 'MyCppData'', COMDAT
; 35 : DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
00000 a1 00 00 00 00 mov eax, DWORD PTR ?globalvar@@3HA ; globalvar
00005 8d 48 01 lea ecx, DWORD PTR [eax+1]
00008 03 c1 add eax, ecx
0000a 89 0d 00 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A, ecx
; 36 : DATA_CPP(3,4),
00010 8d 48 03 lea ecx, DWORD PTR [eax+3]
00013 03 c1 add eax, ecx
00015 89 0d 08 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+8, ecx
; 37 : DATA_CPP(5,6),
0001b 8d 48 05 lea ecx, DWORD PTR [eax+5]
0001e 03 c1 add eax, ecx
00020 89 0d 10 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+16, ecx
; 38 : DATA_CPP(7,8),
00026 8d 48 07 lea ecx, DWORD PTR [eax+7]
00029 03 c1 add eax, ecx
0002b 89 0d 18 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+24, ecx
; 39 : DATA_CPP(9,10),
00031 8d 48 09 lea ecx, DWORD PTR [eax+9]
00034 03 c1 add eax, ecx
00036 89 0d 20 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+32, ecx
0003c a3 00 00 00 00 mov DWORD PTR ?globalvar@@3HA, eax ; globalvar
; 40 : };
00041 c3 ret 0
??__EMyCppData@@YAXXZ ENDP ; 'dynamic initializer for 'MyCppData''
FYI, я нашел эту страницу полезной при настройке visual studio для сборки сборки: Как получить вывод ассемблера из файла C в VS2005
В С++ 11 вы можете написать следующее:
DATA_CPP obj = {1,2}; //Or simply : DATA_CPP obj {1,2}; i.e omit '='
вместо
DATA_CPP obj(1,2);
Расширяя это, вы можете написать:
DATA_CPP MyCppData[5] = { {1,2},
{3,4},
{5,6},
{7,8},
{9,10},
};
вместо этого:
DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
DATA_CPP(3,4),
DATA_CPP(5,6),
DATA_CPP(7,8),
DATA_CPP(9,10),
};
Читайте об унифицированной инициализации.