При объявлении локальных массивов внутри функций компилятор не только резервирует место в стеке под массив, но и инициализирует все его ячейки нулями, чтобы в них не было мусорных значений.
Сделано это из-за быдлокодеров, которые, пользуются переменными, забывая их инициализировать.
Тем не менее, инициализация занимает некоторое время, да и польза от этого сомнительная, если вы способны отдавать отчёт своим действиям.
К счастью, есть способ избежать потерь во времени, пропустив инициализацию:
PHP код:
SomeFunction()
{
// перед объявлением массива перейдём на метку skip_array_init
goto skip_array_init;
// объявляем массив
new arr[256];
skip_array_init:
// в этом месте массив arr будет существовать, но он не будет инициализирован:
// код инициализации существует, но он находится _до_ метки skip_array_init,
// т.е. мы обошли этот код
// дальше - код с использованием массива
}
Скомпилируем код, а затем дизассемблируем его.
Получим следующую последовательность инструкций:
Код HTML:
; Начало функции SomeFunction.
proc
; И сразу же - переход на метку l.0 (см. далее).
jump 0
; Выделение места в стеке под массив из 16 ячеек (ffffffc0 означает "-16")
; (этот код никогда не сработает, переход на l.0 был безусловным).
stack ffffffc0
; Заполнение массива нулями происходит побайтово,
; 16 ячеек - 64 байта (40 в шестнадцатеричной системе счисления).
zero.pri
addr.alt ffffffc0
fill 40
; Метка, с помощью которой пропускается инициализация массива.
l.0
; Установка вершины стека в то положение, когда выделено 16 ячеек под массив,
; Обратите внимание: в отличие от обычного создания массива,
; здесь массив не заполняется никакими значениями,
; происходит только резервирование пространства,
lctrl 5
add.c ffffffc0
sctrl 4
; Код с использованием массива.
; ...
; Высвобождение пространства в стеке и возврат из функции.
stack 40
zero.pri
retn
Если кому интересно, вот тест скорости:
Открыть/закрыть
PHP код:
#include <a_samp>
const PROFILE_ITERATIONS_MAJOR = 1000;
const PROFILE_ITERATIONS_MINOR = 1000;
const STRING_SIZE = 512;
const SZ = STRING_SIZE;
#define _m1();\
{new a[SZ];}
#define method1();\
_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();
#define _m2();\
{goto skip_init;new a[SZ];skip_init:}
#define method2();\
_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();
bool:IsJITEnabled()
{
#emit lctrl 7
#emit retn
return true;
}
main()
{
printf(
"Testing array initialization..." "\n"\
"(%dx%d iterations, array size: %d, JIT status: %sabled)",
PROFILE_ITERATIONS_MAJOR, PROFILE_ITERATIONS_MINOR,
STRING_SIZE, IsJITEnabled() ? ("en") : ("dis")
);
new t1, t2, t, i, j;
t1 = 0, t2 = 0;
for(i = 0; i < PROFILE_ITERATIONS_MAJOR; ++i)
{
t = GetTickCount();
for(j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
}
t1 += GetTickCount()-t;
t = GetTickCount();
for(j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
}
t2 += GetTickCount()-t;
}
printf(
"method 1: %d" "\n"\
"method 2: %d",
t1, t2
);
}
Макросы method1 и method2 сделаны, чтобы повторить в них создание локальных массивов по 10 раз.
Кроме того сами макросы используются в функции main по 10 раз (итого получается 10x10 = 100 повторов).
Это не что иное, как развёртывание циклов. Я сделал именно так, чтобы уменьшить погрешность от циклов и вызовов GetTickCount, таким образом, увеличив точность измерений.
Также в процессе компиляции выдаётся куча варнингов, но они, опять же, из-за макросов, не обращайте на них внимания.
Тест я запускал на массивах из 32, 256 и 512 ячеек, с включенным и выключенным JIT.
Результаты следующие:
Открыть/закрыть
Код:
Testing array initialization...
(1000x1000 iterations, array size: 32, JIT status: disabled)
method 1: 5581
method 2: 47
Testing array initialization...
(1000x1000 iterations, array size: 32, JIT status: enabled)
method 1: 2640
method 2: 4
Код:
Testing array initialization...
(1000x1000 iterations, array size: 256, JIT status: disabled)
method 1: 31324
method 2: 42
Testing array initialization...
(1000x1000 iterations, array size: 256, JIT status: enabled)
method 1: 15106
method 2: 3
Код:
Testing array initialization...
(1000x1000 iterations, array size: 512, JIT status: disabled)
method 1: 63840
method 2: 40
Testing array initialization...
(1000x1000 iterations, array size: 512, JIT status: enabled)
method 1: 29161
method 2: 4
Из измерений можно сделать вывод, что размер массива никак не влияет на результаты метода с пропуском инициализации массива.
Но всё же от трюка не сильно много пользы. Да, цифры в тесте, казалось бы, большие, но не стоит забывать, что они получены с помощью 100 миллионов итераций. В нормальных же условиях время инициализации массива в 512 ячеек составит всего ~0.0006384 мс или ~0.0000006 секунды.