Pergunta

Eu gosto de vetores muito. Eles são bacana e rápido. Mas eu sei que essa coisa chamada um valarray existe. Por que eu iria usar um valarray em vez de um vetor? Eu sei valarrays ter algum açúcar sintático, mas diferente do que, quando eles são úteis?

Foi útil?

Solução

Valarrays (matrizes de valores) são destinados a trazer alguma da velocidade de Fortran como C ++. Você não faria um valarray de ponteiros para que o compilador pode fazer suposições sobre o código e otimizá-lo melhor. (A principal razão que Fortran é tão rápido é que não há nenhum tipo de ponteiro de modo não pode haver aliasing ponteiro.)

Valarrays também têm classes que permitem que você cortá-los de uma maneira razoavelmente fácil, embora essa parte da norma poderia usar um pouco mais de trabalho. Redimensionando-los é destrutivo e eles não têm iteradores.

Então, se é números que você está trabalhando e conveniência não é tão importante valarrays uso. Caso contrário, os vetores são apenas muito mais conveniente.

Outras dicas

valarray é uma espécie de um órfão que nasceu no lugar errado na hora errada. É uma tentativa de otimização, bastante especificamente para as máquinas que foram usadas para a matemática pesados ??quando foi escrito -. Especificamente, processadores vetoriais como os Crays

Para um processador de vetor, o que geralmente queria fazer era aplicar uma única operação para uma matriz inteira, em seguida, aplicar a próxima operação a toda a matriz, e assim por diante até que você tinha feito tudo o que precisava fazer.

A menos que você está lidando com bastante pequenas matrizes, contudo, que tende a funcionar mal com o cache. Na maioria das máquinas modernas, o que você geralmente preferem (na medida do possível) seria a parte de carga da matriz, que todas as operações em que você está indo, em seguida, passar para a próxima parte da matriz.

valarray também é suposto para eliminar qualquer possibilidade de aliasing, que (pelo menos teoricamente) permite que o compilador melhorar a velocidade porque é mais livre para armazenar valores em registos. Na realidade, porém, eu não tenho certeza de que qualquer aplicação real leva vantagem deste em qualquer grau significativo. Eu suspeito que é sim uma espécie de galinha e do ovo de problema - sem suporte de compilador não se tornou popular, e enquanto isso não é popular, ninguém vai se dar ao trabalho de trabalhar em seu compilador para apoiá-lo <. / p>

Há também um (literalmente) enorme gama de classes de auxiliares para usar com valarray. Você começa slice, slice_array, gslice e gslice_array de jogar com peças de um valarray, e torná-lo agir como um array multi-dimensional. Você também terá mask_array para "mascarar" uma operação (por exemplo, adicionar itens em x para y, mas apenas nas posições em que z é diferente de zero). Para ganhar mais do que o uso trivial de valarray, você tem que aprender muito sobre essas classes auxiliares, alguns dos quais são muito complexos e nenhum dos quais parece (pelo menos para mim) muito bem documentado.

A linha inferior:., Embora tenha momentos de brilho, e pode fazer algumas coisas muito ordenadamente, há também algumas razões muito boas que é (e quase certamente irá permanecer) obscurecer

Edit (oito anos depois, em 2017): Alguns dos anteriores tornou-se obsoleto, pelo menos, algum grau. Para um exemplo, a Intel tem implementado uma versão otimizada do valarray para o seu compilador. Ele usa o Intel Integrated Performance Primitives (Intel IPP) para melhorar o desempenho. Embora a melhoria do desempenho exato varia, sem dúvida, um teste rápido com shows de código simples em torno de 2:. Melhoria de 1 em velocidade, em comparação com código idêntico compilado com a implementação "padrão" de valarray

Então, enquanto eu não estou totalmente convencido de que os programadores C ++ vai estar começando a utilização valarray em grandes números, há menos algumas circunstâncias em que ele pode fornecer uma melhoria de velocidade.

Durante a padronização do C ++ 98, valarray foi projetado para permitir algum tipo de cálculos matemáticos rápidos. No entanto, em torno desse tempo Todd Veldhuizen inventado modelos de expressão e criou Blitz ++ , e técnicas de modelos-meta semelhantes foram inventados , que fez valarrays praticamente obsoletos antes o padrão foi ainda lançado. IIRC, o proponente original (s) de valarray abandonou na metade da padronização, que (se for verdade) não ajudou ele quer.

ISTR que a principal razão pela qual não foi removido do padrão é que ninguém teve tempo para avaliar a questão cuidadosamente e escrever uma proposta para removê-lo.

Tenha em mente, porém, que tudo isso é lembrava vagamente boatos. Leve isso com um grão de sal e espero que alguém corrige ou confirma isso.

Eu sei valarrays ter algum açúcar sintático

Eu tenho que dizer que eu não acho que std::valarrays têm muito em forma de açúcar sintático. A sintaxe é diferente, mas eu não chamaria a diferença "açúcar". A API é estranho. A seção sobre std::valarrays em A C ++ Linguagem de programação menciona esta API incomum e o fato de que, uma vez std::valarrays deverão ser altamente otimizado, as mensagens de erro que você recebe ao usá-los será, provavelmente, não-intuitiva.

A título de curiosidade, cerca de um ano atrás eu pitted std::valarray contra std::vector. Eu já não tenho o código ou os resultados precisos (embora ele não deve ser difícil de escrever o seu próprio). Usando GCC I fez ficar um pouco benefício de desempenho ao utilizar std::valarray para a matemática simples, mas não para os meus implementações para calcular o desvio padrão (e, claro, o desvio padrão não é tão complexo, tão longe como matemática vai). Eu suspeito que as operações sobre cada item em um grande jogo std::vector melhor com caches do que operações em std::valarrays ( NOTA , seguindo o conselho de musiphil , eu consegui obter um desempenho quase idêntico vector e valarray).

No final, eu decidi usar std::vector enquanto prestando atenção a coisas como alocação de memória e criação do objeto temporário.


Ambos std::vector e armazenar std::valarray os dados em um bloco contíguo. No entanto, eles acessar os dados usando padrões diferentes, e mais importante, a API para std::valarray incentiva diferentes padrões de acesso do que a API para std::vector.

Para o exemplo de desvio padrão, em um passo particular I necessário para encontrar a média da recolha e a diferença entre o valor de cada elemento e a média.

Para o std::valarray, eu fiz algo como:

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;

Talvez eu tenha sido mais inteligente com std::slice ou std::gslice. Já faz mais de cinco anos.

Para std::vector, eu fiz algo ao longo das linhas de:

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

Hoje eu certamente escrever isso de forma diferente. Se nada mais, gostaria de tirar proveito de C ++ 11 lambdas.

É óbvio que esses dois trechos de código fazer coisas diferentes. Por um lado, o exemplo std::vector não faz uma coleção intermediário como o exemplo std::valarray faz. No entanto, penso que é justo compará-los porque as diferenças são amarradas às diferenças entre std::vector e std::valarray.

Quando eu escrevi essa resposta, eu suspeitava que subtraindo o valor de elementos de dois std::valarrays (última linha no exemplo std::valarray) seria menos do que a linha correspondente no exemplo std::vector (que passa a ser também o último amistoso-cache line).

Acontece, porém, que

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;

Será que a mesma coisa que o exemplo std::vector, e tem um desempenho quase idêntico. No final, a questão é que API você preferir.

valarray deveria deixar alguns Fortran esfregar bondade de processamento de vector fora em C ++. De alguma forma o apoio compilador necessário nunca realmente aconteceu.

Os livros Josuttis contém algumas interessantes (um tanto depreciativa) comentário sobre valarray ( aqui e aqui ).

No entanto, a Intel agora parecem estar revisitando valarray em seus lançamentos recentes do compilador (por exemplo, ver Slide 9 ); este é um desenvolvimento interessante, dado que o seu 4-way SIMD SSE conjunto de instruções está prestes a ser acompanhado por 8-way AVX e 16 vias instruções Larrabee e no interesse da portabilidade ele provavelmente vai ser muito melhor para o código com uma abstração como valarray do que (digamos) intrínsecos.

Eu encontrei um uso bom para valarray. É usar valarray apenas como matrizes 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 descrição da imagem aqui

Podemos implementar acima com 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;
}

Além disso, precisamos script 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()

O padrão C ++ 11 diz:

As classes de matriz valarray são definidos para ser livre de certas formas de aliasing, permitindo assim operações sobre essas classes para ser otimizado.

Veja C ++ 11 26.6.1-2.

std :: valarray é destinado a tarefas pesadas numéricos, como Computational Fluid Dynamics ou Computacional Estrutura Dynamics, em que você tem matrizes com milhões, às vezes dezenas de milhões de itens, e você iterar sobre eles em um loop com também milhões de Timesteps. Talvez std :: vector hoje tem um desempenho comparável, mas, cerca de 15 anos atrás, valarray era quase obrigatório se você queria escrever um solver numérico eficiente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top