С++ valarray против вектора

Мне очень нравятся векторы. Они изящны и быстры. Но я знаю, что эта вещь называется валараем. Почему я должен использовать valarray вместо вектора? Я знаю, что valarrays имеют синтаксический сахар, но кроме этого, когда они полезны?

Ответ 1

Valarrays (массивы значений) предназначены для переноса некоторой скорости Fortran на С++. Вы бы не сделали valarray указателей, поэтому компилятор может сделать предположения о коде и оптимизировать его лучше. (Основная причина, по которой Fortran настолько быстр, заключается в том, что нет указателя, поэтому не может быть никакого сглаживания указателя.)

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

Итак, если это номера, с которыми вы работаете, и удобство не так важно, используйте valarrays. В противном случае векторы намного удобнее.

Ответ 2

valarray - своего рода сирота, которая родилась не в то время и не в том месте. Это попытка оптимизации, особенно для машин, которые использовались для тяжелой математики, когда она была написана, - в частности, для векторных процессоров, таких как Crays.

Для векторного процессора вы обычно хотели применить одну операцию ко всему массиву, затем применить следующую операцию ко всему массиву и так далее, пока вы не сделаете все, что вам нужно сделать.

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

Также предполагается, что valarray исключает любую возможность создания псевдонимов, что (по крайней мере, теоретически) позволяет компилятору повысить скорость, поскольку он более свободен для хранения значений в регистрах. В действительности, однако, я совсем не уверен, что любая реальная реализация в значительной степени использует это в своих интересах. Я подозреваю, что это скорее проблема типа "курица и яйцо" - без поддержки компилятора она не стала бы популярной, и пока она не популярна, никто не собирается работать над своим компилятором для ее поддержки.

Есть также изумительный (буквально) массив вспомогательных классов, которые можно использовать с valarray. Вы получаете slice, slice_array, gslice и gslice_array чтобы играть с частями valarray, и заставить его действовать как многомерный массив. Вы также получаете mask_array чтобы "маскировать" операцию (например, добавлять элементы от x до y, но только в тех местах, где z не равен нулю). Чтобы использовать более чем тривиальное использование valarray, вы должны много узнать об этих вспомогательных классах, некоторые из которых довольно сложны и ни один из которых не кажется (по крайней мере мне) очень хорошо задокументированным.

Итог: хотя в нем есть моменты блеска, и он может делать некоторые вещи довольно аккуратно, есть также несколько очень веских причин, по которым он (и почти наверняка останется) неясным.

Изменить (восемь лет спустя, в 2017 году): Некоторые из предыдущих устарели, по крайней мере, до некоторой степени. Например, Intel внедрила оптимизированную версию valarray для своего компилятора. Он использует Intel Integrated Performance Primitives (Intel IPP) для повышения производительности. Хотя точное улучшение производительности, несомненно, варьируется, быстрый тест с простым кодом показывает увеличение скорости примерно в 2: 1 по сравнению с идентичным кодом, скомпилированным со "стандартной" реализацией valarray.

Поэтому, хотя я не совсем уверен, что программисты C++ начнут использовать valarray в огромных количествах, есть некоторые обстоятельства, при которых он может улучшить скорость.

Ответ 3

Во время стандартизации С++ 98 valarray был разработан, чтобы позволить некоторые быстрые математические вычисления. Тем не менее, в то время Тодд Велдхьюзен изобрел шаблоны выражений и создал blitz ++, и были изобретены аналогичные методы шаблона-мета, что сделало валарины в значительной степени устаревшими до стандарт был даже выпущен. IIRC, первоначальный разработчик valarray отказался от него на полпути к стандартизации, который (если это правда) тоже ему не помог.

ISTR, что основная причина, по которой он не был удален из стандарта, заключается в том, что никто не нашел времени для тщательного анализа проблемы и написания предложения об ее удалении.

Пожалуйста, имейте в виду, что все это смутно помнит слухи. Возьмите это с солью и надейтесь, что кто-то ее поправит или подтвердит.

Ответ 4

Я знаю, у valarrays есть синтаксический сахар

Я должен сказать, что я не думаю, что std::valarrays имеет много синтаксического сахара. Синтаксис другой, но я бы не назвал разницу "сахар". API странный. В разделе о std::valarray в C++ Языке программирования упоминается этот необычный API и тот факт, что, поскольку ожидается, что std::valarray будет высоко оптимизирован, любые сообщения об ошибках, которые вы получите при их использовании, вероятно, будут не интуитивными.

Из любопытства около года назад я std::valarray с std::vector. У меня больше нет кода или точных результатов (хотя это не должно быть трудно написать свой собственный). Используя GCC, я получил небольшое преимущество в производительности, когда использовал std::valarray для простой математики, но не для своих реализаций для расчета стандартного отклонения (и, конечно, стандартное отклонение не так сложно, как математика). Я подозреваю, что операции с каждым элементом большого std::vector лучше std::valarray с кешами, чем с std::valarray s. (ПРИМЕЧАНИЕ, следуя советам от musiphil, мне удалось получить почти одинаковую производительность от vector и valarray).

В конце концов, я решил использовать std::vector, уделяя пристальное внимание таким вещам, как выделение памяти и создание временных объектов.


И std::vector и std::valarray хранят данные в непрерывном блоке. Однако они получают доступ к этим данным, используя разные шаблоны, и, что более важно, API для std::valarray поддерживает другие шаблоны доступа, чем API для std::vector.

Для примера стандартного отклонения на конкретном шаге мне нужно было найти среднее значение сбора и разницу между значением каждого элемента и средним значением.

Для std::valarray я сделал что-то вроде:

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> temp(mean, original_values.size());
std::valarray<double> differences_from_mean = original_values - temp;

Я мог бы быть более умным с std::slice или std::gslice. Прошло уже более пяти лет.

Для std::vector я сделал что-то вроде:

std::vector<double> original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();

std::vector<double> differences_from_mean;
differences_from_mean.reserve(original_values.size());
std::transform(original_values.begin(), original_values.end(), std::back_inserter(differences_from_mean), std::bind1st(std::minus<double>(), mean));

Сегодня я бы написал это по-другому. Если бы не что иное, я бы воспользовался C++ 11 лямбдами.

Очевидно, что эти два фрагмента кода делают разные вещи. Например, пример std::vector не создает промежуточную коллекцию, как пример std::valarray. Тем не менее, я думаю, что было бы справедливо сравнивать их, потому что различия связаны с различиями между std::vector и std::valarray.

Когда я писал этот ответ, я подозревал, что вычитание значения элементов из двух std::valarray (последняя строка в примере std::valarray) будет менее дружественным к кешу, чем соответствующая строка в примере std::vector (который бывает также последняя строка).

Оказывается, однако, что

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> differences_from_mean = original_values - mean;

Делает то же самое, что и пример std::vector, и имеет почти идентичную производительность. В конце концов, вопрос в том, какой API вы предпочитаете.

Ответ 5

valarray предполагалось, что некоторая формальность векторной обработки FORTRAN стирается на С++. Как-то необходимая поддержка компилятора никогда не происходила.

В книгах Josuttis есть интересный (несколько пренебрежительный) комментарий по valarray (здесь и здесь).

Однако теперь Intel, похоже, пересматривает valarray в своих последних выпусках компилятора (например, см. слайд 9); это интересная разработка, учитывая, что их четырехпозиционный набор инструкций SIMD SSE планируется объединить с 8-сторонними AVX и 16-сторонними инструкциями Larrabee, и в интересах переносимости, вероятно, будет намного лучше кодировать абстракцию, такую ​​как valarray, чем (скажем) внутренности.

Ответ 6

Я нашел одно хорошее использование для valarray. Это использовать Valarray, как NumPy массивов.

auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);

enter image description here

Мы можем реализовать выше с Valarray.

valarray<float> linspace(float start, float stop, int size)
{
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + i * (stop-start)/size;
    return v;
}

std::valarray<float> arange(float start, float step, float stop)
{
    int size = (stop - start) / step;
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + step * i;
    return v;
}

string psstm(string command)
{//return system call output as string
    string s;
    char tmp[1000];
    FILE* f = popen(command.c_str(), "r");
    while(fgets(tmp, sizeof(tmp), f)) s += tmp;
    pclose(f);
    return s;
}

string plot(const valarray<float>& x, const valarray<float>& y)
{
    int sz = x.size();
    assert(sz == y.size());
    int bytes = sz * sizeof(float) * 2;
    const char* name = "plot1";
    int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, bytes);
    float* ptr = (float*)mmap(0, bytes, PROT_WRITE, MAP_SHARED, shm_fd, 0);
    for(int i=0; i<sz; i++) {
        *ptr++ = x[i];
        *ptr++ = y[i];
    }

    string command = "python plot.py ";
    string s = psstm(command + to_string(sz));
    shm_unlink(name);
    return s;
}

Также нам нужен скрипт на python.

import sys, posix_ipc, os, struct
import matplotlib.pyplot as plt

sz = int(sys.argv[1])
f = posix_ipc.SharedMemory("plot1")
x = [0] * sz
y = [0] * sz
for i in range(sz):
    x[i], y[i] = struct.unpack('ff', os.read(f.fd, 8))
os.close(f.fd)
plt.plot(x, y)
plt.show()

Ответ 7

В стандарте С++ 11 говорится:

Классы массива valarray определены как свободные от определенных форм aliasing, что позволяет оптимизировать операции над этими классами.

См. С++ 11 26.6.1-2.

Ответ 8

std :: valarray предназначен для тяжелых числовых задач, таких как "Динамика вычислительной жидкости" или "Динамика вычислительной структуры", в которых у вас есть массивы с миллионами, иногда десятками миллионов элементов, и вы выполняете их в цикле с миллионами шагов. Возможно, сегодня std :: vector имеет сравнимую производительность, но около 15 лет назад valarray был почти обязателен, если вы хотели написать эффективный числовой решатель.

Ответ 9

Так как С++ 98, стандартная библиотека С++ предоставляет класс valarray < > для обработки массивов числовых значения. Валарай представляет собой представление математического понятия линейной последовательности значений. Он имеет одно измерение, но вы можете получить иллюзию более высокой размерности с помощью специальных методов вычисленных индексов и мощных возможностей подмножества. Следовательно, ванарра может использоваться как как для векторных, так и для матричных операций и для обработки математических систем полиномиальных уравнения с хорошей производительностью. Классы valarray позволяют некоторым сложным оптимизациям получить хорошую производительность для обработки массивов значений. Однако неясно, насколько важен этот компонент стандартной библиотеки С++ будет в будущем, потому что другие интересные разработки работают еще лучше. Один из многих Интересными примерами являются система Блица. Если вы заинтересованы в цифровой обработке, вы должны посмотри на это. Клаузарные классы не были разработаны очень хорошо. На самом деле никто не пытался определить, окончательная спецификация работала. Это произошло потому, что никто не чувствовал себя "ответственным" за эти занятия. люди, которые внедрили valarrays в стандартную библиотеку С++, покинули комитет задолго до первого Стандарт С++ был закончен. Например, для использования валарианов вам часто нужны неудобные и длительные преобразования типов.