Tổng số ổn định của số thứ tự


12

Tôi có một danh sách khá dài các số dương dấu phẩy động ( std::vector<float>, kích thước ~ 1000). Các số được sắp xếp theo thứ tự giảm dần. Nếu tôi tổng hợp chúng theo thứ tự:

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

Tôi đoán tôi có thể có một số vấn đề ổn định số, vì gần cuối của vectơ sumsẽ lớn hơn nhiều v. Giải pháp đơn giản nhất là đi qua vectơ theo thứ tự ngược lại. Câu hỏi của tôi là: đó có phải là hiệu quả cũng như trường hợp chuyển tiếp? Tôi sẽ có nhiều bộ nhớ cache bị mất?

Có giải pháp thông minh nào khác không?


1
Câu hỏi tốc độ rất dễ trả lời. Điểm chuẩn nó.
Davide Spataro

Là tốc độ quan trọng hơn độ chính xác?
stark

Không hoàn toàn trùng lặp, nhưng câu hỏi rất giống nhau: tổng của chuỗi sử dụng float
acraig5075

4
Bạn có thể phải chú ý đến số âm.
AProgrammer

3
Nếu bạn thực sự quan tâm đến độ chính xác đến mức độ cao, hãy xem tổng kết Kahan .
Max Langhof

Câu trả lời:


3

Tôi đoán tôi có thể có một số vấn đề ổn định số

Vì vậy, kiểm tra cho nó. Hiện tại bạn có một vấn đề giả định, có thể nói, không có vấn đề gì cả.

Nếu bạn kiểm tra, và giả thuyết cụ thể hóa thành một vấn đề thực tế , thì bạn nên lo lắng về việc thực sự sửa nó.

Đó là - độ chính xác của dấu phẩy động có thể gây ra sự cố, nhưng bạn có thể xác nhận xem nó có thực sự đúng với dữ liệu của bạn hay không, trước khi ưu tiên điều đó hơn mọi thứ khác.

... Tôi sẽ thiếu bộ nhớ cache nhiều hơn?

Một nghìn float là 4Kb - nó sẽ phù hợp với bộ nhớ cache trên hệ thống thị trường đại chúng hiện đại (nếu bạn có một nền tảng khác trong tâm trí, hãy cho chúng tôi biết đó là gì).

Rủi ro duy nhất là trình tải trước sẽ không giúp bạn khi lặp lại, nhưng tất nhiên véc tơ của bạn có thể đã ở trong bộ đệm. Bạn thực sự không thể xác định điều này cho đến khi bạn cấu hình trong ngữ cảnh của chương trình đầy đủ của mình, vì vậy không có gì phải lo lắng về nó cho đến khi bạn có một chương trình đầy đủ.

Có giải pháp thông minh nào khác không?

Đừng lo lắng về những điều có thể trở thành vấn đề, cho đến khi chúng thực sự trở thành vấn đề. Nhiều nhất là đáng chú ý các vấn đề có thể xảy ra và cấu trúc mã của bạn để bạn có thể thay thế giải pháp đơn giản nhất có thể bằng một giải pháp được tối ưu hóa cẩn thận sau đó, mà không cần viết lại mọi thứ khác.


5

Tôi đã đánh dấu chuẩn trường hợp sử dụng của bạn và kết quả (xem hình ảnh đính kèm) chỉ ra hướng mà nó không tạo ra bất kỳ sự khác biệt nào về hiệu suất để lặp lại tiến hoặc lùi.

Bạn có thể muốn đo trên phần cứng + trình biên dịch của bạn.


Sử dụng STL để thực hiện tổng số nhanh như lặp thủ công trên dữ liệu nhưng biểu cảm hơn nhiều.

sử dụng như sau để tích lũy ngược:

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

trong khi tích lũy về phía trước:

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

nhập mô tả hình ảnh ở đây


Trang web đó là siêu mát mẻ. Chỉ cần chắc chắn: bạn không định thời gian cho thế hệ ngẫu nhiên, phải không?
Ruggero Turra

Không, chỉ có phần trong statevòng lặp được tính thời gian.
Davide Spataro

2

Giải pháp đơn giản nhất là đi qua vectơ theo thứ tự ngược lại. Câu hỏi của tôi là: đó có phải là hiệu quả cũng như trường hợp chuyển tiếp? Tôi sẽ có nhiều bộ nhớ cache bị mất?

Có nó là hiệu quả. Dự đoán chi nhánh và chiến lược bộ đệm thông minh từ phần cứng của bạn được điều chỉnh để truy cập tuần tự. Bạn có thể tích lũy vector của mình một cách an toàn:

#include <numeric>

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

2
Bạn có thể làm rõ: trong bối cảnh này, "truy cập tuần tự" có nghĩa là tiến, lùi hoặc cả hai?
Ruggero Turra

1
@RuggeroTurra Tôi không thể trừ khi tôi có thể tìm thấy một nguồn và tôi không có tâm trạng để đọc dữ liệu CPU ngay bây giờ.
YSC

@RuggeroTurra Thông thường truy cập tuần tự có nghĩa là chuyển tiếp. Tất cả các trình tải trước bộ nhớ bán khá đều bắt được truy cập tuần tự.
Bàn chải đánh răng

@ Bàn chân, cảm ơn. Vì vậy, nếu tôi lặp lại lạc hậu, về nguyên tắc, đó có thể là vấn đề về hiệu suất
Ruggero Turra

Về nguyên tắc, trên ít nhất một số phần cứng, nếu toàn bộ vectơ chưa trong bộ đệm L1.
Vô dụng

2

Với mục đích này, bạn có thể sử dụng trình lặp ngược mà không có bất kỳ chuyển vị nào trong std::vector<float> vec:

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

Hoặc thực hiện cùng một công việc bằng cách sử dụng thuật toán đại số tiêu chuẩn:

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

Hiệu suất phải giống nhau, chỉ thay đổi hướng bỏ qua của vectơ của bạn


Sửa lỗi cho tôi nếu tôi sai, nhưng tôi nghĩ rằng điều này thậm chí còn hiệu quả hơn so với tuyên bố foreach mà OP đang sử dụng, vì nó giới thiệu một chi phí chung. YSC nói đúng về phần ổn định số, tho.
sephiroth

4
@sephiroth Không, bất kỳ trình biên dịch nửa vời nào sẽ không thực sự quan tâm bạn đã viết một phạm vi cho hay một trình vòng lặp cho.
Max Langhof

1
Hiệu suất trong thế giới thực được quyết định là không đảm bảo giống nhau, do lưu trữ / tìm nạp trước. Thật hợp lý khi OP cảnh giác với điều đó.
Max Langhof

1

Nếu bằng sự ổn định bằng số, bạn có nghĩa là chính xác, thì có, bạn có thể kết thúc với các vấn đề chính xác. Tùy thuộc vào tỷ lệ của các giá trị lớn nhất đến nhỏ nhất và yêu cầu của bạn về độ chính xác trong kết quả, điều này có thể hoặc không thể là một vấn đề.

Nếu bạn muốn có độ chính xác cao, thì hãy xem xét tổng kết Kahan - điều này sử dụng một số float bổ sung để bù lỗi. Ngoài ra còn có tổng kết cặp .

Để phân tích chi tiết về sự đánh đổi giữa độ chính xác và thời gian, xem bài viết này .

CẬP NHẬT cho C ++ 17:

Một vài câu trả lời khác đề cập std::accumulate. Vì C ++ 17 có các chính sách thực thi cho phép các thuật toán được song song hóa.

Ví dụ

#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';
}

Điều này sẽ làm cho việc tổng hợp các bộ dữ liệu lớn nhanh hơn với chi phí cho các lỗi làm tròn không xác định (tôi giả định rằng người dùng sẽ không thể xác định phân vùng luồng).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.