ArrayResize i koszt rozrostu tablicy
Powiększanie tablicy o jeden element w pętli zamienia kod liniowy w kwadratowy. Trzeci argument ArrayResize, o którym prawie nikt nie wie.
1 fragment kodu z tej strony przeszedł przez kompilator MetaEditor build 6090, 2026-08-05.
Zarządzanie tablicami dynamicznymi to chleb powszedni w programowaniu EA. Pobieranie historii świec, zleceń czy pozycji wymaga użycia tablic, których rozmiar nie jest z góry znany. Wbudowana funkcja ArrayResize wydaje się prosta – podajesz nowy rozmiar. Jednak pod maską terminala kryje się skomplikowany mechanizm alokacji pamięci, którego nieznajomość potrafi zwolnić EA o kilkaset procent.
Czym jest fragmentacja i realokacja?
Kiedy zwiększasz rozmiar tablicy (np. z 1000 do 1001 elementów), system operacyjny nie może po prostu "dopisać" jednego elementu na końcu w pamięci, ponieważ obszar ten może być już zajęty przez inne zmienne. MQL5 musi wtedy zaalokować nowy, większy blok pamięci, skopiować stare dane do nowego bloku, a stary blok zwolnić. Jest to operacja o złożoności czasowej O(N).
Jeśli robisz to w pętli, dodając po jednym elemencie przy każdej iteracji, złożoność całej operacji rośnie do O(N^2). Dla 100 000 elementów oznacza to miliony niepotrzebnych operacji kopiowania pamięci.
Rozwiązanie: Trzeci parametr ArrayResize
Wielu programistów nie wie, że funkcja ArrayResize posiada trzeci, opcjonalny parametr: reserve_size. Pozwala on na "rezerwację" pamięci na przyszłość bez zmiany logicznego rozmiaru tablicy (zwracanego przez ArraySize). Jest to odpowiednik metody .reserve() z std::vector w C++.
Terminal alokuje wtedy ciągły blok pamięci wystarczający dla reserve_size elementów. Dopóki nowy rozmiar nie przekroczy zarezerwowanej puli, żadne kopiowanie nie ma miejsca. Dodawanie elementów staje się operacją O(1) amortyzowaną.
// BŁĄD: Klasyczny antywzorzec wydajnościowy
void AppendBad(double &arr[], double value) {
int size = ArraySize(arr);
// Przy każdym wywołaniu terminal kopiuje całą tablicę!
ArrayResize(arr, size + 1);
arr[size] = value;
}
// POPRAWNIE: Użycie mechanizmu rezerwacji
void AppendGood(double &arr[], double value, int &reservedCapacity) {
int size = ArraySize(arr);
// Jeśli brakuje miejsca, zwiększamy pule rezerwy
if(size >= reservedCapacity) {
reservedCapacity = size + 1000; // Skok o 1000 elementów
// Trzeci parametr fizycznie alokuje więcej pamięci, ale ArraySize się nie zmienia
ArrayResize(arr, size + 1, reservedCapacity);
} else {
// Szybkie dodanie bez realokacji
ArrayResize(arr, size + 1);
}
arr[size] = value;
}
void OnStart() {
double myArray[];
int capacity = 0;
ArrayResize(myArray, 0, 0); // Inicjalizacja
ulong startTime = GetTickCount64();
for(int i = 0; i < 100000; i++) {
AppendGood(myArray, i, capacity);
}
Print("Czas trwania: ", GetTickCount64() - startTime, " ms");
}