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



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

660   7  

7 ответов:

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

У 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::valarray s in Язык Программирования C++ упоминает этот необычный API и тот факт, что с std::valarrays, как ожидается, будут сильно оптимизированы, любые сообщения об ошибках, которые вы получите при их использовании, вероятно, будут не интуитивно понятный.

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

Comments

    Ничего не найдено.