วิธีที่เร็วที่สุดในการรีเซ็ตค่าทุกค่าของ std :: vector <int> เป็น 0


199

วิธีที่เร็วที่สุดในการรีเซ็ตค่าทุกค่าเป็นstd::vector<int>0 และรักษาขนาดเริ่มต้นของเวกเตอร์คืออะไร

สำหรับลูปที่มีโอเปอเรเตอร์ [] หรือไม่



1
"เร็วที่สุด" ในประสิทธิภาพ? หรือในวิธีที่ง่ายที่สุดที่จะใช้ / บำรุงรักษา?
TheGeneral

คำตอบ:


346
std::fill(v.begin(), v.end(), 0);

49
เมื่อดูที่แอสเซมบลีเอาท์พุท gcc จะทำการยกเลิกการวนลูปนี้โดยใช้การลงทะเบียน mmx เพื่อถ่ายโอนข้อมูลในครั้งละ 16 ไบต์จนกว่ามันจะเข้าใกล้จุดสิ้นสุด ฉันว่ามันค่อนข้างเร็ว รุ่น memset ข้ามไปเป็น memset ซึ่งฉันเดาว่ามันเร็วพอ ฉันจะใช้วิธีการของคุณ
Omnifarious

แต่การกระโดดไปที่ memset เป็นคำสั่งเดียวดังนั้นการใช้มันจะส่งผลให้มีขนาดไบนารีที่เล็กกว่า
Alexander Shishenko

2
นี่ไม่ใช่สิ่งที่ OP ต้องการ แต่เพียงกำหนดเวกเตอร์ของคุณให้เป็นขนาดใหม่ ( v = std::vector<int>(vec_size,0)) ดูเหมือนว่าจะเร็วกว่าfillบนเครื่องของฉันเล็กน้อย
Yibo Yang

1
assignวิธีนี้เป็นวิธีสำนวนมากที่สุดของการทำมันสำนวนมากกว่าการใช้
alfC

1
กำหนดให้กับเวกเตอร์ใหม่จะทำการจัดสรรฮีปหรือไม่ ยกเลิกการจัดสรรเวกเตอร์ที่มีอยู่แล้วหรือไม่ ฉันเห็นว่าการช้ากว่า memset และคณะ
Conrad Jones

151

เช่นเคยเมื่อคุณถามเกี่ยวกับเร็วที่สุด: วัด! การใช้วิธีการด้านบน (สำหรับ Mac โดยใช้เสียงดังกราว):

Method      |  executable size  |  Time Taken (in sec) |
            |  -O0    |  -O3    |  -O0      |  -O3     |  
------------|---------|---------|-----------|----------|
1. memset   | 17 kB   | 8.6 kB  | 0.125     | 0.124    |
2. fill     | 19 kB   | 8.6 kB  | 13.4      | 0.124    |
3. manual   | 19 kB   | 8.6 kB  | 14.5      | 0.124    |
4. assign   | 24 kB   | 9.0 kB  | 1.9       | 0.591    |

ใช้การทำซ้ำ 100000 ครั้งบนเวกเตอร์ที่มี 10,000 รายการ

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

สำหรับการอ้างอิงรหัสที่ใช้:

#include <vector>

#define TEST_METHOD 1
const size_t TEST_ITERATIONS = 100000;
const size_t TEST_ARRAY_SIZE = 10000;

int main(int argc, char** argv) {

   std::vector<int> v(TEST_ARRAY_SIZE, 0);

   for(size_t i = 0; i < TEST_ITERATIONS; ++i) {
   #if TEST_METHOD == 1 
      memset(&v[0], 0, v.size() * sizeof v[0]);
   #elif TEST_METHOD == 2
      std::fill(v.begin(), v.end(), 0);
   #elif TEST_METHOD == 3
      for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) {
         *it = 0;
      }
   #elif TEST_METHOD == 4
      v.assign(v.size(),0);
   #endif
   }

   return EXIT_SUCCESS;
}

บทสรุป:ใช้std::fill(เพราะอย่างที่คนอื่นพูดถึงมันมากที่สุด)!


3
+1 เกณฑ์มาตรฐานเฉพาะนี้ไม่ได้ข้อสรุป แต่ประเด็นนี้ถูกต้องอย่างยิ่งคุณควรเขียนการทดสอบประสิทธิภาพของตัวเลือกอื่น ๆ เนื่องจากจะใช้จริง หากไม่มีความแตกต่างด้านประสิทธิภาพให้ใช้แหล่งที่มาที่ง่ายที่สุด
Steve Jessop

3
"... ไม่ได้ข้อสรุป ... " IMO ความไม่ลงรอยกันในตัวเองนี้เป็นจุดที่ดีสำหรับการทำเกณฑ์มาตรฐานบ่อยครั้งกว่าเครื่องมือเพิ่มประสิทธิภาพจะทำงานได้ดีมากสำหรับสถานการณ์ที่ OP ถาม และฉันจะปรับเปลี่ยนประโยคสุดท้ายของคุณเพื่ออ่าน "หากไม่มีความแตกต่างด้านประสิทธิภาพที่สำคัญ ... "
Fabio Fracassi

4
อัปเดตการใช้Noniusสำหรับการวัดประสิทธิภาพ: clang3.6-libc ++ - c ++ 1y-O3 , gcc4.9-c ++ 1y-O3และgcc5-c ++ 1y-O3 - TL; DR : assignช้าลงยกเว้นความจุขนาดเล็ก libc++บน รหัสcoliru / วาง
sehe

2
นอกจากนี้ว้าวถ้าคุณสนใจความเร็วโดยไม่มีการเพิ่มประสิทธิภาพ (ซึ่งอาจเป็นไปได้ถ้าคุณปรับใช้ในโหมด 'ดีบั๊ก' ซึ่งบางทีมทำ) fillดูแย่มาก มันเป็นคำสั่งสองขนาดที่ช้ากว่าในการทดสอบนี้
Kyle Strand

5
@ KyleStrand: ไม่ใช่ว่าการเติมนั้นแย่มากมันเป็นเทมเพลตและรหัสนั้นถูกสร้างขึ้นด้วย -O0 ภายในหน่วยการแปลของคุณ เมื่อคุณใช้ memset คุณกำลังใช้รหัส libc ซึ่งถูกคอมไพล์ด้วย -O3 (แม้ว่าคุณจะคอมไพล์โค้ดด้วย -O0) ถ้าคุณใส่ใจเรื่องความเร็วในการดีบั๊กและใช้เทมเพลตคุณจะต้องใช้การสร้างอินสแตนซ์เทมเพลตอย่างชัดเจนในไฟล์แยกต่างหากซึ่งคุณได้คอมไพล์ด้วย -O3
Tic

25

วิธีการเกี่ยวกับassignฟังก์ชั่นสมาชิก?

some_vector.assign(some_vector.size(), 0);

2
OP ต้องการรีเซ็ตค่าที่มีอยู่ แต่คำตอบของคุณดีกว่าเมื่อต้องการปรับขนาดและรีเซ็ตค่า ขอบคุณ!

15

ถ้ามันเป็นเพียงเวกเตอร์ของจำนวนเต็มฉันจะลองก่อน:

memset(&my_vector[0], 0, my_vector.size() * sizeof my_vector[0]);

มันไม่ได้เป็น C ++ มากดังนั้นฉันแน่ใจว่ามีบางคนที่จะให้วิธีการที่ถูกต้อง :)


3
เนื่องจากมาตรฐาน (2003 TC1) รับประกันได้ว่า std :: vector นั้นต่อเนื่องกันในหน่วยความจำจึงน่าจะใช้ได้ หากไลบรารี c ++ ของคุณไม่สอดคล้องกับ 2003 TC1 แสดงว่าไม่ใช้สิ่งนี้
Mario

2
@ มาริโอ: ฉันจะไม่โพสต์สิ่งนี้เว้นแต่ว่าเป็นเรื่องจริงและคิดว่าเป็นที่รู้จักกันดีแน่นอน :) แต่ขอบคุณ.
คลาย

1
ฉันตรวจสอบชุดประกอบ ::std::fillวิธีการขยายไปยังสิ่งที่ถูกสาปอย่างรวดเร็วแม้ว่าบิตทางด้านรหัส bloaty เพราะมันเป็นแบบอินไลน์ทั้งหมด ฉันยังคงใช้มันเพราะมันดีกว่าที่จะอ่าน
Omnifarious

4
คุณควรที่จะเพิ่มการตรวจสอบว่าเวกเตอร์ว่างเปล่าและไม่ทำอะไรเลยในกรณีนี้ การคำนวณ & buf [0] สำหรับเวกเตอร์เปล่าสามารถสร้างการยืนยันในรหัส STL
Sergey

4

ลอง

std::fill

และนอกจากนี้ยังมี

std::size siz = vec.size();
//no memory allocating
vec.resize(0);
vec.resize(siz, 0);

ปรับขนาดเป็นสิ่งที่ดีมาก
Nick

4

ฉันมีคำถามเดียวกัน แต่ค่อนข้างสั้นvector<bool>(afaik มาตรฐานอนุญาตให้นำมาใช้ภายในแตกต่างจากองค์ประกอบบูลีนต่อเนื่อง) ดังนั้นฉันจึงทำการทดสอบที่แก้ไขเล็กน้อยโดย Fabio Fracassi ซ้ำอีกครั้ง ผลลัพธ์มีดังนี้ (คูณเป็นวินาที):

            -O0       -O3
         --------  --------
memset     0.666     1.045
fill      19.357     1.066
iterator  67.368     1.043
assign    17.975     0.530
for i     22.610     1.004

เห็นได้ชัดว่าขนาดเหล่านี้vector<bool>::assign()เร็วกว่า รหัสที่ใช้สำหรับการทดสอบ:

#include <vector>
#include <cstring>
#include <cstdlib>

#define TEST_METHOD 5
const size_t TEST_ITERATIONS = 34359738;
const size_t TEST_ARRAY_SIZE = 200;

using namespace std;

int main(int argc, char** argv) {

    std::vector<int> v(TEST_ARRAY_SIZE, 0);

    for(size_t i = 0; i < TEST_ITERATIONS; ++i) {
#if TEST_METHOD == 1
        memset(&v[0], false, v.size() * sizeof v[0]);
#elif TEST_METHOD == 2
        std::fill(v.begin(), v.end(), false);
   #elif TEST_METHOD == 3
        for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) {
            *it = 0;
        }
   #elif TEST_METHOD == 4
      v.assign(v.size(),false);
   #elif TEST_METHOD == 5
      for (size_t i = 0; i < TEST_ARRAY_SIZE; i++) {
          v[i] = false;
      }
#endif
    }

    return EXIT_SUCCESS;
}

ฉันใช้คอมไพเลอร์ GCC 7.2.0 บน Ubuntu 17.10 บรรทัดคำสั่งสำหรับการรวบรวม:

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