Pull to refresh

Comments 29

Почему всего лишь -O1? Хотя бы уж -Ofast -ftree-vectorize -msse2
Код для вектора тоже было бы вежливо предоставить. А так мы сравниваем бульдога с носорогом. Погодите-ка… Это сравнение питоновых list и vector? А причём тогда
проверить теорию одного коллеги, что C++ vector мог бы быстрее справиться с задачей, чем CPython list.


Или посыл статьи в том, что использовать нативные контейнеры обычно более лучшая идея, чем, не разобравшись, пихать что-то? С этим не поспоришь.
Код для вектора тоже было бы вежливо предоставить. А так мы сравниваем бульдога с носорогом. Погодите-ка… Это сравнение питоновых list и vector?

У Python нет vector. Это биндинг из Cython, который берет из
<vector>
класс vector
оригинальный код из Cython
cdef extern from "<vector>" namespace "std" nogil:
    cdef cppclass vector[T,ALLOCATOR=*]:
        ctypedef T value_type
        ctypedef ALLOCATOR allocator_type

        # these should really be allocator_type.size_type and
        # allocator_type.difference_type to be true to the C++ definition
        # but cython doesn't support deferred access on template arguments
        ctypedef size_t size_type
        ctypedef ptrdiff_t difference_type

        cppclass iterator:
            T& operator*()
            iterator operator++()
            iterator operator--()
            iterator operator+(size_type)
            iterator operator-(size_type)
            difference_type operator-(iterator)
            bint operator==(iterator)
            bint operator!=(iterator)
            bint operator<(iterator)
            bint operator>(iterator)
            bint operator<=(iterator)
            bint operator>=(iterator)
        cppclass reverse_iterator:
            T& operator*()
            reverse_iterator operator++()
            reverse_iterator operator--()
            reverse_iterator operator+(size_type)
            reverse_iterator operator-(size_type)
            difference_type operator-(reverse_iterator)
            bint operator==(reverse_iterator)
            bint operator!=(reverse_iterator)
            bint operator<(reverse_iterator)
            bint operator>(reverse_iterator)
            bint operator<=(reverse_iterator)
            bint operator>=(reverse_iterator)
        cppclass const_iterator(iterator):
            pass
        cppclass const_reverse_iterator(reverse_iterator):
            pass
        vector() except +
        vector(vector&) except +
        vector(size_type) except +
        vector(size_type, T&) except +
        #vector[input_iterator](input_iterator, input_iterator)
        T& operator[](size_type)
        #vector& operator=(vector&)
        bint operator==(vector&, vector&)
        bint operator!=(vector&, vector&)
        bint operator<(vector&, vector&)
        bint operator>(vector&, vector&)
        bint operator<=(vector&, vector&)
        bint operator>=(vector&, vector&)
        void assign(size_type, const T&)
        void assign[input_iterator](input_iterator, input_iterator) except +
        T& at(size_type) except +
        T& back()
        iterator begin()
        const_iterator const_begin "begin"()
        size_type capacity()
        void clear()
        bint empty()
        iterator end()
        const_iterator const_end "end"()
        iterator erase(iterator)
        iterator erase(iterator, iterator)
        T& front()
        iterator insert(iterator, const T&) except +
        iterator insert(iterator, size_type, const T&) except +
        iterator insert[Iter](iterator, Iter, Iter) except +
        size_type max_size()
        void pop_back()
        void push_back(T&) except +
        reverse_iterator rbegin()
        const_reverse_iterator const_rbegin "crbegin"()
        reverse_iterator rend()
        const_reverse_iterator const_rend "crend"()
        void reserve(size_type)
        void resize(size_type) except +
        void resize(size_type, T&) except +
        size_type size()
        void swap(vector&)

        # C++11 methods
        T* data()
        void shrink_to_fit()


Почему всего лишь -O1? Хотя бы уж -Ofast -ftree-vectorize -msse2

А в чём собственно проблема? Я описал почему мы используем так.
У Python нет vector. Это биндинг из Cython
А я повторю вопрос: причём тут C++? Зачем использовать Cython без его использования? Вопрос это не такой простой и глупый, как кажется. Что было бы, если бы вы не контейнер втянули, а сам словарь написали на С++, а в питон пробросили методы?
Я описал почему мы используем так.
Нет, не описал. «Мы используем -O1» — это не аргумент.
Нет, не описал. «Мы используем -O1» — это не аргумент.

Наверно мало в тексте было написано:
Очень хочется, чтобы можно было собирать *.whl для проекта и чтобы это всё завелось на практически любой системе

Были проблемы при установке на машинах со слегка отличной архитектурой, поэтому поставили флаг -O1. В любом случае, наличие большей оптимизации производительности не принесло или добавило её незначительно (меньше 0.01с), поэтому мы используем для сборки такой уровень оптимизации. Как я уже написал в тексте, была цель сделать максимально приближено к условиям реальной эксплуатации.

Что было бы, если бы вы не контейнер втянули, а сам словарь написали на С++, а в питон пробросили методы?

Это вот вопрос уже интереснее, но не думаю, что профит был бы намного больше. Хотя думаю стоит проверить и такой вариант, для чистоты эксперимента. Впрочем, такой вариант использовать станет очень неудобно, поэтому будет не соблюдено главное требование — удобство при разработке. Т.к. этот код работать будет в библиотеке, то наверняка его захочется потом перегрузить и т.п. В случае с python/cython это решается просто, а с чистым C++ будет много лишней возни, как, например, наследование и т.п. Можно конечно только одну функцию вставки/обработки/сортировки использовать, но не настолько большой профит будет (да и не факт, что будет).

Если при включении оптимизации выше чем O1 в релизной сборке у вас начинаются проблемы, то это прямой показатель того, что что-то где-то криво запрограммировано.

До этого было -O3. Решили плавно наращивать и смотреть по отзывам. Разницы в целом не было, кроме -O0.

Были проблемы при установке на машинах со слегка отличной архитектурой
Если вы не планируете разворачиваться на Pentium 2, проблем быть не должно. По крайней мере, на актуальных архитектурах, включая атомы.
не думаю, что профит был бы намного больше
Зависит от квалификации программиста и сценариев использования. Тут у недавнего поста уравнение Френеля для миллиона нормалей считается 3-5 секунд. habr.com/company/otus/blog/433588

Ну, вот, смотрите, берём самый тупой пример, переписанный с вашего. Я использую старые версии компилятора, в которых нет any, поэтому данные будут храниться либо в map<string,string>, либо в структуре.
Исходники
struct Dict 
{
    std::string name;
    int test_data;
    std::string test_data2;
    std::vector<int> test_data3;
    std::map<std::string,std::string> properties;
};

typedef std::vector<std::map<std::string,std::string>> dictionary;
typedef std::vector<Dict> dictionary2;

int range_attempts = 1000000;

std::vector<int> range(int N) {
    std::vector<int> v (N, 0);
    for (int i = 1; i < N; ++i) {
        v[i] = i;
    }
    return v;
}

void runtest(dictionary & dict)
{
    long long a, b;
    QElapsedTimer timer;
    dict.clear();
    timer.start();
    dict.assign(range_attempts, std::map<std::string,std::string>());
    for (int i = 0; i < range_attempts; ++i) {
        dict[i]["name"      ] = "test_" + std::to_string(i);
        dict[i]["test_data" ] = std::to_string(i);
        dict[i]["test_data2"] = std::to_string(i);
        dict[i]["test_data3"] = std::to_string(i);
    }
    a = timer.elapsed();
    dict.clear();
    b = timer.elapsed();
    qDebug() << a * 0.001 << b * 0.001;
}

void runtest2(dictionary2 & dict)
{
    long long a, b;
    QElapsedTimer timer;
    dict.clear();
    timer.start();
    dict.assign(range_attempts, Dict());
    for (int i = 0; i < range_attempts; ++i) {
        dict[i].name       = "test_" + std::to_string(i);
        dict[i].test_data  = i;
        dict[i].test_data2 = std::to_string(i);
        dict[i].test_data3 = range(10);
    }
    a = timer.elapsed();
    dict.clear();
    b = timer.elapsed();
    qDebug() << a * 0.001 << b * 0.001;
}

void test(QWidget *parent) 
{
    dictionary dict;
    for (int i = 0; i < 10; ++i) {
        runtest(dict);
    }
    dictionary2 dict2;
    for (int i = 0; i < 10; ++i) {
        runtest2(dict2);
    }
}


Собираем в gcc 4.8.*, запускаем, получаем в среднем 24.203 и 10.115 секунд для чисто строкового контейнера и структуры, соответственно. ОГО! У вас куда быстрее, чем у меня, есть чему порадоваться! Не спешите. Берём MSVC 2014 — и получаем 2.4 и 0.86 секунд. Да, компилятор решает. Получается, у вас быстрее? Есть одно «НО». Intel Core2 CPU 6420 @ 2.13 GHz
Запускаем на Ryzen 3 2200G и видим уже 1.3 и 0.5 секунд для gcc и 1.05 и 0.39 секунд для MSVC. И потребление памяти на уровне 220 Мб и 180 Мб, соответственно. Опция компиляции -O2 без агрессивных ключей. Без оптимизации под задачи, без каких-либо ухищрений. Без многопотока.

Делайте выводы сами.
Если вы не планируете разворачиваться на Pentium 2, проблем быть не должно. По крайней мере, на актуальных архитектурах, включая атомы.

Ответ несостоятелен, автор имеет ввиду march, который никого отношения к O не имеет.

Исходники

Это не исходник, а неведомая портянка с хрен пойми зачем туда впихнутым Qt. При этом ладно бы core, но там gui.

Собираем в gcc 4.8.*, запускаем, получаем в среднем 24.203 и 10.115 секунд для чисто строкового контейнера и структуры, соответственно.

Вменяемость цифр крайне сомнительна.

Берём MSVC Да, компилятор решает.

Тезис несостоятелен, MSVC является самым слабым компилятором — это первое. Второе, определяющим тут является качество имплементации stdlib.

Запускаем на Ryzen 3 2200G и видим уже 1.3 и 0.5 секунд для gcc и 1.05 и 0.39 секунд для MSVC.

Как это соотносится с:
запускаем, получаем в среднем 24.203 и 10.115


Опять же, запустить портянку без заморочек не представляется возможным. Полезность её крайне сомнительна.

Так я и не говорил, что такие числа по времени будут у всех на всех процессорах. Я не совсем понял какие выводы следовало бы сделать?
Но я все равно хочу попробовать вынести весь C++ код в .h и посмотреть как это повлияет.

UFO just landed and posted this here
Ну во-первых. Тут полностью неадекватная модерация — авторы не пропускают критику, а модераторов либо нет, либо они мотивированы поступать аналогично. Поэтому, в данном случае, этой некий нейтральный(с уклоном в сторону автора) ответ призванный пройти модерацию.

Во-вторых, небезопасно это потому, что fast — это fast-math, который разрешает компилятору изменения порядка вычислений для плавучки, что небезопасно т.к. изменения порядка изменяет результаты вычислений.
UFO just landed and posted this here
UFO just landed and posted this here

У вас vector<Py_Object *> создаётся через new, а освобождается через free().
И соответственно PyDict_new создаётся, но Py_DECREF нет. По этому pyo3 рулит, но это не точно =]

Спасибо за заметку. В понедельник попробую это проверить в сочетании с тем, что предложил iCpu по поводу полного переноса кода с вектором в C++.

А ведь я не поленился. Самое интересное тут то, что питон я никогда в глаза не видел. К тому же мне непонятно — что там на С++ собрались люди писать, если они не то что С++ не знают — они даже какие-то базовые основы программирования не особо знают.

Если кратко. Я взял этот код, благо википедия помогла его запустить. Я посмотрел на два сгенерированных листинга и увидел PyDict_SetItemString и PyDict_SetItem. Далее, я загуглил PyDict_SetItemString и оказалось, что эта функция создаёт пистон-объект из си-строки. Вторая же функция принимает пистон-строку, да мало того, что пистон-строку — он ещё «кэширует» все литералы.

И что же я сделал? Сделал невозможное — поменял одно на другое. Генератор создал пистон-строку(что очевидно, т.к. в коде используется пистон-строка) и закэшировал(по принципу си) её. Всё аналогично генерации из питона.

И о чудо — test_vector стала быстрее. Т.к. у вас никаких тестов нет — проверить валидность я не смог. В любом случае несостоятельность статьи нотариально доказана.

Теперь по поводу общей глупости в статье. Ну во-первых, что измеряют эти тесты? Они измеряют вот это:

dict(
            name = 'test_{}'.format(i),
            test_data = i,
            test_data2 = str(i),
            test_data3 = range(10),
        )


И это очевидно. Какое отношение бенчмарк, где создание dict занимает 99% времени измеряет оверхед на list? Никаким. И во-вторых — тут никак не сослаться на ошибку, ведь проверяется это очень просто:

        data_list.append(dict(
            #name = 'test_{}'.format(i),
            test_data = i,
            #test_data2 = str(i),
            #test_data3 = range(10),
        ))


И о чудо — код с вектором в 3раза быстрее.

О таких «мелочах» как:

data_list.resize(range_attempts)

Я даже говорить не буду.
Не то, чтобы я защищаю автора, — он сам за себя ответит, но статья как чисто прикладная задача очень интересна. Если нужно создавать объекты — их нужно создавать (привет, К.О.), а разные сценарии работы требуют разные способы инициализации данных. К примеру, правильное использование Cython требует наличие головы и рук, которые должны расти хотя бы из жопы, а не в неё. То же с тестами.

Так что с некоторыми аргументами автора я полностью согласен: если на питоне он может это написать относительно качественно и быстро — не надо давать ему гранату, пусть и дальше пишет на питоне.
но статья как чисто прикладная задача очень интересна.

Чем?

Если нужно создавать объекты — их нужно создавать (привет, К.О.), а разные сценарии работы требуют разные способы инициализации данных.

Что из этого следует и зачем это было написано? Кто-то говорил о какой-то инициализации данных? Нет.

К примеру, правильное использование Cython требует наличие головы и рук, которые должны расти хотя бы из жопы, а не в неё.

И? К чему это? Причём тут Cython?

Так что с некоторыми аргументами автора я полностью согласен: если на питоне он может это написать относительно качественно и быстро — не надо давать ему гранату, пусть и дальше пишет на питоне.

Никаких аргументов нет и небыло. Статья целиком и полностью несостоятельна — сравнение некорректно, выводы некорректны.

Скорее выводы из этого комментария несостоятельны, потому как я в статье очень конкретно обозначил, что цель написать не просто супероптимизировано, но так чтобы это укладывалось в рамках задачи: массив словарей.
Если просто поотключать всё из кода, то он безусловно будет работать около 0мс, но зачем такой код нужен?


Цель же статьи не сравнить C++ и Python, а сравнить возможность и необходимость решения задачи, используя те или иные возможности C++ расширения.

Пациент пошел ставить минусы всем моим комментам. Это настолько глупо.

Скорее выводы из этого комментария несостоятельны

Основания для этих нелепых попыток?

что цель написать не просто супероптимизировано, но так чтобы это укладывалось в рамках задачи: массив словарей.

К чему написан этот нелепый набор слов? Я где-то говорил о каких-то оптимизациях? Нет.

Если просто поотключать всё из кода, то он безусловно будет работать около 0мс, но зачем такой код нужен?

К чему написан этот нелепый набор слов? Я где-то говорил, что нужно что-то отключать? Нет. Я сообщил о несостоятельности бенчмарков и сравнения, причины были обозначены — есть что возразить — возражайте под оригинальным комментом, цитируя то, на что возражаете.

Цель же статьи не сравнить C++ и Python, а сравнить возможность и необходимость решения задачи, используя те или иные возможности C++ расширения.

К чему написан очередной нелепый набор слов? Выше мною была доказана несостоятельность как ваша, так и ваших попыток что-то сравнивать.
Пациент пошел ставить минусы всем моим комментам. Это настолько глупо.
Глупо жаловаться на то, что вам ставят минусы. Возможно это связанно с тем, что вы вместо того, чтобы услышать людей, доказываете какую-то свою правоту:
К чему написан очередной нелепый набор слов?

Я сообщил о несостоятельности бенчмарков и сравнения… Ну во-первых, что измеряют эти тесты?.. И это очевидно. Какое отношение бенчмарк, где создание dict занимает 99% времени измеряет оверхед на list? Никаким.

Кроме поверхностных выводов, вы статью читали (не только сам скрипт)? Вопрос стоял вполне себе конкретно: нужно было обработать один большой массив словарей (не map'ов, не структур, а конкретно словарей типа dict из Python), да и сделать это так, чтобы потом не пришлось танцевать с бубном, чтобы использовать этот массив в Python-коде. Цели переписать всё на C++ не было.
Что из этого следует и зачем это было написано? Кто-то говорил о какой-то инициализации данных? Нет.

А как работать с данными, если их нет? Даже если бы данные где-либо инициализировались до этого, то после потребовалось бы их всё равно туда складывать, поэтому задача приближена не к синтетическим тестам, а к реальной эксплуатации, то как оно реально будет использоваться.

Более того, основной посыл статьи чётко обозначен:
После этого эксперимента, мы решили сильно не заморачиваться с поддержкой Python 2.7 и переделкой каких-либо частей приложений на C++, просто потому что оно того не стоит. Всё уже написали до нас, нам остаётся это лишь правильно применить для решения конкретной задачи.

Даже если мы всю реализацию переместим в C/C++ — это ничего не даст, потому что по прежнему мы будем работать в Python и оперировать его объектами. Для C/C++ есть своё место здесь — вести расчёты или полностью выносить критический функционал, однако это может стоить ещё дороже, ведь по факту, придётся делать то же, что уже реализовано целым сообществом разработчиков Python и не факт, что мы справимся лучше.
Глупо жаловаться на то, что вам ставят минусы. Возможно это связанно с тем, что вы вместо того, чтобы услышать людей, доказываете какую-то свою правоту:

Я слушаю людей, просто я не понимаю на что надо отвечать. Вы выкатываете мне какие-то нелепые тезисы, обвиняете меня в том, чего я не делаю. Оправдываетесь тем, что к делу отношения не имеет.

Кроме поверхностных выводов, вы статью читали (не только сам скрипт)? Вопрос стоял вполне себе конкретно: нужно было обработать один большой массив словарей

Вы опять пытаетесь выдать голоса в своей голове за мои тезисы. Про «не словари» говорил вам не я, про оптимизации говорил вам не я.

Моя претензия по поводу словарей состояла в том. Давайте я вам объясню попроще. Мы хотим сравнивать температуру чая и кофе. Как вы это делаете? Вы взяли два стакана с водой(при этом, как оказалось, разной температуры), капнули в один каплю чая, во второй каплю кофе и получили какие-то результаты. И вы пошли дальше, вы взяли не по одному, а по 4 стакана, слили их в ведро и произвели описанные выше манипуляции.

Очевидно, что человек обладающей банальной логикой сможет осознать проблему происходящего, вернее всю нелепость происходящего. Тут не нужно знать питон, С++, нужно иметь элементарный кругозор доступный любому программисту, особенно тому, который пытается что-то измерять, что-то оценивать. Ничего более я не говорил.

Т.е. вопрос звучит так. Если основная работа у вас в словарях, то зачем и для чего вы начали сравнивать вектор/питон-список? Какой в этой смысл?

(не map'ов, не структур, а конкретно словарей типа dict из Python)

Вы опять всё перепутали. Это было в другом комменте от другого человека. По поводу моих претензий — они заключались в том, что декларируя сравнения С++-вектора/питон-списка вы не сравнивали вектор/список, а сравнивали хрен пойми что.

А как работать с данными, если их нет? Даже если бы данные где-либо инициализировались до этого, то после потребовалось бы их всё равно туда складывать, поэтому задача приближена не к синтетическим тестам, а к реальной эксплуатации, то как оно реально будет использоваться.

Абсолютно нелепое заявление. В контексте сравнения вектора/списка никого не должно это интересовать. Пример со стаканами я вам привёл. К тому же, эта инициализация у вас была не идентична и даже тут вы засыпались.

Более того, основной посыл статьи чётко обозначен:

И? Это выводы основанные на несостоятельных измерениях и интерпретациях. Да и сама постановка задачи нелепа. Давайте за секунду пройдём по вектору указателей 1кк раз.

Ладно, я могу предположить, что вы не знали о том, что эта задача исполняется на порядки быстрее, а значит время отработки логики вектора в задаче единицы, доли процента. Очевидно, что если мы ускорим список в 10раз — мы ускорим программу на 10%, но это не значит, что список равен вектору и наоборот.

Тут куда не глянь везде треш. Т.е. просто непонятно как вы пришли к подобным выводам, причём тут вектор, причём тут это сравнение. Зачем говорить о векторе, а потом съезжать «нам неважно что там с вектором». Слишком сложно для меня.

Даже если мы всю реализацию переместим в C/C++ — это ничего не даст, потому что по прежнему мы будем работать в Python и оперировать его объектами.

Вы уже всю реализацию переместили в С/С++. В этом суть cython. Далее уже сравнивается нативные для С/С++ решения, либо другие решения на С/С++ созданные для питона.

Кстати, в понятие «всю» не входит никакой питон. Я могу предположить, что в cython можно реализовать какие-то модули для обычного питона и через это интегрировать их с уже существующем кодом на питоне.

Для C/C++ есть своё место здесь — вести расчёты или полностью выносить критический функционал

Где здесь? В мире питона? Ну дак вы говорили не об этом, вы утверждали, что «использование С++ ничего не даёт»(зачем вам тогда cython, если оно ничего не даёт? Или вы про ручное переписывание? Ну дак зачем в cython этот функционал? Его авторы идиоты и делают ненужно?). Теперь вы утверждаете обратное.

однако это может стоить ещё дороже

Это как в случае выше? Очень весомый аргумент.

ведь по факту, придётся делать то же, что уже реализовано целым сообществом разработчиков Python и не факт, что мы справимся лучше.

Это очень глупый тезис. Разработчики питона реализовывали логику языка питон, его примитивов и прочего. Очевидно, что для решения любой дачи не нужны эти примитивы и вы сами это подтвердили. Вы можете использовать питон-лист, а можете С++-вектор.

Мне даже не хочется обсуждать эту примитивную подмену понятий. Вы пишете код, который решает конкретную задачу и это задача не та, которую решают авторы питона. Эти задачи могут где-то пересекаться, но очень редко.

К тому же, у «разработчики С++» так же не глупые и могут написать нормальный список, нормальную мапу и прочее. Только тут нет тех ограничений, которые есть в пистоне(как и любом скриптовом языке).

Я вообще первый раз вижу питон, но посмотрев на описание pylong я увидел, что там каике-то 256битные числа(или что-то типа того). Я посмотрел на строки, которые в utf-8. Очевидно, что подобные решения удобны для некоего общего случая использования, но они чего-то стоят, они очень много стоят.

Нужны ли вам в ключах utf8? Сомневаюсь. Нужны ли вам числа шире 64бит? Сомневаюсь. И абсолютно неважно, кто является разработчиков питона — написать быстрее utf8-строки, либо 256битные числа — невозможно. Под быстрыми имеется ввиду относительно базовых.

В этом смысл С/С++-решений. Вы не платит за то, что вам ненужно. Вам нужны 256битные числа? Пожалуйста, какие угодно. Не нужны? Берите базовые и быстрее. Нужны utf8строки? Берите. Не нужны — берите быстрее базовые.

Если кратко. Я взял этот код, благо википедия помогла его запустить. Я посмотрел на два сгенерированных листинга и увидел PyDict_SetItemString и PyDict_SetItem. Далее, я загуглил PyDict_SetItemString и оказалось, что эта функция создаёт пистон-объект из си-строки. Вторая же функция принимает пистон-строку, да мало того, что пистон-строку — он ещё «кэширует» все литералы.

Вот это интересно. Надо будет в пн попробовать добавить в скрипт и посмотреть что выйдет.
from libc.stdlib cimport free
...
from libcpp.vector cimport vector

...

cdef dict_vec *data_list
data_list = new dict_vec()
...
free(data_list)

Если C++ не знаете, идёте в документацию по Cython и читаете:


C++ objects can be dynamically allocated with new and del keywords.

Намёк на то, что можно было вместо free() использовать del data_list? Есть ли от этого какая-то практическая польза?

Нельзя использовать free для удаления объектов, созданных через new! free нужно использовать только для освобождения памяти, выделенной через malloc. Это же азы, ну как так то, ребята?


И вообще, я не вижу никакой практической пользы в создании объекта вектора динамически. Зачем вы вообще это делаете?

И вообще, я не вижу никакой практической пользы в создании объекта вектора динамически. Зачем вы вообще это делаете?

Эксперимент. В рамках Django это нормальное явление, поэтому так и сделали.
Нельзя использовать free для удаления объектов, созданных через new! free нужно использовать только для освобождения памяти, выделенной через malloc. Это же азы, ну как так то, ребята?

Да, это мой косяк в силу недостатка знания. В пн попробую реализовать через malloc и обновлю статью.
Да, это мой косяк в силу недостатка знания. В пн попробую реализовать через malloc и обновлю статью.

Зачем malloc?! Зачем пытаться создавать вектор динамически? std::vector сам управляет памятью под свои элементы, это по сути динамический массив, вам не надо заморачиваться вообще с управлением памятью для этого контейнера и создавать его объект в куче. Просто создаёте объект вектора на стеке и резервируете необходимое количество памяти под количество элементов, которые вам надо хранить, с помощью vector.reserve и всё.

Sign up to leave a comment.

Articles

Change theme settings