[c++] 주문 번호의 효율적이고 안정적인 합계

부동 소수점 양수 ( std::vector<float>, size ~ 1000)의 목록이 꽤 있습니다. 숫자는 내림차순으로 정렬됩니다. 내가 순서대로 합하면 :

for (auto v : vec) { sum += v; }

나는 벡터의 끝 부분에 가까운 이후 나는 약간의 수치 안정성에 문제가있을 수 있습니다 생각 sum보다 훨씬 클 것이다 v. 가장 쉬운 해결책은 벡터를 역순으로 순회하는 것입니다. 내 질문은 : 앞으로뿐만 아니라 효율적입니까? 더 많은 캐시가 누락됩니까?

다른 스마트 솔루션이 있습니까?



답변

수치 적 안정성 문제가있을 수 있습니다

테스트 해보십시오. 현재 가상의 문제가 있습니다. 즉, 전혀 문제가 없습니다.

테스트를 수행하고 가정이 실제 문제 로 구체화 되면 실제로 수정하는 것에 대해 걱정해야합니다.

즉, 부동 소수점 정밀도로 인해 문제 발생할 있지만 다른 모든 것보다 우선 순위를 부여하기 전에 실제로 데이터에 대한 여부를 확인할 수 있습니다.

… 더 많은 캐시가 누락됩니까?

1 천 개의 플로트는 4Kb입니다. 현대식 대량 시장 시스템의 캐시에 적합합니다 (다른 플랫폼을 염두에두고 있다면 그것이 무엇인지 알려주십시오).

유일한 위험은 프리 페 처가 뒤로 반복 할 때 도움이되지 않지만 물론 벡터가 이미 캐시에 있을 수 있다는 것입니다. 전체 프로그램의 맥락에서 프로파일 링 할 때까지 실제로이를 확인할 수 없으므로 전체 프로그램이있을 때까지 걱정할 필요가 없습니다.

다른 스마트 솔루션이 있습니까?

실제로 문제가 될 때까지 문제가 될 수있는 것에 대해 걱정하지 마십시오. 기껏해야 가능한 문제에 주목하고 코드를 구성하여 다른 모든 것을 다시 쓰지 않고도 가장 간단한 솔루션을 신중하게 최적화 된 솔루션으로 대체 할 수 있습니다.


답변

I의 벤치 마크 사용 사례 및 결과는 루프에 대한 성능 차이 앞뒤로하지 않는 방향으로 점 (첨부 이미지 참조).

하드웨어 + 컴파일러에서도 측정 할 수 있습니다.


STL을 사용하여 합계를 수행하면 데이터를 수동으로 반복하는 것만 큼 빠르지 만 훨씬 표현력이 좋습니다.

역 축적에는 다음을 사용하십시오.

std::accumulate(rbegin(data), rend(data), 0.0f);

앞으로 누적하는 동안 :

std::accumulate(begin(data), end(data), 0.0f);

여기에 이미지 설명을 입력하십시오


답변

가장 쉬운 해결책은 벡터를 역순으로 순회하는 것입니다. 내 질문은 : 앞으로뿐만 아니라 효율적입니까? 더 많은 캐시가 누락됩니까?

예, 효율적입니다. 하드웨어의 지점 예측 및 스마트 캐시 전략은 순차적 액세스를 위해 조정됩니다. 벡터를 안전하게 축적 할 수 있습니다.

#include <numeric>

auto const sum = std::accumulate(crbegin(v), crend(v), 0.f);


답변

이를 위해 다음과 같이 조옮김없이 역 반복자를 사용할 수 있습니다 std::vector<float> vec.

float sum{0.f};
for (auto rIt = vec.rbegin(); rIt!= vec.rend(); ++rIt)
{
    sum += *rit;
}

또는 표준 알고리즘을 사용하여 동일한 작업을 수행하십시오.

float sum = std::accumulate(vec.crbegin(), vec.crend(), 0.f);

성능은 동일해야하며 벡터의 바이 패스 방향 만 변경해야합니다.


답변

수치 적 안정성으로 정확성을 의미한다면, 정확도 문제가 생길 수 있습니다. 가장 큰 값과 가장 작은 값의 비율 및 결과의 정확성에 대한 요구 사항에 따라 문제가 될 수도 있고 아닐 수도 있습니다.

높은 정확도를 원한다면 Kahan summation 을 고려하십시오 -오류 보상을 위해 여분의 부동 소수점을 사용합니다. 또한이 페어의 합은 .

정확도와 시간 간의 트레이드 오프에 대한 자세한 분석은 이 기사를 참조 하십시오 .

C ++ 17 업데이트 :

다른 답변 중 일부는 언급했다 std::accumulate. C ++ 17부터 알고리즘을 병렬화 할 수 있는 실행 정책 이 있습니다.

예를 들어

#include <vector>
#include <execution>
#include <iostream>
#include <numeric>

int main()
{
   std::vector<double> input{0.1, 0.9, 0.2, 0.8, 0.3, 0.7, 0.4, 0.6, 0.5};

   double reduceResult = std::reduce(std::execution::par, std::begin(input), std::end(input));

   std:: cout << "reduceResult " << reduceResult << '\n';
}

결정적이지 않은 반올림 오류로 인해 큰 데이터 세트를 더 빠르게 합산해야합니다 (사용자가 스레드 분할을 결정할 수 없다고 가정합니다).


답변