22 января 2020

Как на C++ быстро заполнить нулями большой массив?

Пишу, перевожу и иллюстрирую IT-статьи. На proglib написал 140 материалов. Увлекаюсь Python, вебом и Data Science. Открыт к диалогу – ссылки на соцсети и мессенджеры: https://matyushkin.github.io/links/ Если понравился стиль изложения, упорядоченный список публикаций — https://github.com/matyushkin/lessons
Пара слов к вопросу об эффективном заполнении массивов одинаковыми значениями и о том, почему нельзя слепо доверять компиляторам.
9
Как на C++ быстро заполнить нулями большой массив?

Трэвис Даунс в недавнем посте рассказал, что некоторые компиляторы C++ испытывают проблемы в производительности при заполнении массивов нулями. Даниэль Лемир продолжил разговор об этой проблеме в своём блоге и провёл наглядный тест для двух подходов.

Как правило, чтобы заполнить массив некоторым значением, C++ программисты вызывают std::fill. Можно предположить, что для такой простой задачи, как заполнение массива нулями, стандартная библиотека C++ обеспечивает наилучшую производительность. Однако в случае компиляторов GNU GCC это не так.

Следующая строка кода на C ++ компилируется как цикл, который заполняет каждый отдельный байт нулями с применением обычного флага оптимизации -O2.

        std::fill(p, p + n, 0);

    

Когда массив большой, этот подход становится менее эффективен, чем реализации функций C, например, memset.

        memset(p, 0, n);

    

Трэвис объясняет, как исправить код С++, чтобы он работал так же быстро, как функция memset.

Что насчёт очень больших массивов? Что делать, если нужно записать нули во всём двухгигабайтном массиве? Результаты теста на 2 Гб массиве следующие:

memset 30 Гб/c
std:fill 1.7 Гб/c

Функция memset в проведённом тесте в 15 раз быстрее, чем std::fill. Это показывает, что при работе с большими объёмами данных, вас может ограничивать не пропускная способность, а просто неэффективная реализация алгоритма. Заполнение через std::fill в этом случае работает медленнее, чем хороший сетевой адаптер или быстрый диск.

Другой урок, который можно извлечь из этого примера, состоит в том, что просто писать код на C++ недостаточно для обеспечения хорошей производительности. Хотя существует много технических решений, направленных на то, чтобы сделать C++ быстрым, и, обычно всё прекрасно работает, нужно сохранять бдительность.

Ссылка на более подробные результаты теста.

МЕРОПРИЯТИЯ

Комментарии

 
 
29 августа 2023

Ещё вариант, если проект позволяет - использовать DMA. Тогда заполнение памяти может быть произведено (и не только нулями) пока процессор делает что-то более полезное.

29 августа 2023

Заполнять надо целыми того размера, который на этой архитектуре (к примеру 64 бит), если очень большой массив, то может быть задействовать и многопоточность. Таким образом можно попробовать закастить массив к машинному слову, определив максимальное целое количество слов, которое влезет в эту область памяти. А потом задействовать обычный алгоритм STL или их параллельную версию, для установки 0, при необходимости заполнить участок в конце, который не влез в слово. Таким образом вместо заполнения по байту может получиться 8 или более байт за 1 раз !

16 апреля 2020

А где больше общее количество глюков: в библиотеке, которую надо сначала подцепить на этапе разработки, потом слинковать и только потом вызвать, а в конце ещё грамотно закрыть, или в простейшем цикле, в котором ошибиться не возможно даже специально? Где больше общее количество глюков: в библиотеке, чьи разработчики не знали конкретных задач, или в простом цикле, в котором невозможно ошибиться? А подобрать размер блока для хотя бы одной разновидности компьютеров можно и самостоятельно. Если Вы не одиночка, то можете протестировать несколько размеров на разных машинах. А если есть возможность разобрать документацию, то можно такую простую задачу и не тестировать, а посчитать. При этом библиотека или тоже сделана под что-то конкретное, или раздута ради автоматического выбора под целевой процессор. Но раздувание кода как раз и есть следующий источник тормозов после косых алгоритмов и кривого выбора процессорных операций. Ах, есть ещё выбор версии инсталятором? Так это можно внести и в инсталятор прилады.

01 марта 2020

int mass[sz] = { 0 }; А чем так хуже? Я просто новичок, не понимаю.

Это если вы имеете дело с обычным массивом, но не с динамическим, когда встаёт вопрос о работе с указанными объёмами. Подробнее описано вот здесь: http://rsdn.org/forum/cpp/1668439.hot

27 февраля 2020

Неважно какого цвета кошка если она успешно ловит мышей. С. председатель Мао

21 февраля 2020

Не согласен с позицией в статье. Не вижу смысла куда либо гнаться по скорости. memset - это допотопная Си функция std::fill - C++ функция Пишите на Си - используйте Си Пишите на Си++, используйте Си++ Нет смысла в лишний раз умничать и мешать код

21 февраля 2020

Спасибо за комментарий! Обращаем ваше внимание, что мы привели решение проблемы и на чистом С++.

ВАКАНСИИ

Добавить вакансию
Middle/Senior C++ HFT разработчик
Москва, по итогам собеседования
Go-разработчик
по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

LIVE >

Подпишись

на push-уведомления