LINUX.ORG.RU

Два философских вопроса по malloc/free

 , , ,


0

2

Здравствуйте. За месяц читания чужого Си-кода (и писания своего), меня не покидают два концептуальных вопроса про кучу

  1. Много где вижу конструкцию
    if(pth != NULL) free(pth);
    
    Несмотря на то, что в мане написано If ptr is NULL, no operation is performed. Откуда такое недоверие к манам?
  2. Каждый раз когда делаю malloc/free для локальных переменных (особенно если в цикле), в одном месте появляется неприятный зуд на тему «Память же фрагментируется. Нельзя часто выделять освобождать. Особенно много маленьких кусочков.» Оправдан ли этот зуд или все там норм с кучей?
★★★★★

Последнее исправление: beastie (всего исправлений: 1)

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

зачем ТС динамическая память, если он её убивает в конце функции

Для строк же. Сплит, джойн, конкат, форматированный вывод в строку. Тут даже C99 VLA arrays не сильно упростят жизнь.

Сначала заюзал bstrlib. Там если что можно было обернуть память на стеке в bstring, так что malloc сильно не парил. Но всязи с упоротостью этих строк перешел на sds. Значительно проще, но абсолютно всё на аллокациях. И тут начало зудеть )

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

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

Ибо у системы одни цели (многозадачность випервых рядах), у тебя другие, более узкие (плодить тонны короткоживущих объектов). И ничего не мешает подточить инструменты системы в своих целях.

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

Для строк же. сплит, джойн, конкат, форматированный вывод в строку

поменяй собственный взгляд на «что есть строка» и всё пройдёт..

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

ак ни печально

не печально, печально возможноe обратное

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

С чего начать?

с исходной задачи должно быть :-)

откуда взялись обилие split/join/concat с (начальными) строками ? синтаксический анализ и трансляция кода/текста ?

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

mock-заглушка для одного девайса с telnet-like протоколом.

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

Можно написать простенький сплиттер, возвращающий указатели на нужные куски исходной строки. А можно тупо вызвать split и получить массив строк

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

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

А ещё лучше вместо выделения множества мелких кусков выделить один большой и сделать пул.

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

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

Можно написать простенький сплиттер, возвращающий указатели на нужные куски исходной строки

this..возможно даже заюзать bison+flex (не всегда но как правило ДА), чтобы было понятно не только тебе - не все горят желанием разбирать/сопровождать/отлаживать парсеры

а вообще :

«mock-заглушка для одного девайса с telnet-like протоколом» - как оно стало bottle-neck ? что вы озаботились памятью/скоростью ??

покажите код - и вам помогут..

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

Выдели один раз длинный(N*PAGE_SIZE) буфер и в нём играйся, возвращать память в ОС по каждому чиху не обязательно.

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

и в нём играйся

Буфер-то я выделю. Но игрушек для него не завезли (или я не нашел). А игра опасными бритвами (mem*, str*, указатели) высасывает все силы)

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

bison+flex

Да там разделенные пробелами строки. Сплит строки на бизоне - это, ИМХО, достойно размещения на ibash.org.ru )

makoven ★★★★★
() автор топика

Несмотря на то, что в мане написано If ptr is NULL, no operation is performed. Откуда такое недоверие к манам?

Многим людям проще написать строчку кода, чем открывать ман. Но это плохой подход.

Каждый раз когда делаю malloc/free для локальных переменных (особенно если в цикле), в одном месте появляется неприятный зуд на тему «Память же фрагментируется. Нельзя часто выделять освобождать. Особенно много маленьких кусочков.» Оправдан ли этот зуд или все там норм с кучей?

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

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

вообще-то бизон нужен не для сплита а чтобы из потока лексем удобно-однотипно получить выражение с которым можно что-то сделать. Вы-ж не для спортивного интереса разбираете «строки разделённые пробелами» ? :-)

PS/ и что-то мне подсказывает что тема топика порождена обилием личного времени ТС чем реальной проблемой ..

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

Системе нужна свободная оперативка, стобы каждое приложение могло работать.

А программы с множеством объектов будут работать тем шустрее, чем реже надо будет оперативу чистить.

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

что ядро делает оверкоммит

Некоторые выключают этот костыль для говнокода, и ещё бывает не только Линукс.

anonymous
()

Прям таки философских

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

Производительность не сильно волнует. Больше простота кода. И чтобы долгоработающая програма внезапно не грохнулась из-за сильной фрагментированности

нафига тогда С?

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

Системе нужна свободная оперативка, стобы каждое приложение могло работать.

Какое отношение фрагментация хипа процесса имеет к системе? Правильно - никакого.

anonymous
()

Зуд оправдан. Посмотри на «современные» DE после недели аптайма.

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

Сплит

Я както давно сделал себе либу с легковесными строками (правда на C++). Но суть в том что там строка у меня это просто структура:

struct {
  const char *data;
  unsigned length;
}
Причем data ссылается на внешний буффер которым сама строка не владеет. Очень удобно использовать в различных текстовых процессорах (например прочитали строчку с сокета/файла в буффер и нужно ее распарсить). Очень частво весь процесс разбора (сплит, trim, lcut, rcut, substr, и тд) идет без единого malloc/free и без единого memcpy/strcpy/strdup ...

Но конечно там где уже нужно формировать новые строки (join, cat, sprintf) от аллокаций избавится сложнее.

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

Причем data ссылается на внешний буфер которым сама строка не владеет.

Называл бы это не «строка», а «string view».

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

C названиями у меня всегда туго было, бывает сидиш пол часа и думаеш как класс/неймспейс/библиотеку назвать. Уже есть четкое представление по реализации - а названия нет :)

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

А что такое «системный malloc» ? Есть реализация С функции malloc в glibc но он никак не системный, и многие программы его даже и не используют вовсе а притаскивают свой статически слинкованный ...

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

С названиями у меня всегда туго было

У всех так. Хорошее название трудно найти.

Просто в данном случае для подобной сущности уже есть готовое имя.

i-rinat ★★★★★
()
Ответ на: комментарий от zaz

Очень удобно

Удобно, но не очень. Нет \0 в конце строки. Это сразу ставит крест почти на всех функциях, принимающих char*. Почти все реализации динамических строки после каждой операции добавляют 0. А sds так вообще возвращает сишную строку, длина которой хранится за несколько байт до начала строки. Удобно - не надо подключать shs.h в основной header-файл

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

Потомучто питон - это мнимая простота. Быстро и весело пишешь модуль, все работает. А потом осознаешь, что он тянет пару сотен мегабайт интерпретатора и батареек. И, что еще хуже, оказываешься заперт в одном языке. Не подключишь этот свой модуль ни к чему кроме питона.

А rust я еще не осилил.

А на плюсы у меня просто мозгов не хватит)

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

Оправдан ли этот зуд или все там норм с кучей?

насколько часто malloc/free? 10 раз в секунду? 10 раз в минуту?

менеджер памяти не настолько тупой чтобы наделать очень сильную фрагментацию памяти.

Если ты считаешь, что всё-таки ты хочешь улучшить свою работу с памятью, посмотри на http://man7.org/linux/man-pages/man2/madvise.2.html

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

Сотни раз в секунду, кусочки до килобайта

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

Потомучто питон - это мнимая простота

быстроfix:

python - это комплексная прост[о|]та

ps. pune intended

вот бы ... python'а да .... golang'а да от rust'а ... и всё это с возможностями .... c++

бывает что про роль программиста - это вытягивание из работающего прототипа(с многими терабайтами непонятно как обусловленного кода/данных реализованной системы) - сущностно её идеального представления --- а кто/что кормит прогера в этот растянутый период???

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

вот бы ... python'а да .... golang'а да от rust'а ... и всё это с возможностями .... c++

Да легко. Надо просто добавить в C++ еще синтаксиса: gc, горутины, лайфтаймы, опциональную типизацию. Вот будет язык так язык. Всем языкам язык

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

1. Много людей разных, много крешей было, вот и боятся... Сам ткаим был, но перестал так делать.

2. Бояться не надо, так как malloc выделяет из пула. Если ты не делаешь чего-то ОЧЕНЬ странного, что не решается на уровне настроек аллокатора, можно просто забыть про фрагментацию. Тем более, что жтим процессом на уровне приложения управлять всё равно невозможно, если уж не повезло, так не повезло...

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

Бояться не надо, так как malloc выделяет из пула.

В твоих фантазиях разве что. В реальности гнутый маллок это списковый аллокатор.

Если ты не делаешь чего-то ОЧЕНЬ странного

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

А далее уже всё просто - бахнул 100метров кусаками, потом бахнул кило под результат - результат живёт, а 100метров в памяти болтаются и она никогда уже не уменьшится.

И прочие ахренительные особенности ущербанского маллока на списке.

что не решается на уровне настроек аллокатора

Ну покажи мне насройки, которые решат проблемы выше.

можно просто забыть про фрагментацию.

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

Тем более, что жтим процессом на уровне приложения управлять всё равно невозможно, если уж не повезло, так не повезло...

Действительно - бахнул левы/свой/выкинул нахрен это ущербанство и управляй как надо. Но да, действительно нельзя.

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

+1 ламерок в комьюнити. Люблю лор.

anonymous
()

GNUшный malloc не такой простой как может показаться если что.

И да, если malloc вернул null, то до места с free вообще дело дойти не должно.

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

походу надо читать исходники менеджера памяти чтобы узреть истину)

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

В твоих фантазиях разве что. В реальности гнутый маллок это списковый аллокатор.

А это не имеет особого значения, pool-овые аллокаторы теряют ещё больше памяти из-за эффектов сходных с фрагментацией.

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

Всё же очевидно, пуловый аллокатор набирает память блоками по n-страниц которые потом нарезает на чанки. Чтобы стало плохо достаточно выделить кучу объектов из пула и отдать все кроме каждого (block_size/chunk_size)-го, тогда в пуле будет лежать мёртвым грузом blocks * (block_size - chunk_size) памяти.

Если приложение интенсивно пользуется кучей постоянно меняя размеры чанков (например, push-ит в кучу векторов) или просто со временем меняется относительное распределение по размерам чанков, то в размерных пулах начинает оседать сравнительно много неиспользуемой памяти.

Чтобы glibc-подобный аллокатор начал бесполезно удерживать память нужно приложить на порядок больше усилий. Например, в примере выше такой аллокатор спокойно раздаст дырки между прореженными чанками если они подойдут по размеру. Профит от pool-овых аллокаторов только в гарантированно маленькой константе на все операции если не учитывать дёрганье sbrk() и mmap().

mashina ★★★★★
()

Память же фрагментируется. Нельзя часто выделять освобождать. Особенно много маленьких кусочков.

Наоборот, фрагментация будет происходить при аллокациях больше страницы. Для маленьких аллокаций маллок обычно использует кэш в виде пула объектов, поэтому чем меньше размер, тем меньше накладные расходы. Если аллокаций меньше 100к в секунду, и они меньше 512 байт, то можно особо не беспокоится. Цена такой аллокации - примерно лок мьютекса.

http://gee.cs.oswego.edu/dl/html/malloc.html

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