Распределитель блоков выполняет одно большое выделение для свободного хранилища / кучи, а затем внутренне разделяет эту память на куски. Один недостаток здесь заключается в том, что он распределяет этот кусок (который должен быть большим, и часто определяемым пользователем для каждого случая использования), так что даже если вы не используете все это, эта память связана. Во-вторых, стандартный распределитель памяти построен поверх new / delete, который, в свою очередь, часто строится поверх malloc / free. Хотя я и не помню, гарантированно ли при всех обстоятельствах malloc / free будет поточно-ориентированным, но так оно и есть.
Но, наконец, причина того, почему распределители блоков работают так хорошо, заключается в том, что у них есть информация, которой нет у стандартных распределителей, и им не нужно покрывать очень широкий набор вариантов использования. Например, если вы сделали std::map< int, int >()
, и он выделил 1 МБ, вы, вероятно, разозлились бы, но если бы вы сделали std::map< int, int, std::less< int >, block_alloc< 1024 * 1024 > >()
, вы бы ожидали этого. Стандартные распределители не выделяются в блоках, они запрашивают новую память через новую, а новые, в свою очередь, вообще не имеют контекста. Он получает запрос памяти произвольного размера и должен найти непрерывное число байтов для возврата. Что делает большинство реализаций, так это то, что они имеют набор областей памяти, которые они поддерживают в различных кратных единицах (например, более или менее вероятно, что область для 4 байтов, вероятно, будет присутствовать, так как запросы на 4 байта очень распространены). Если запрос не является даже кратным, становится труднее вернуть хороший кусок, не теряя места и вызывая фрагментацию. В основном, управление памятью произвольных размеров очень трудно сделать, если вы хотите, чтобы оно было близко к постоянному времени, низкой фрагментации, поточно-ориентированному и т. Д.
Документация Boost pool содержит полезную информацию о том, как может работать хороший распределитель блоков.