in Computing and stuff

dynаrray c++11

Попадна ми едно предложение за новия стандарт (С++14), масив чиито размер не може да се променя след като бъде създаден, но пък размера се определя runtime – dynarray.
С две думи – идеята е да ползва stack memory (когато поисканата памет не е твърде много), а в другите случаи да се ползва heap memory. Целта е и всякакви други контейнери го ползват, а резултата ще е повишена ефективност.
Разбира се, всичко това да се случва само в рамките на scope на функция (защото това е lifetime-a на stack memory).

Възможно приложение на тази структура : да речем имате multithreaded scanline render engine и всяка нишка има нужа от pixel buffer. Ако картинката е малка и се rend-ва бързо можем да ползваме stack mem, ако е голяма и се ренди бавно – heap (overhead-а от new[] ще е пренебрежим). (а можем да алокираме pixel buffer преди rend-a и да ползваме него, но да речем че не ни се занимава с това).

Сега, възниква въпроса, можем ли да имплементираме dynarray в С++11.
Ето oпитите, които направих и заключенията до които стигнах.


Единственото, което не е много ясно е как да алокираме динамично памет на стека.

Опция 1.
Заделяме костантно памет в обекта, ползваме нея ако стига, в противен случай – heap-a.

Това работи. Проблема е, че дори и да ползваме heap memory, пак имаме overhead-a от maxBytesOnStack на стека. А това не е приемливо, защото се борехме за performance и memory.

Опция 2.
Заделяме споделене памет за всички dyn_array обекти, когато ползваме stack memory търсим в нея с custom allocator. Ако няма място – ползваме heap.

Проблема тук е, че трябва да подържаме допълнителна структура от данни, за да може customAllocator-a да работи, имаме overhead от търсенето в него. От време на време трябва да извършваме “поддръжката” му и да сливаме парчетата които са едно до друго, може да получим фрагментация, имплементацията е сложна. Та трябва да има и по-добър начин.

Oпция 3.
Можем да ползваме функцията alloca(size_t bytesCount), която прави точно това – алокира памет на стека. Тази памет още повече не се освобождава при излизане от scope, а чак при излизане от функцията, която е направила извикването. Тоест, този код crash-ва със stackoverflow :

Ето и какво бихме написали с alloca

Това компилирано с clang3.1 и с прости тестове работи. Но работи заради чист късмет – dyn_array() освен конструктор е и функция, и в (1) заделената памет с alloca() спира да я има.

Опция 4.
Да се опитаме да ползваме С++11 за да излъжем компилатора 🙂
Ще оставим една член-данна на класа от тип функция, а нея ще инициализираме с ламбда, която заделя статична памет и връща указател към нея (tnx to Komitov за идеята).

Сега, ясно е че имаме overhead oт това че заделяме maxBytesOnStack всеки път, вместо elementsCount*sizeof(T). А и не можем да зачистим статичната памет (2).
Опитите да го излъжа с връщане на const&, ползването на extension-a на gcc (int i = 42; int arr[i]) и др. не завършиха успешно.
Но пък написах този ред код докато пробвах всякаквите там неща 🙂
std::function < const T(&())[maxBytesOnStack] > (lambda която връща ref към С масив).
Все още обаче имам някакви надежди, че е възможно да се напише hack с такава ламбда.

Опция 5.
Опция 5. е Опция 3., с тази разлика че статичната памет се заделя където трябва (в извикващата функция). Освен това кода е по обширен

Можем да го ползваме почти като нормална функция :

Като заключение,
(1) std::dynarray ползвайки С++11 изглежда не може да се направи (имплементацията в clang e cheat&fake – ползва единствено malloc, това не нарушава стандарта, ама все пак ..), трябват промени в компилатора и/или да се пише на assembler.

(2) В С++11 един прост конструктор отнема над 200 символа – сигурен съм че с python с толкова мога да вдигна web server :). Изглежда пичовете от С++ комитета пружинките на клавиатурата за нищо ги нямат.

(3) Може би е добре да си отворя един github 🙂

Write a Comment

Comment


*