Eu tenho uma lista bastante longa de números positivos de ponto flutuante ( std::vector<float>
, tamanho ~ 1000). Os números são classificados em ordem decrescente. Se eu somar eles seguindo a ordem:
for (auto v : vec) { sum += v; }
Acho que posso ter algum problema de estabilidade numérica, já que perto do final do vetor sum
será muito maior que v
. A solução mais fácil seria atravessar o vetor na ordem inversa. Minha pergunta é: isso é eficiente e também o caso a seguir? Faltarei mais cache?
Existe alguma outra solução inteligente?
c++
floating-point
precision
Ruggero Turra
fonte
fonte
Respostas:
Então teste para isso. Atualmente, você tem um problema hipotético, ou seja, nenhum problema.
Se você testar, e o hipotético se materializar em um problema real , deverá se preocupar em corrigi-lo.
Ou seja, a precisão de ponto flutuante pode causar problemas, mas você pode confirmar se realmente faz com seus dados, antes de priorizar isso sobre todo o resto.
Milhares de carros alegóricos são 4Kb - caberão no cache de um sistema moderno de mercado de massa (se você tiver outra plataforma em mente, diga-nos o que é).
O único risco é que o pré-buscador não o ajude ao iterar para trás, mas é claro que seu vetor já pode estar no cache. Você realmente não pode determinar isso até criar um perfil no contexto de seu programa completo, portanto não adianta se preocupar com isso até ter um programa completo.
Não se preocupe com coisas que podem se tornar problemas, até que elas realmente se tornem problemas. No máximo, vale a pena observar possíveis problemas e estruturar seu código para que você possa substituir a solução mais simples possível por uma cuidadosamente otimizada posteriormente, sem reescrever todo o resto.
fonte
I banco marcado seu caso de uso e os resultados (ver imagem em anexo) apontam para a direção que não faz qualquer diferença de desempenho para fazer um loop para a frente ou para trás.
Você também pode medir no seu hardware + compilador.
O uso do STL para realizar a soma é tão rápido quanto o loop manual dos dados, mas muito mais expressivo.
use o seguinte para acumulação reversa:
enquanto para acumulação direta:
fonte
state
loop é cronometrada.Sim, é eficiente. A previsão de ramificação e a estratégia de cache inteligente do seu hardware são ajustadas para acesso seqüencial. Você pode acumular com segurança seu vetor:
fonte
Para esse fim, você pode usar o iterador reverso sem nenhuma transposição no seu
std::vector<float> vec
:Ou faça o mesmo trabalho usando o algoritmo padrão:
O desempenho deve ser o mesmo, alterado apenas na direção de desvio do seu vetor
fonte
Se por estabilidade numérica você quer dizer precisão, sim, você pode acabar com problemas de precisão. Dependendo da proporção do maior para o menor, e de seus requisitos de precisão no resultado, isso pode ou não ser um problema.
Se você deseja ter alta precisão, considere o somatório de Kahan - isso usa um flutuador extra para compensação de erro. Também há somas aos pares .
Para uma análise detalhada da troca entre precisão e tempo, consulte este artigo .
ATUALIZAÇÃO para C ++ 17:
Algumas das outras respostas mencionam
std::accumulate
. Desde o C ++ 17, existem políticas de execução que permitem que os algoritmos sejam paralelizados.Por exemplo
Isso deve tornar a soma de conjuntos de dados grandes mais rápida, com o custo de erros de arredondamento não determinísticos (suponho que o usuário não consiga determinar o particionamento do encadeamento).
fonte