Может ли многопоточность ускорить выделение памяти?

Я работаю с 8-ядерным процессором и использую потоки Boost для запуска большой программы. Логически, программа может быть разделена на группы, где каждая группа управляется потоком. Внутри каждой группы некоторые классы ссылаются на "новый" оператор в общей сложности 10000 раз. Rational Quantify показывает, что "новое" распределение памяти занимает максимальное время обработки при запуске программы и замедляет работу всей программы.

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

Я не понимаю, как здесь будет управляться распределение памяти. Будет ли планировщик ОС реально распределять память параллельно?

Ответы

Ответ 1

Динамическое распределение памяти использует кучу приложения/модуля/процесса (но не поток). Куча может обрабатывать только один запрос распределения одновременно. Если вы попытаетесь выделить память в "параллельных" потоках, они будут обработаны должным образом кучей. Вы не получите такого поведения, как: один поток ждет, чтобы получить свою память, а другой может попросить некоторых, в то время как третий получает некоторые. Потокам придется выстраиваться в очередь, чтобы получить свой кусок памяти.

Вам понадобится пул кучи. Используйте то, что куча не занята в данный момент, чтобы выделить память. Но тогда вы должны следить за жизнью этой переменной, чтобы она не выделялась на другой куче (что могло бы вызвать сбой).

Я знаю, что Win32 API имеет такие функции, как GetProcessHeap(), CreateHeap(), HeapAlloc() и HeapFree(), которые позволяют вам создавать новую кучу и выделять/освобождать память из конкретной кучи HANDLE. Я не знаю эквивалентности в других операционных системах (я искал их, но безрезультатно).

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

Ответ 2

Стандартный CRT

В то время как со старыми из Visual Studio блокировщик CRT по умолчанию блокировал, это уже не так, по крайней мере, для Visual Studio 2010 и новее, которое вызывает соответствующие функции ОС напрямую. Менеджер кучи Windows блокировал до Widows XP, в XP дополнительный Low Fragmentation Heap не блокирует, а по умолчанию - и новые операционные системы (Vista/Win7) по умолчанию использует LFH. Производительность последних (Windows 7) распределителей очень хорошая, сопоставимая с масштабируемыми заменами, перечисленными ниже (вы все же можете предпочесть их, если таргетинг на более старые платформы или когда вам нужны другие функции, которые они предоставляют). Существует несколько множественных "масштабируемых распределителей" с разными лицензиями и разными недостатками. Я думаю, что в Linux стандартная библиотека времени выполнения уже использует масштабируемый распределитель (некоторый вариант PTMalloc).

Масштабируемые замены

Я знаю о:

Возможно, вы захотите проверить Опыт масштабируемого распределения памяти в своем опыте использования некоторых из них в проекте Windows.

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

Ответ 3

Есть два масштабируемых замены для malloc, которые я знаю:

У меня нет опыта работы с Hoard (который плохо работал в исследовании), но Emery Berger скрывается на этом сайте и был поражен результатами. Он сказал, что он посмотрит, и я предполагаю, что, возможно, были некоторые особенности либо теста, либо реализации, которые "захватили" Hoard, поскольку общая обратная связь обычно хороша.

Одно слово предостережения с jemalloc, оно может потратить немного места, когда вы быстро создаете, а затем отбрасываете потоки (поскольку он создает новый пул для каждого потока, который вы выделяете). Если ваши потоки стабильны, не должно быть никаких проблем с этим.

Ответ 4

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

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

Одна хорошая вещь, чтобы начать с при улучшении производительности, - это вопрос , если вам нужно делать то, что вы сейчас делаете. В этом случае вы можете предварительно выделить объекты? Существует ли в системе максимальное количество объектов X? Не могли бы вы повторно использовать объекты? Все это лучше, потому что вам необязательно делать выделения на критическом пути. Например. если вы можете повторно использовать объекты, пользовательский распределитель с заранее выделенными объектами будет работать хорошо. Кроме того, на какой ОС вы работаете?

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

Удачи!

Ответ 5

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

(вы можете переопределить новый и удалить)

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

ограничьте свои потоки количеством доступных вам ядер.

Ответ 6

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

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

Ответ 7

Вам нужно проверить свою компиляторную документацию, независимо от того, обеспечивает ли он поток распределителя или нет. Если этого не произойдет, вам нужно будет перегрузить нового оператора и сделать его потокобезопасным. Иначе это приведет либо к segfault, либо к UB.

Ответ 8

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

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

Ответ 9

Вы можете взглянуть на "Распределитель памяти" :" это замена для замены malloc(), которая может значительно повысить производительность приложений, особенно для многопоточных программ, работающих на многопроцессорных компьютерах.

Ответ 10

  • Лучшее, что вы можете попытаться достичь ~ 8 распределения памяти параллельно (поскольку у вас есть 8 физических ядер), а не 10000, как вы писали

  • Стандартный malloc использует мьютекс, а стандартный STL-распределитель делает то же самое. Поэтому при вводе потоков он не будет автоматически ускоряться. Тем не менее, вы можете использовать другую библиотеку malloc (google, например, "ptmalloc" ), которая не использует глобальную блокировку. если вы используете STL (например, выделяете строки, векторы), вам нужно написать свой собственный распределитель.

Довольно интересная статья: http://developers.sun.com/solaris/articles/multiproc/multiproc.html