ผลรวมที่มีเสถียรภาพของตัวเลขที่สั่งซื้อ


12

ฉันมีจำนวนบวกของตัวเลขบวกจำนวนจุดลอยตัว ( std::vector<float>ขนาด ~ 1,000) ตัวเลขจะเรียงตามลำดับที่ลดลง ถ้าฉันรวมพวกเขาตามคำสั่ง:

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

ฉันเดาว่าฉันอาจมีปัญหาเสถียรภาพเชิงตัวเลขเนื่องจากใกล้ถึงจุดสิ้นสุดของเวกเตอร์sumจะยิ่งใหญ่กว่าvมาก วิธีแก้ปัญหาที่ง่ายที่สุดคือสำรวจเวกเตอร์ในลำดับย้อนกลับ คำถามของฉันคือ: มีประสิทธิภาพเช่นเดียวกับกรณีไปข้างหน้า? ฉันจะมีแคชหายไปอีกหรือไม่

มีโซลูชันอัจฉริยะอื่น ๆ อีกหรือไม่


1
คำถามความเร็วตอบง่าย เกณฑ์มาตรฐาน
Davide Spataro

ความเร็วสำคัญกว่าความแม่นยำหรือไม่?
สิ้นเชิง

ไม่ใช่คำถามที่ซ้ำกัน แต่คล้ายกันมาก: ผลรวมของซีรี่ส์โดยใช้ float
acraig5075

4
คุณอาจต้องใส่ใจกับตัวเลขติดลบ
AProgrammer

3
ถ้าคุณจริงดูแลเกี่ยวกับความแม่นยำในระดับสูง, ตรวจสอบKahan บวก
Max Langhof

คำตอบ:


3

ฉันเดาว่าฉันสามารถมีปัญหาความมั่นคงเชิงตัวเลข

ดังนั้นลองทดสอบดู ขณะนี้คุณมีปัญหาสมมุติฐานซึ่งก็คือไม่มีปัญหาเลย

หากคุณทดสอบและสมมุติฐานปรากฏเป็นปัญหาจริงแล้วคุณควรกังวลเกี่ยวกับการแก้ไขจริง

นั่นคือ - ความแม่นยำจุดลอยตัวสามารถทำให้เกิดปัญหาได้ แต่คุณสามารถยืนยันได้ว่าเป็นประโยชน์กับข้อมูลของคุณหรือไม่ก่อนที่จะให้ความสำคัญกับสิ่งอื่น

... ฉันจะมีแคชมากกว่านี้หรือไม่

หนึ่งพันลอยคือ 4Kb - มันจะพอดีกับแคชในระบบตลาดมวลชนที่ทันสมัย ​​(ถ้าคุณมีแพลตฟอร์มอื่นในใจบอกเราว่ามันคืออะไร)

ความเสี่ยงเพียงอย่างเดียวคือ prefetcher จะไม่ช่วยให้คุณเมื่อ iterating ถอยหลัง แต่แน่นอนเวกเตอร์ของคุณอาจอยู่แล้วจะอยู่ในแคช คุณไม่สามารถระบุสิ่งนี้ได้จนกว่าคุณจะโพรไฟล์ในบริบทของโปรแกรมเต็มของคุณดังนั้นจึงไม่มีประโยชน์ที่จะกังวลจนกว่าคุณจะมีโปรแกรมเต็ม

มีโซลูชันอัจฉริยะอื่น ๆ อีกหรือไม่

อย่ากังวลเกี่ยวกับสิ่งต่าง ๆ ที่อาจเป็นปัญหาจนกว่าพวกเขาจะกลายเป็นปัญหา ส่วนใหญ่มันมีค่าที่จะสังเกตเห็นปัญหาที่อาจเกิดขึ้นและจัดโครงสร้างโค้ดของคุณเพื่อให้คุณสามารถแทนที่โซลูชันที่ง่ายที่สุดเท่าที่จะเป็นไปได้ด้วยการปรับปรุงอย่างระมัดระวังในภายหลังโดยไม่ต้องเขียนทุกอย่างอีก


5

ฉันทำเครื่องหมายกรณีการใช้งานของคุณและผลลัพธ์ (ดูรูปที่แนบมา) ชี้ไปที่ทิศทางที่มันไม่ได้สร้างความแตกต่างของประสิทธิภาพในการวนไปข้างหน้าหรือข้างหลัง

คุณอาจต้องการวัดกับคอมไพเลอร์ + ฮาร์ดแวร์ของคุณเช่นกัน


การใช้ STL เพื่อทำผลรวมนั้นเร็วพอ ๆ กับการวนลูปมากกว่าข้อมูล แต่แสดงออกได้มากกว่า

ใช้สิ่งต่อไปนี้สำหรับการสะสมแบบย้อนกลับ:

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

ในขณะที่ไปข้างหน้าสะสม:

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

ป้อนคำอธิบายรูปภาพที่นี่


เว็บไซต์นั้นเด็ดสุด ๆ เพียงเพื่อให้แน่ใจว่า: คุณไม่ได้จับเวลารุ่นสุ่มใช่มั้ย
Ruggero Turra

ไม่ได้stateหมดเวลาเพียงบางส่วนในการวนซ้ำ
Davide Spataro

2

วิธีที่ง่ายที่สุดก็คือสำรวจเวกเตอร์ในลำดับที่กลับกัน คำถามของฉันคือ: มีประสิทธิภาพเช่นเดียวกับกรณีไปข้างหน้า? ฉันจะมีแคชหายไปอีกหรือไม่

ใช่มันมีประสิทธิภาพ การทำนายสาขาและกลยุทธ์สมาร์ทแคชจากฮาร์ดแวร์ของคุณได้รับการปรับแต่งเพื่อการเข้าถึงแบบลำดับ คุณสามารถสะสมเวกเตอร์ของคุณได้อย่างปลอดภัย:

#include <numeric>

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

2
คุณสามารถอธิบายได้ไหม: ในบริบทนี้ "การเข้าถึงตามลำดับ" หมายถึงการส่งต่อย้อนกลับหรือทั้งสองอย่าง?
Ruggero Turra

1
@RuggeroTurra ฉันไม่สามารถยกเว้นว่าฉันสามารถหาแหล่งที่มาและฉันไม่ได้อยู่ในอารมณ์ที่จะอ่านเอกสารข้อมูลทางเทคนิคของ CPU ในขณะนี้
YSC

@RuggeroTurra โดยปกติแล้วการเข้าถึงต่อเนื่องจะหมายถึงการส่งต่อ prefetchers หน่วยความจำกึ่งดีทั้งหมดจับไปข้างหน้าการเข้าถึงตามลำดับ
แปรงสีฟัน

@ แปรงสีฟันขอบคุณ ดังนั้นถ้าฉันวนไปข้างหลังโดยหลักการแล้วมันอาจเป็นปัญหาด้านประสิทธิภาพ
Ruggero Turra

โดยหลักการแล้วในฮาร์ดแวร์อย่างน้อยถ้าเวกเตอร์ทั้งหมดไม่ได้อยู่ในแคช L1
ไร้ประโยชน์

2

เพื่อจุดประสงค์นี้คุณสามารถใช้ iterator กลับโดยไม่ต้อง transpositions ใด ๆ ในของคุณstd::vector<float> vec:

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

หรือทำงานเดียวกันโดยใช้ algortithm มาตรฐาน:

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

ประสิทธิภาพจะต้องเหมือนกันเปลี่ยนทิศทางบายพาสของเวกเตอร์ของคุณเท่านั้น


แก้ไขให้ฉันถ้าฉันผิด แต่ฉันคิดว่านี่มีประสิทธิภาพมากกว่าคำสั่ง foreach ที่ใช้อยู่เพราะจะทำให้เกิดค่าใช้จ่าย YSC ถูกต้องเกี่ยวกับส่วนความมั่นคงของตัวเลข tho
Sephiroth

4
@sephiroth ไม่คอมไพเลอร์ที่มีคุณสมบัติครึ่งหนึ่งจะไม่สนใจว่าคุณจะเขียน range-for หรือ iterator ให้หรือไม่
Max Langhof

1
ประสิทธิภาพในโลกแห่งความเป็นจริงไม่ได้รับประกันว่าจะเหมือนกันเนื่องจากแคช / การดึงข้อมูลล่วงหน้า มันสมเหตุสมผลสำหรับ OP ที่จะต้องระมัดระวัง
Max Langhof

1

หากความเสถียรของตัวเลขคุณหมายถึงความถูกต้องใช่คุณอาจพบปัญหาความแม่นยำ ขึ้นอยู่กับอัตราส่วนของค่าที่มากที่สุดต่อค่าที่น้อยที่สุดและข้อกำหนดของคุณเพื่อความถูกต้องแม่นยำในผลลัพธ์อาจมีหรือไม่มีปัญหาก็ได้

หากคุณต้องการมีความแม่นยำสูงให้พิจารณาผลรวมของ Kahanซึ่งจะใช้การลอยแบบพิเศษเพื่อชดเชยข้อผิดพลาด นอกจากนี้ยังมีผลบวกจากจำนวน

สำหรับการวิเคราะห์รายละเอียดของการถ่วงดุลอำนาจระหว่างความถูกต้องและเวลาที่ดูบทความนี้

อัปเดตสำหรับ 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';
}

สิ่งนี้จะทำให้การสรุปชุดข้อมูลขนาดใหญ่เร็วขึ้นด้วยค่าใช้จ่ายของข้อผิดพลาดในการปัดเศษ nondeterministic (ฉันสมมติว่าผู้ใช้จะไม่สามารถกำหนดเธรดการแบ่งพาร์ติชันได้)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.