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

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

7 ответов


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

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

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


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

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

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

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

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

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

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

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


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

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

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


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

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

из любопытства, около года назад я pitted std::valarray против std::vector. У меня больше нет кода или точных результатов (хотя не должно быть сложно написать свой собственный). Использование GCC I сделал получить небольшое преимущество производительности при использовании std::valarray для простой математики, но не для моих реализаций для вычисления стандартного отклонения (и, конечно, стандартное отклонение не так сложно, насколько математика идет). я подозреваю, что операции на каждом пункт в большом std::vector играть лучше с кэшем, чем операции на std::valarrays. (Примечание, следующие советы 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 lambdas.

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

когда я писал этот ответ, я подозревал, что вычитания элементов из двух std::valarrays (последняя строка в 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 вы предпочитаете.


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

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

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


стандарт C++11 говорит:

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

См. C++11 26.6.1-2.


Я нашел одно хорошее использование для 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()