Std:: fill не превращается в memset для типов POD

Я ожидаю, что std:: fill на непрерывном контейнере, скажем std::vector, автоматически скомпилируется на вызов memset. Однако, когда я попробовал следующий код

#include <vector>
#include <algorithm>
#include <numeric>

using namespace std;

int main()
{
    vector<double> vec(300000);

    fill(vec.begin(),vec.end(),0.0);

    memset(&vec[0],0,vec.size()*sizeof(double));
}

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

Ответ 1

Обращаясь к вашему конкретному примеру double, это должно было быть оптимизацией на платформе, и, скорее всего, g++ решил не делать этого. Разумеется, все платформы используют представление double, для которого 0.0 не означает все нулевые байты. Обратите внимание, что при настройке на любое число, отличное от нуля, есть совершенно другая игра, так как она не просто устанавливает каждый байт на ноль: существует конкретный шаблон, который необходимо соблюдать. Это ухудшается с отрицательными числами.

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

Ответ 2

Стандарт не принуждает разработчиков использовать memset(). Но, например, gcc использует memset() для std::fill() для контейнеров char.

Ответ 3

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

Позор - это то, что простой цикл генерируется, потому что он, по крайней мере, упрощает до чего-то вроде:

const T val(0.0);
for (size_t i = 0; i < 30000; ++i) vec.data[i] = double(val);

... но он не делает окончательный дедуктивный скачок, что цикл 0..30000 через массив типов контейнеров, инициализированных с тем же значением, лучше всего делать с помощью memset. Как упоминалось wilhelmtell, некоторые реализации специализируются на нескольких типах контейнеров, где есть большой выигрыш (цикличность на символах медленная). Я действительно хочу, чтобы компиляторы сделали этот последний прыжок, потому что это помогло бы использовать библиотеки контейнеров вообще, если бы люди знали, что этого не произойдет.