В с++ деструктор — это неотъемлемая часть функции. А вот освобождение памяти можно вынести за её пределы почти всегда. Идея очень проста в реализации, но не совсем понятно, как много она может дать. Вот по этому обращаюсь к уважаемой аудитории с просьбой поделиться соображениями, опытом и наработками.
Сама идея:
создать глобальный объект хранящий указатели (void*) на память требующую освобождения. По образу вот этого
... назовём его LockFreeVPBuffer.
создать поток который по мере необходимости(или просто всегда, или по таймеру) освобождает всю память по указателям из LockFreeVPBuffer.
Заменить (overload) операторы new, new[], delete, delete[] на такие, чтоб при delete/delete[] указатель добавлялся в LockFreeVPBuffer (освобождать память на месте если буфер полон)
В new/new[] можно обработать неудачу при выделении. Не сразу бросать исключение, а сначала очистить содержимое LockFreeVPBuffer, и если повторно обламается то, тогда уже бросать.
Предположительные "за":
Основная логика будет выполнятся быстрее, и часть работы равномерно разойдётся по всему времени разряжая пиковые моменты.
Освобождение памяти будет идти последовательно и возможно меньше чередоваться с выделением, что замедлит фрагментацию памяти.
Предположительные "против":
Оверхед на дополнительный поток, если активных потоков в программе мало, или процессоров(physical threads) мало, то это может стать довольно чувствительным.
Работы становится не меньше, весь тот же код всё равно выполнится, плюс добавится работа с LockFreeVPBuffer.
П.С. Обратите внимание, что речь не идёт о том с каким аллокатором работает программа, системным или кастомным. (хотя это тоже может быть важно )
Здравствуйте, Caracrist, Вы писали:
C>В с++ деструктор — это неотъемлемая часть функции. А вот освобождение памяти можно вынести за её пределы почти всегда. Идея очень проста в реализации, но не совсем понятно, как много она может дать. Вот по этому обращаюсь к уважаемой аудитории с просьбой поделиться соображениями, опытом и наработками.
C>Сама идея: C>создать глобальный объект хранящий указатели (void*) на память требующую освобождения. По образу вот этого
... назовём его LockFreeVPBuffer. C>создать поток который по мере необходимости(или просто всегда, или по таймеру) освобождает всю память по указателям из LockFreeVPBuffer. C>Заменить (overload) операторы new, new[], delete, delete[] на такие, чтоб при delete/delete[] указатель добавлялся в LockFreeVPBuffer (освобождать память на месте если буфер полон) C>В new/new[] можно обработать неудачу при выделении. Не сразу бросать исключение, а сначала очистить содержимое LockFreeVPBuffer, и если повторно обламается то, тогда уже бросать.
C>Предположительные "за": C>Основная логика будет выполнятся быстрее, и часть работы равномерно разойдётся по всему времени разряжая пиковые моменты. C>Освобождение памяти будет идти последовательно и возможно меньше чередоваться с выделением, что замедлит фрагментацию памяти.
C>Предположительные "против": C>Оверхед на дополнительный поток, если активных потоков в программе мало, или процессоров(physical threads) мало, то это может стать довольно чувствительным. C>Работы становится не меньше, весь тот же код всё равно выполнится, плюс добавится работа с LockFreeVPBuffer.
C>П.С. Обратите внимание, что речь не идёт о том с каким аллокатором работает программа, системным или кастомным. (хотя это тоже может быть важно )
Не забудьте ещё что к каждому указателю надо прицепить счётчик ссылок иначе ваш мемориколлектор освободит память которая всё ещё используется. По сути вам всё равно нужны те же самые умные указатели. Только вместо того что бы освобождать память в деструкторе когда исчезает последняя ссылка, вы добавляете себе лишний геморой с дополнительным потоком, глобальным хранилищем указателей и т.п.
Здравствуйте, TarasKo, Вы писали:
TK>Не забудьте ещё что к каждому указателю надо прицепить счётчик ссылок иначе ваш мемориколлектор освободит память которая всё ещё используется. По сути вам всё равно нужны те же самые умные указатели. Только вместо того что бы освобождать память в деструкторе когда исчезает последняя ссылка, вы добавляете себе лишний геморой с дополнительным потоком, глобальным хранилищем указателей и т.п.
Если сделать GC, то счетчиков не будет — тут есть выигрыш в использовании процессорного времени не-GC-потоками
Но топикстартер сказал, что это не д.быть GC
И мне пока не понятно что же он тогда имеет ввиду и что там будет лучше
Здравствуйте, s.ts, Вы писали:
ST>А чем это отличается от GC ?
Не ведёт подсчет ссылок, не вычисляет круговую зависимость, не выполняет код специализированный для объекто (деструкторы, или дипоузы)
Этот модуль на порядок легче GC.
Здравствуйте, TarasKo, Вы писали:
TK>Не забудьте ещё что к каждому указателю надо прицепить счётчик ссылок иначе ваш мемориколлектор освободит память которая всё ещё используется. По сути вам всё равно нужны те же самые умные указатели. Только вместо того что бы освобождать память в деструкторе когда исчезает последняя ссылка, вы добавляете себе лишний геморой с дополнительным потоком, глобальным хранилищем указателей и т.п.
нет никакой связи между тем, как вы решаете когда освобождать память в своей программе и тем, что я предлогаю. MC подключается там где код выполняет delete, тоесть уже после подсчётов сылок или ещё какой методики.
Здравствуйте, TarasKo, Вы писали:
TK>Не забудьте ещё что к каждому указателю надо прицепить счётчик ссылок иначе ваш мемориколлектор освободит память которая всё ещё используется. По сути вам всё равно нужны те же самые умные указатели. Только вместо того что бы освобождать память в деструкторе когда исчезает последняя ссылка, вы добавляете себе лишний геморой с дополнительным потоком, глобальным хранилищем указателей и т.п.
Не.
Он предлагает сделать deferred HeapFree: в delete вызов HeapFree не происходит, а адрес памяти, которую нужно освободить передаётся в lock-free очередь, которую периодически выгребает специальный поток и для каждого элемента этой очереди зовёт HeapFree.
Всё, чего он добьётся — потенциальное уменьшение затрат на вызов HeapFree (а в многопоточке огромную долю времени выполнения HeapFree занимает сидение в критической секции).
Не думаю что овчинка стоит выделки, но в каком то наборе задач может и даст какой нить performance boost.
По мне так проще сделать нормальный per-thread pool аллокатор. Это и аллокацию ускорит и деаллокацию.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, CreatorCray, Вы писали:
CC>Не. CC>Он предлагает сделать deferred HeapFree: в delete вызов HeapFree не происходит, а адрес памяти, которую нужно освободить передаётся в lock-free очередь, которую периодически выгребает специальный поток и для каждого элемента этой очереди зовёт HeapFree. CC>Всё, чего он добьётся — потенциальное уменьшение затрат на вызов HeapFree (а в многопоточке огромную долю времени выполнения HeapFree занимает сидение в критической секции). CC>Не думаю что овчинка стоит выделки, но в каком то наборе задач может и даст какой нить performance boost. CC>По мне так проще сделать нормальный per-thread pool аллокатор. Это и аллокацию ускорит и деаллокацию.
Хм, а зачем его делать ? Никто же не запрещает иметь кучу для каждого потока. То есть, конечно, она не потоку принадлежит, но из других потоков к ней не обращаться. И кстати, при этом можно будет поставить HEAP_NO_SERIALIZE и убрать это сидение в критической секции
Ниже выделено мной
If HEAP_NO_SERIALIZE is not specified (the simple default), the heap serializes access within the calling process. Serialization ensures mutual exclusion when two or more threads attempt simultaneously to allocate or free blocks from the same heap. There is a small performance cost to serialization, but it must be used whenever multiple threads allocate and free memory from the same heap. Setting HEAP_NO_SERIALIZE eliminates mutual exclusion on the heap. Without serialization, two or more threads that use the same heap handle might attempt to allocate or free memory simultaneously, which may cause corruption in the heap. Therefore, HEAP_NO_SERIALIZE can safely be used only in the following situations:
The process has only one thread.
The process has multiple threads, but only one thread calls the heap functions for a specific heap. The process has multiple threads, and the application provides its own mechanism for mutual exclusion to a specific heap.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Хм, а зачем его делать? Никто же не запрещает иметь кучу для каждого потока.
Можно и так. Другой момент что со своим пулом можно:
1) сделать ещё более быструю аллокацию/деаллокацию
2) проще сделать обработку случая "выделяем в одном потоке, освобождаем в другом" и "насоздавали в этом потоке, отдали указатели в другой и завершили поток"
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Хм, а зачем его делать? Никто же не запрещает иметь кучу для каждого потока. CC>Можно и так. Другой момент что со своим пулом можно: CC>1) сделать ещё более быструю аллокацию/деаллокацию CC>2) проще сделать обработку случая "выделяем в одном потоке, освобождаем в другом" и "насоздавали в этом потоке, отдали указатели в другой и завершили поток"
Всё можно... Можно и свой GC построить. Вопрос цены/качества.
То что я предложил, не универсальное и суперское решение . Это всего лишь элементарное в реализации (несколько часов и готово), очень простое улучшение, которое может улучшить (возможно существенно) работу многопоточного приложения.
Здравствуйте, CreatorCray, Вы писали:
CC>2) проще сделать обработку случая "выделяем в одном потоке, освобождаем в другом" и "насоздавали в этом потоке, отдали указатели в другой и завершили поток"
А это и в том варианте, что я предложил, никому делать не запрещено. Лишь бы два потока не пытались одновременно к куче обратиться.
Здравствуйте, Caracrist, Вы писали:
C>Всё можно... Можно и свой GC построить.
Нельзя. Если ты начнешь двигать объекты в памяти — как быть с сохраненными указателями ?
C>То что я предложил, не универсальное и суперское решение . Это всего лишь элементарное в реализации (несколько часов и готово), очень простое улучшение, которое может улучшить (возможно существенно) работу многопоточного приложения.
Насчет существенно- сомневаюсь, но согласен с CreatorCray — иногда может быть полезным. В общем, идея имеет право на существование, но вряд ли особо ценна.
Здравствуйте, CreatorCray, Вы писали:
CC>2) проще сделать обработку случая "выделяем в одном потоке, освобождаем в другом" и "насоздавали в этом потоке, отдали указатели в другой и завершили поток"
Впрочем да, это и с Heap просто делается.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, Pavel Dvorkin, Вы писали:
CC>>2) проще сделать обработку случая "выделяем в одном потоке, освобождаем в другом" и "насоздавали в этом потоке, отдали указатели в другой и завершили поток" PD>А это и в том варианте, что я предложил, никому делать не запрещено. Лишь бы два потока не пытались одновременно к куче обратиться.