- выделение кучи действительно, довольно дорого.
- преждевременная оптимизация - это плохо, но если ваша библиотека довольно общая и матрицы огромные, поиск эффективного дизайна может быть преждевременным. В конце концов, вы не хотите изменять свой дизайн после того, как накопили много зависимостей от него.
- Существуют различные уровни, на которых вы можете решить эту проблему. Например, можно избежать затрат на выделение кучи, решая их на уровне распределителя памяти (например, пул памяти для каждого потока)
- хотя выделение кучи стоит дорого, вы создаете одну гигантскую матрицу только для выполнения довольно дорогих операций над матрицами (обычно линейной сложности или хуже). Условно говоря, выделение матрицы в бесплатном хранилище может быть не таким дорогим по сравнению с тем, что вы неизбежно будете делать с ним впоследствии, поэтому оно может быть довольно дешевым по сравнению с общей логикой функции, такой как сортировка.
Я рекомендую вам написать код естественным образом, принимая во внимание возможность №3 в будущем. То есть, не используйте ссылки на матричные буферы для промежуточных вычислений, чтобы ускорить создание временных. Сделать временные и вернуть их по значению. Корректность и хорошие, понятные интерфейсы на первом месте.
Главным образом цель здесь состоит в том, чтобы отделить политику создания матрицы (с помощью распределителя или других средств), которая дает вам передышку для оптимизации в качестве запоздалой мысли без изменения слишком большого количества существующего кода. Если вы можете сделать это, изменив только детали реализации задействованных функций или, что еще лучше, изменив только реализацию вашего матричного класса, тогда вы действительно преуспеете, потому что тогда вы сможете оптимизировать без изменения дизайна, и любой проект, который позволяет это, как правило, будет завершен с точки зрения эффективности.
ВНИМАНИЕ: Следующее предназначено, только если вы действительно хотите выжать максимум из каждого цикла. Важно понять # 4, а также получить хороший профилировщик. Стоит также отметить, что вы, вероятно, добьетесь большего успеха, оптимизировав шаблоны доступа к памяти для этих матричных алгоритмов, чем пытаясь оптимизировать распределение кучи.
Если вам нужно оптимизировать распределение памяти, рассмотрите возможность его оптимизации с использованием чего-то общего, например, пула памяти для каждого потока. Например, вы могли бы заставить свою матрицу принимать необязательный распределитель, но я подчеркиваю здесь необязательный, и вначале я бы также подчеркнул правильность с помощью тривиальной реализации распределителя.
Другими словами:
Лучше ли объявлять M1 (n, p) внутри каждой функции или
скорее раз и навсегда в main () и передать его каждой функции как
своего рода контейнер, который каждая функция может использовать в качестве места для отходов.
Продолжайте и создайте M1 как временное в каждой функции. Старайтесь не требовать от клиента составления матрицы, которая не имеет для него / нее значения только для вычисления промежуточных результатов. Это могло бы раскрыть детали оптимизации, чего мы должны избегать при разработке интерфейсов (скрыть все детали, о которых клиенты не должны знать).
Вместо этого сосредоточьтесь на более общих понятиях, если вы абсолютно хотите, чтобы эта опция ускорила создание этих временных файлов, как необязательный распределитель. Это соответствует практическим проектам, таким как std::set
:
std::set<int, std::less<int>, MyFastAllocator<int>> s; // <-- okay
Хотя большинство людей просто делают:
std::set<int> s;
В вашем случае это может быть просто:
M1 my_matrix (n, p, alloc);
Это небольшая разница, но распределитель - это гораздо более общая концепция, которую мы можем использовать, чем кэшированная матрица, которая в ином случае не имеет никакого значения для клиента, за исключением того, что это какой-то кэш, который требуется вашим функциям, чтобы помочь им быстрее вычислять результаты.Обратите внимание, что он не должен быть общим распределителем.Это может быть просто ваш предварительно выделенный матричный буфер, переданный в конструктор матрицы, но концептуально было бы хорошо выделить его просто потому, что это нечто более непрозрачное для клиентов.
Кроме того, конструирование этоговременный матричный объект также потребует осторожности, чтобы не разделять его между потоками.Это еще одна причина, по которой вы, вероятно, захотите немного обобщить концепцию, если вы пойдете по пути оптимизации, так как что-то более общее, такое как матричный распределитель, может учитывать безопасность потока или, по крайней мере, подчеркивать больше при разработке, что отдельный распределитель долженможет быть создан для каждого потока, но объект с необработанной матрицей, вероятно, не может.
Вышеприведенное полезно только в том случае, если вы действительно заботитесь о качестве ваших интерфейсов в первую очередь.Если нет, я бы порекомендовал следовать совету Матье, поскольку это намного проще, чем создание распределителя, но мы оба подчеркиваем, что ускоренную версию необязательно .