2009-10-21 2 views
130

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

+2

Только обдумывал это на днях. Насколько я знаю, это действительно так же, как специализированный математический вектор. – GManNickG

+0

Не делает ли valarray шаблоны выражений? –

+0

Физик Ульрих Мутце предоставляет прецедент для 'valarray' [здесь] (http://goo.gl/QVDJKq) и [здесь] (http://goo.gl/ja7q8d) – lifebalance

ответ

60

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

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

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

+13

У Fortran были указатели со времен Fortran 90 – user2023370

+6

Они не предназначены для того, чтобы избежать указателей. C++ 11 определяет begin() и end() в valarray, которые возвращают итераторам к ним –

+3

@ user2023370: вот почему так много пользователей Fortran предпочитают Fortran 77. :) – Michael

33

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

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

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

+0

шаблоны вычетов можно также зачислить в Vandevoorde тоже, правильно? –

+0

@ Никос: Не то, чтобы я знал. Хотя я мог ошибаться. Что у вас есть за это чтение? – sbi

+0

упоминается в книге «C++ Templates - полное руководство», я думаю, что принято считать, что оба изобрели их [самостоятельно] (http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Сайт/статьи/CUJ/2003/0306/cuj0306becker /). –

22

valarray должен был позволить некоторому совершенствованию векторной обработки FORTRAN стираться на C++. Как-то необходимая поддержка компилятора никогда не происходила.

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

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

116

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.

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

+0

Является ли оно специально запрещенным для хранения произвольных типов объектов внутри valarray? – Mehrdad

+4

@Mehrdad: Да - есть (довольно длинный) список ограничений в [Numeric.Requirements]. Всего за пару примеров все абстрактные классы и исключения запрещены. Он также требует эквивалентности (например) построения копии и последовательности построения по умолчанию, за которой следует назначение. –

+0

@ Джерри Коффин, это страшно. мы обещаем, что мы его не будем использовать. –

23

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

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

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

+0

Я не могу думать о какой-либо причине, почему 'std :: vector' будет лучше играть с кешами, чем' std :: valarray'; они оба выделяют единый непрерывный блок памяти для своих элементов. – musiphil

+1

@musiphil Мой ответ слишком длинный для комментария, поэтому я обновил ответ. –

+0

Спасибо за ваш обновленный ответ. – musiphil

7

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

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

См. C++ 11 26.6.1-2.

+0

Поскольку я предполагаю, что стандарт определяет, какие формы, вы можете их процитировать? Кроме того, реализованы ли они с помощью кодовых трюков или являются исключениями на основе компилятора для правил псевдонимов в другом месте на этом языке? –

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()