С против.C++ для повышения производительности при распределении памяти
-
22-09-2019 - |
Вопрос
Я планирую участвовать в разработке кода, написанного на языке C для анализа сложных задач методом Монте-Карло.Этот код размещает в памяти огромные массивы данных для ускорения своей работы, поэтому автор кода выбрал C вместо C++, утверждая, что на C можно сделать более быстрый и надежный (относительно утечек памяти) код.
Согласны ли вы с этим?Каким будет ваш выбор, если во время вычислений вам потребуется хранить в памяти 4–16 ГБ массивов данных?
Решение
Определенно С++.По умолчанию между ними нет существенной разницы, но C++ предоставляет несколько вещей, которых нет в C:
- конструкторы/деструкторы.Они позволяют автоматизировать большую часть управления памятью, повышая надежность.
- распределители для каждого класса.Они позволяют оптимизировать распределение в зависимости от того, как разрабатываются и/или используются конкретные объекты.Это может быть особенно полезно, если вам нужно большое количество мелких объектов (приведу один очевидный пример).
Суть в том, что в этом отношении C не дает абсолютно никаких преимуществ перед C++.В самом худшем случае вы можете делать то же самое и теми же способами.
Другие советы
Есть одна особенность C99, которая отсутствует в C++ и которая потенциально дает значительный прирост скорости в тяжелом коде обработки чисел, и это ключевое слово restrict
.Если вы можете использовать компилятор C++, который его поддерживает, то у вас в комплекте есть дополнительный инструмент для оптимизации.Однако это всего лишь потенциальная выгода:достаточная встраивание может обеспечить ту же оптимизацию, что и restrict
и более.Это также не имеет никакого отношения к распределению памяти.
Если автор кода может продемонстрировать разницу в производительности между кодом C и C++, выделяющим массив размером 4–16 ГБ, то (а) я удивлён, но ок, разница есть, и (б) сколько раз собирается ли программа выделять такие большие массивы?Действительно ли ваша программа будет тратить значительное количество времени на выделение памяти, или она тратит большую часть своего времени доступ память и выполнение вычислений?На самом деле нужно много времени делать что-либо с массивом размером 4 ГБ по сравнению со временем, необходимым для выделения, а это означает, что вам следует беспокоиться о производительности «чего-либо», а не о производительности распределения.Спринтеров очень волнует, как быстро они отрываются от блоков.Марафонцы, не так уж и много.
Вы также должны быть осторожны при проведении бенчмаркинга.Вы должны сравнивать, например malloc(size)
против new char[size]
.Если вы тестируете malloc(size)
против new char[size]()
тогда это несправедливое сравнение, поскольку последний устанавливает память в 0, а первый — нет.Сравните с calloc
вместо этого, но также обратите внимание, что malloc
и calloc
оба доступны из C++ в том (маловероятном) случае, что они окажутся заметно быстрее.
В конечном счете, однако, если автор «владеет» проектом или начал его и предпочитает писать на C, а не на C++, тогда он не должен оправдывать свое решение вероятно ложными заявлениями о производительности, он должен оправдать его, сказав: «Я предпочитаю C». , и это то, что я использую».Обычно, когда кто-то делает такое заявление о языковых характеристиках, а тестирование оказывается неверным, вы обнаруживаете, что производительность не является реальной причиной языковых предпочтений.Доказательство ложности утверждения на самом деле не приведет к тому, что автор этого проекта внезапно полюбит C++.
Реальной разницы между C и C++ с точки зрения распределения памяти нет.C++ имеет больше «скрытых» данных, таких как виртуальные указатели и т. д., если вы решили использовать виртуальные методы для своих объектов.Но выделение массива символов в C столь же затратно, как и в C++, более того, они, вероятно, оба используют для этого malloc.С точки зрения производительности C++ вызывает конструктор для каждого объекта массива.Обратите внимание, что это делается только в том случае, если он есть, конструктор по умолчанию ничего не делает и оптимизируется.
Пока вы предварительно выделяете пулы данных, чтобы избежать фрагментации памяти, все будет в порядке.Если у вас есть простые POD-структуры без виртуальных методов и без конструкторов, разницы нет.
Единственное, что не в пользу C++, — это его дополнительная сложность. — совместите это с программистом, который его неправильно использует, и вы легко сможете заметно тормозить.Использование компилятора C++ без функций C++ обеспечит такую же производительность.Правильно используя C++, у вас есть некоторые возможности стать быстрее.
Язык — не твоя проблема, выделение и перемещение больших массивов.
Основная смертельная ошибка, которую вы можете совершить при распределении (на любом языке), — это выделение 16 ГБ памяти, инициализация ее нулевым значением только для того, чтобы позже заполнить ее фактическими значениями.
Наибольшего прироста производительности я ожидал от алгоритмической оптимизации, улучшающей локальность ссылок.
В зависимости от базовой ОС вы также можете влиять на алгоритмы кэширования, например.указывая, что диапазон памяти обрабатывается только последовательно.
При распределении необработанных данных не должно быть разницы между C и C++ в большинстве систем, поскольку они обычно используют одни и те же механизмы библиотеки времени выполнения.Интересно, была ли это классическая ошибка в тестировании, когда они также измеряли время выполнения вызовов конструктора в C++ и удобно забыли включить время выполнения любого кода инициализации в C.
Кроме того, аргумент «более надежный (относительно утечек памяти)» не выдерживает никакой критики, если вы используете RAII в C++ (как и должно быть).Если только кто-то не говорит о повышении надежности утечки, использование RAII, интеллектуальных указателей и классов контейнеров уменьшит вероятность утечек, а не увеличит ее.
Мои основные проблемы с выделением такого большого количества памяти могут быть двоякими:
- Если вы приближаетесь к пределу физической памяти на машинах, на которых вы запускаете симуляцию Монте-Карло, это хороший способ снизить производительность, поскольку диск вполне может начать перегружаться, когда системе виртуальной памяти потребуется начать много подкачки. .Виртуальная память не является «бесплатной», хотя многие люди так думают.
- Необходимо тщательно продумать размещение данных, чтобы максимизировать использование кэша процессора, иначе вы частично потеряете преимущества хранения данных в основной памяти.
Если распределение памяти является узким местом в таком коде, я бы предложил перепроектировать, а не менять язык для более быстрого распределения.Если вы выделяете память один раз, а затем выполняете множество вычислений, я ожидаю, что эти вычисления станут узким местом.Если стоимость распределения значительна, что-то здесь не так.
Вы также можете использовать семейство функций распределения памяти C в C++:оба стандартные malloc
и free
, realloc
увеличивать/сжимать массивы и alloca
для выделения памяти в стеке.
Если вы пойдете с new
, он выделит больше памяти, чем необходимо (в основном во время отладки) и выполнит дополнительные проверки на согласованность.Он также вызовет конструктор классов.В выпуске (-O3
) build разница будет незначительной для большинства приложений.
Что теперь new
приносит то, что malloc не находится на месте new
.Вы можете предварительно выделить буфер, а затем использовать его на месте. new
поместить вашу структуру в этот буфер, тем самым сделав ее «выделение» мгновенным.
В общем, я бы не остался в стороне от C из-за проблем с производительностью.Во всяком случае, ваш код будет более эффективным, потому что классы передают this
указатель в регистрах вместо параметров, как в эквиваленте C.Реальная причина держаться подальше от C — это размер среды выполнения C++.Если вы разрабатываете программы для встраиваемых систем или загружаемые при загрузке программы, вы не можете встроить среду выполнения размером ~4 МБ.Однако для обычных приложений это не будет иметь значения.
Если во время вычислений вам необходимо хранить в памяти 4-16 ГБ массивов данных, а физической памяти у вашей машины всего 2 ГБ, то что?
Что делать, если на вашем компьютере 16 ГБ физической памяти?Операционная система не занимает физическую память?
Предоставляет ли операционная система адресное пространство размером 4 ГБ, 16 ГБ и т. д.?
Я полагаю, что если производительность является основным ограничением реализации, то понимание того, как платформы, которые предназначены для использования, функционируют и работают, гораздо более важно, чем вопрос о любой измеримой разнице в производительности между C и C++ при идентичных средах и алгоритмах.