LINUX.ORG.RU

Правильное использование realloc

 ,


0

5

Статические анализаторы любят ругаться на такую конструкцию:

ptr=realloc(ptr, size);

Если память не выделится, то в ptr запишется 0, а старое значение ptr будет потеряно, ужас-ужас.
Ну и что? Если память не выделилась, то значит, все очень плохо и программе нет смысла работать.
Она грохнется при попытке разыменовать ptr.

★★
Ответ на: комментарий от anonymous

кинуть исключение/вернуть ошибку наверх

наверх - это куда?
я не про функцию или библиотеку какую-то написал, а про программу в целом

dt1 ★★
() автор топика
Ответ на: комментарий от dt1

наверх - это куда?

Туда, откуда вызвали текущую функцию.

я не про функцию или библиотеку какую-то написал, а про программу в целом

Нет никакой «программы в целом», как и нет никакой гарантированной «памяти в системе», она пропадет сразу после проверки, или проверка может сфейлить. Делай правильно в каждом частном случае и все будет хорошо.

anonymous
()
Ответ на: комментарий от dt1

он не зря по умолчанию работает

Чё, серьёзно?

можно, конечно, его отключить, но система при нехватке памяти фактически подвиснет

Кроме OOM есть лимиты на процесс (см. ulimits) и разного рода контейнеры с ограничениями (см. cgroups). Существуют malloc-совместимые аллокаторы в которых можно искусственно ограничить память приложению и вообще готовить её как угодно.

Ещё раз повторю, если malloc(...) вернул NULL, то это не значит что памяти нет, дальше начнёт действовать OOM Killer и приложение не сможет доработать корректно.

уж как по мне, то лучше в таком случае завершить прогу аварийно, чем пытаться что-то показывать и сохраняться

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

mashina ★★★★★
()
Ответ на: комментарий от mashina

Библиотекам разумно обрабатывать NULLы чтобы их можно было использовать в разных сценариях.

++

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

Вот уже внутри конкретных оберток можно спокойно решать: выдать предупреждение или завершить с ошибкой. В простых фиговинах вполне можно и так:

// now allocate memory: gpamount + rest_pars_num + 1 for terminating NULL
        Glob->ports = realloc(Glob->ports, gpamount + 1);
        if(!Glob->ports) ERR("Realloc");
(это мультитерминальный сниффер — нет смысла его запускать, если будут проблемы с именованием или открыванием хотя бы одного из N портов, заданных пользователем).

А вот если там идет обработка кучи данных, и не получилось лишь новую порцию засунуть, лучше обработку продолжить, а пользователя предупредить, что набор неполный.

anonymous
()
Ответ на: комментарий от invy

не использовать realloc
std::vector::resize

Я бы объяснил разницу, но шаблонный умишко типичного крестовика просто не поймет. Если все же хочешь попытаться: при определенных условиях realloc может быть выполнен без копирования, в отличие от крестовых убогих new/delete.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

Умишко типичного си-я-у-мамки-хакира не понимает, что если у него узкое место - реаллокация, то у него либо вообще проблема алгоритме как таковом, либо неадекватная стратегия увеличения размера выделяемых данных.

И ты не поверишь, vector::resize при определенных условиях тоже ничего никуда копировать не будет. См. всё ту же стратегию роста.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от kawaii_neko

Тут где-то уже всплывала эта ссылочка, запощу ещё раз.

http://blog.httrack.com/blog/2014/04/05/a-story-of-realloc-and-laziness/

We’re pretty done here: the kernel never actually touched a single bit of memory related to our allocated block, and just swapped few bits to relocate the entire region elsewhere.

We may argue that we are still O(N) technically, but

  • instead of copying entire pages of 4KB (or 2MB for huge pages), we are swapping integers inside the kernel structure
  • we are touching hot structures in the kernel, neither cold memory, nor bytes swapped on disk

Therefore, we are O(N), but with a huge divisor.

Oh, by the way, did you know that O(N) was in many cases misleading ?

In this case, N can be at most 2^48 (the maximum virtual space size). Most machines only deal with few gigabytes of memory, at most 64GB for most architectures, that is, 2^36 bytes. A huge page is 2^21 bytes, leaving 2^15 operations at most for a mremap (yes, that’s only 32768 operations).

Well, the worst case scenario is cheap in all cases, and this is what I suspected from the beginning.

Further read: do not hesitate to have a look at Gorman’s book Understanding the Linux Virtual Memory Manager.

TL;DR: do not worry, be lazy, and trust realloc.

doushiyou
()
Ответ на: комментарий от invy

И ты не поверишь, vector::resize при определенных условиях тоже ничего никуда копировать не будет

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

kawaii_neko ★★★★
()
Ответ на: комментарий от invy

Почему вы такие склочные?

И ты не поверишь, vector::resize при определенных условиях тоже ничего никуда копировать не будет. См. всё ту же стратегию роста.

malloc + memcpy + free, которые в конечном счёте использует std::vector (хорошо ещё, если memcpy), на порядок медленнее realloc, и ты с этим ничего не сделаешь (прочитай статью по моей ссылке). Использовать realloc ему мешает то, что он обязан дёргать функции данного ему в качестве шаблонного параметра аллокатора для выделения памяти, а у него есть только allocate да deallocate.

doushiyou
()
Ответ на: комментарий от invy

Кстати:

Умишко типичного си-я-у-мамки-хакира не понимает, что если у него узкое место - реаллокация, то у него либо вообще проблема алгоритме как таковом, либо неадекватная стратегия увеличения размера выделяемых данных.

Facebook такие баки, ага.

https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md#object-r...

doushiyou
()
Ответ на: комментарий от doushiyou

Ты так и не понял, что я написал. Прочти еще раз, подумай. realloc в конечном итоге тоже выполнит malloc+memcpy+free, если не получится увеличить размер. Точно так же работет vector::resize: если влазит в зарезервированное место, то никакой аллокации не будет.

invy ★★★★★
()
Ответ на: комментарий от dt1

Кстати, кьютишные QList, QByteArray и QString используют realloc.

И не используют убогую концепцию std::allocator. Только к 17-му стандарту до плюсомудрецов начало что-то доходить и они замахнулись на polymorhic_allocator. Глядишь, лет через 20 допрут до нужности realloc-а.

kawaii_neko ★★★★
()
Ответ на: комментарий от invy

Ты вообще в курсе что существуют всякие там MMU и что есть виртуальная и физическая память? Если бы ты был в курсе, то догадался бы, что для того, чтобы сделать realloc вообще необязательно что-то куда-то копировать. В любом случае. Единственное условие для успешного realloc - наличие свободных страниц физической памяти. Если они есть, то никто никуда ничего копировать не будет. Если нет - то realloc завершится с ошибкой.

Вред от «языков высокого уровня» с «высоким уровнем абстракции» и кучей ненужных баззвордов - налицо. А потом все удивляются, почему софт всё тормознее и тормознее. :)

Stanson ★★★★★
()
Ответ на: комментарий от invy

realloc в конечном итоге тоже выполнит malloc+memcpy+free, если не получится увеличить размер

Для кого я цитировал статью? Для кого я писал «прочитай статью по моей ссылке»? А?

Не тебе бенчмарк (первый мерял какую-то фигню, удалил его):

http://codervil.blogspot.ru/2012/11/how-to-outperform-stdvector-in-1-easy.html

doushiyou
()
Ответ на: комментарий от kawaii_neko

Глядишь, лет через 20 допрут до нужности realloc-а.

Пользы то от этого будет мало. В плюсах нельзя же просто так скопировать объект из одного куска памяти в другой (fall-back режим работы realloc включение которого заранее угадать нельзя), в общем случае по крайней мере. Для критичных мест проще накодить свою обёртку над realloc или вообще использовать цепочки буферов.

mashina ★★★★★
()
Ответ на: комментарий от invy

realloc в конечном итоге тоже выполнит malloc+memcpy+free, если не получится увеличить размер

Как ты пять звёзд-то набрал?

mix_mix ★★★★★
()
Ответ на: комментарий от doushiyou

вот вы на него накинулись, я на следующем примере:

#include <QList>
#include <QVector>
#include <vector>

#include <QElapsedTimer>
#include <QDebug>

int main(int argc, char *argv[])
{
    int *a = (int *)malloc(sizeof(int));
    QVector<int> v;
    std::vector<int> v1;
    QList<int> l;

    QElapsedTimer timer;

    timer.start();
    for (int i = 0; i < 10000000; i++)
        a = (int *)realloc(a, (i+2)*sizeof(int));
    qDebug() << "C realloc  " << timer.elapsed();

    timer.start();
    for (int i = 0; i < 10000000; i++)
        v1.push_back(1);
    qDebug() << "std::vector" << timer.elapsed();

    timer.start();
    for (int i = 0; i < 10000000; i++)
        l.push_back(1);
    qDebug() << "QList      " << timer.elapsed();

    timer.start();
    for (int i = 0; i < 10000000; i++)
        v.push_back(1);
    qDebug() << "QVector    " << timer.elapsed();

    return 0;
}

получаю
C realloc   108
std::vector 43
QList       95
QVector     68

dt1 ★★
() автор топика
Ответ на: комментарий от kawaii_neko

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

mashina ★★★★★
()
Последнее исправление: mashina (всего исправлений: 1)
Ответ на: комментарий от dt1

я на следующем примере

А ты вообще понимаешь, что в приведенном тобой коде происходит? Спрашиваю для порядку, потому как ты явно не в курсе, как работают контейнеры. Можешь еще побайтовое чтение из файла с std::ifstream::get сравнить.

kawaii_neko ★★★★
()

Ты задолбал отбрыкиваться на советы своим «не факт, что...»!

Не факт, что завтра комп вообще включится. Значит его сегодня надо выкинуть?

Не факт, что ты завтра вообще проснешся. Значит надо сегодня...

anonymous
()
Ответ на: комментарий от dt1

он не зря по умолчанию работает

админ отдельная профессия тоже не зря

можно, конечно, его отключить, но система при нехватке памяти фактически подвиснет

вообще не про это

вообще, когда свободной памяти мало, тормоза начинаются нереальные

нет

уж как по мне, то лучше в таком случае завершить прогу аварийно, чем пытаться что-то показывать и сохраняться

хорошо, что ты не пишешь код

anonymous
()

Она грохнется при попытке разыменовать ptr.

Если программист быдлокодер - то да. А если нет, то там как минимум должен быть if (!ptr) abort(); А лучше - корректная обработка этого события. Самое простое - корректно освободить ресурсы и сохранить по возможности данные пользователя. Самое правильное - не упасть в принципе, если можно. А, например, выдать пользователю сообщение о нехватки памяти и отказаться выполнять запрошенное действие, продолжив работать с тем, на что памяти хватило.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от kawaii_neko

А ты вообще понимаешь, что в приведенном тобой коде происходит?

Чтоб быть объективным, в коде на С не так уж часто можно встретить преаллокацию под будущий рост буфера.

anonymous
()
Ответ на: комментарий от dt1

У тебя в твоём «C realloc» реальный realloc вызывается 10 миллионов раз, а в «QList» и «QVector» тот же самый realloc вызывается на порядки реже (не помню, как там конкретно преаллокация считается, в qAllocMore ЕМНИП смотреть надо). :)

Stanson ★★★★★
()
Ответ на: комментарий от dt1

http://www.cplusplus.com/reference/vector/vector/

Internally, vectors use a dynamically allocated array to store their elements. This array may need to be reallocated in order to grow in size when new elements are inserted, which implies allocating a new array and moving all elements to it. This is a relatively expensive task in terms of processing time, and thus, vectors do not reallocate each time an element is added to the container.

Instead, vector containers may allocate some extra storage to accommodate for possible growth, and thus the container may have an actual capacity greater than the storage strictly needed to contain its elements (i.e., its size). Libraries can implement different strategies for growth to balance between memory usage and reallocations, but in any case, reallocations should only happen at logarithmically growing intervals of size so that the insertion of individual elements at the end of the vector can be provided with amortized constant time complexity (see push_back).

Therefore, compared to arrays, vectors consume more memory in exchange for the ability to manage storage and grow dynamically in an efficient way.

Собственно, то, сколько сейчас зарезервировано места в std::vector, можно узнать, вызвав capacity(). В libstdc++ от g++ вместимость умножается на два (если старая — не ноль, конечно), когда приходит время её увеличить:

#include <cstddef>
#include <cstdio>
#include <vector>

int main() {
    std::vector<int> v;
    std::printf("Initial capacity: %zu\n", v.capacity());
    std::size_t old_capa = v.capacity();
    for (int i = 0; i <= 1025; ++i) {
        v.push_back(i);
        if (v.capacity() != old_capa) {
            std::printf("size=%zu, capacity=%zu\n", v.size(), v.capacity());
            old_capa = v.capacity();
        }
    }
}
Initial capacity: 0
size=1, capacity=1
size=2, capacity=2
size=3, capacity=4
size=5, capacity=8
size=9, capacity=16
size=17, capacity=32
size=33, capacity=64
size=65, capacity=128
size=129, capacity=256
size=257, capacity=512
size=513, capacity=1024
size=1025, capacity=2048

Так что померяй-ка вот это:

    int *a = NULL;
    std::size_t capacity = 0;
    for (int i = 0; i < 10000000; ++i) {
        std::size_t old_len = i;
        if (old_len >= capacity) {
            if (capacity) {
                capacity *= 2;
            } else {
                capacity = 1;
            }
            if (!(a = (int*)std::realloc(a, capacity * sizeof(int)))) {
                std::abort();
            }
        }
        a[old_len] = i;
    }

doushiyou
()
Ответ на: комментарий от Stanson

Если бы ты был в курсе, то догадался бы, что для того, чтобы сделать realloc вообще необязательно что-то куда-то копировать.

Поскольку это НЕ стандартизировано, то realloc может на разных системах быть реализован самыми разными методами. В худшем случае - malloc/memcpy/free.

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

invy ★★★★★
()
Ответ на: комментарий от doushiyou

Этот бенчмарк тоже меряет какую-то фигню и непонятно как. (Там даже кода нет).

invy ★★★★★
()
Ответ на: комментарий от doushiyou

Самое смешное, что по твоей же ссылке в комментариях автор поста пишет:

I'm not sure I'd actually recommend using a realloc_vector in production code anyway.

In practice I think it's wiser to use a std::vector and reserve() memory up front, using an estimate of the final size if you don't know it exactly up front.

invy ★★★★★
()
Ответ на: комментарий от invy

Самое смешное, что...

ты услышал звон и не знаешь где он. Автор не осилил сделать корректный контейнер потому и не рекомендует свой тестовый пример.

mashina ★★★★★
()
Ответ на: комментарий от invy

Поскольку это НЕ стандартизировано, то realloc может на разных системах быть реализован самыми разными методами. В худшем случае - malloc/memcpy/free.

malloc/memcpy/free нужен _только_ на системах без MMU. Для систем с MMU всё стандартизировано делается без malloc/memcpy/free.

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

Сам по себе ресайз достаточно сложно сделать узким местом, хотя некоторые умудряются заниматься копированием при наличии MMU. А вот дурацкий, неправильный ресайз может вести к приличному перерасходу памяти из-за всяких идиотских самодельных преаллокаторов. Что собственно и наблюдается.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

всё стандартизировано

Где? Ссылку на стандарт, где сказано, как realloc будет работать.

Сам по себе ресайз достаточно сложно сделать узким местом

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

Что собственно и наблюдается.

Примеры?

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от invy

Где? Ссылку на стандарт, где сказано, как realloc будет работать.

Документация на процессоры. Раздел MMU. При наличии MMU делать realloc копированием - клинический идиотизм.

Примеры?

В сырцы вот этого всего барахла и смотри. QList ЕМНИП 2 или 4 страницы лишних загребает. У realloc оверхед максимум страница.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

Документация на процессоры. Раздел MMU.

Ну, что я и говорил - нету никаких стандартов относительно имплементации realloc'а. Это значит что рантайим может делать всё что угодно.

При наличии MMU делать realloc копированием - клинический идиотизм.

Кто сказал?

В сырцы вот этого всего барахла и смотри.

Я думал, ты примеры программ приведешь и успешных оптимизаций, где существенный перерасход устраняется заменой аллокатора.

invy ★★★★★
()
Ответ на: комментарий от invy

Ну, что я и говорил - нету никаких стандартов относительно имплементации realloc'а. Это значит что рантайим может делать всё что угодно.

И конечно же это повод не использовать потенциальные преимущества рантайма?

Смирись уже, что цпп - говно, не пригодное для написания системного софта (хотя имхо никакой сколько-нибудь сложный софт на нем хорошо не написать). А на прикладном поле, где во главе стоит скорость разработки, цпп также сливает всему. Вот и получается язык без задач.

Я думал, ты примеры программ приведешь и успешных оптимизаций, где существенный перерасход устраняется заменой аллокатора.

Firefox. Замена dlmalloc на jemalloc позволяет таки возвращать часть памяти системе.

kawaii_neko ★★★★
()
Последнее исправление: kawaii_neko (всего исправлений: 1)
Ответ на: комментарий от Stanson

Зато этот идиотский культелист/культевектор выделяет в разы больше памяти, чем нужно!

Я, кстати, как-то у кого-то (точно не припомню) натыкался на такой идиотизм: каждый раз, как память кончалась, ее количество realloc'ом удваивалось! Блин, ну а почему не удесятерялось? Дебильные погромисты... Переделал, чтобы каждый раз строго определенное количество памяти добавлялось.

anonymous
()
Ответ на: комментарий от kawaii_neko

потому как ты явно не в курсе, как работают контейнеры

телепаты в треде, кто-то явно не в курсе, что такое троллинг

dt1 ★★
() автор топика
Ответ на: комментарий от doushiyou

Так что померяй-ка вот это:

да понятно, что будет меньше, но видно же, что без всякого realloc 10 миллионов расширения std:vector происходит за какие-то 43 миллисекунды
нужно очень постараться, чтобы это стало узким местом в программе

dt1 ★★
() автор топика
Ответ на: комментарий от anonymous

Потому что, в отличии от тебя, это делали не дебильные программисты, как ты их назвал, а люди с ученой степенью в computer science. 2 - ротому что в среднем удваивание дает хороший компромис между частотой ресайзов и «перерасходом» памяти.

invy ★★★★★
()
Ответ на: комментарий от anonymous

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

Stanson ★★★★★
()
Ответ на: комментарий от kawaii_neko

И конечно же это повод не использовать потенциальные преимущества рантайма?

Это повод не тратить свое время на микрооптимизации которые еще и боком вылезти могут и использовать стандартные контейнеры.

Еще раз: если у тебя ресайз вызывает проблемы, то проблема в дизайне, а не в ресайзе. (Может быть за исключением некоторых единичных случаев).

invy ★★★★★
()
Ответ на: комментарий от doushiyou
C realloc    116
C realloc 2  13
std::vector  43
QList        203
QVector      61
dt1 ★★
() автор топика
Ответ на: комментарий от invy

Забавно:

в среднем удваивание дает хороший компромис между частотой ресайзов и «перерасходом» памяти.

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

Вот нахрена жрать в 2 раза больше памяти, если при ресайзе можно легко обойтись без копирования? Только от собственной тупости и помешанности на всяких сферических абстракциях в вакууме.

Кстати, на ARM и MIPS realloc работает гораздо быстрее std::vector.

Т.е. в угоду каким-то сферическим абстракциям надо жрать в 2 раза больше памяти и работать медленнее на абсолютном большинстве имеющихся на планете процессоров.

Stanson ★★★★★
()
Ответ на: комментарий от invy

А слабо не использовать баззворд «контейнеры» и говорить на нормальном человеческом языке? :) Или без баззвордов как-то уже повыпендриватся не получится, да?

если у тебя ресайз вызывает проблемы

Ещё раз. Ресайз ни у кого проблем не вызывает. Проблемы вызывает ужор памяти всякими сраными «контейнерами».

Stanson ★★★★★
()

Правильное использование realloc

{
  auto loc = realloc(ptr, size);
  if(loc)
    ptr = reinterpret_cast<MY_TYPE*>(loc);
  else {
    free(ptr);
    ptr = nullptr;
    throw std::bad_alloc;
  }
}

/thread

robus ★★★★★
()
Ответ на: комментарий от invy

в среднем удваивание дает хороший компромис между частотой ресайзов и «перерасходом» памяти

Только конченый дебил может, когда к 100МБ надо добавить пару килобайтных объектов, вместо того, чтобы добавить десяток килобайт, добавлять еще 100МБ! Из которых наверняка в дальнейшем 99МБ использоваться вообще не будет!

И представь, если таких контейнеров пара десятков!!! Вот и жрут говнобраузеры с говноохфисами кучу ресурсов. А писали бы их программисты, а не идиоты «с ученой степенью в CS», все было бы отлично!

anonymous
()
Ответ на: комментарий от anonymous

Только конченый дебил может, когда к 100МБ надо добавить пару килобайтных объектов,

Не вызывать reserve.

anonymous
()
Ответ на: комментарий от Stanson

Вот нахрена жрать в 2 раза больше памяти

В два раза по сравнению с чем? Если знаешь сколько её потребуется - просто резервируешь заранее и ничего лишнего выделяться не будет. Если не знаешь или «лень задумываться», кто тебе виноват? А ещё есть shrink_to_fit.

DarkEld3r ★★★★★
()
Ответ на: комментарий от Stanson

Вот нахрена жрать в 2 раза больше памяти, если при ресайзе можно легко обойтись без копирования?

Что-то у тебя с логикой какая-то проблема и с пониманием процесса.

Избыточное потребление памяти у тебя будет, если у тебя проблемы в дизайне программы и ты 100500 мегабайт фигачишь в вектор пушбэком.

Т.е. в угоду каким-то сферическим абстракциям надо жрать в 2 раза больше памяти и работать медленнее на абсолютном большинстве имеющихся на планете процессоров.

Не надо перекладывать с больной головы на здоровую. Если ты не способен выделить сколько тебе надо и полагаешься на ресайз/реаллок вообще, без необхоимости, то проблема не в имплементации контейнеров в плюсах.

invy ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.