การใช้อาร์เรย์หรือ std :: vectors ใน C ++ ช่องว่างของประสิทธิภาพคืออะไร


208

ในหลักสูตร C ++ เราไม่แนะนำให้ใช้อาร์เรย์ C ++ ในโครงการใหม่อีกต่อไป เท่าที่ฉันรู้ Stroustroup ตัวเองไม่แนะนำให้ใช้อาร์เรย์ แต่มีความแตกต่างด้านประสิทธิภาพที่สำคัญหรือไม่


2
ทำไมคุณคิดว่ามีช่องว่างของประสิทธิภาพ
Martin York เมื่อ

99
เพราะโดยปกติแล้วด้วยฟังก์ชั่นที่ดีกว่าประสิทธิภาพที่แย่ที่สุด
tunnuz

19
ฉันเห็นด้วยเกี่ยวกับการปรับให้เหมาะสมก่อนเวลา แต่การเลือกวิธีการจัดเก็บข้อมูลที่ดีกว่านั้นเป็นเรื่องที่สมเหตุสมผล บ่อยครั้งในโลกแห่งความเป็นจริงจะต้องมีการจัดส่งรหัสและผลิตภัณฑ์ถัดไปที่พัฒนาขึ้นและขั้นตอนการเพิ่มประสิทธิภาพจะไม่เกิดขึ้น
Ant

132
ฉันหวังว่าผู้คนจะหยุดกรีดร้อง "การเพิ่มประสิทธิภาพก่อนวัยอันควร!" เมื่อใดก็ตามที่มีคนถามคำถามง่ายๆเกี่ยวกับการแสดง! ตอบคำถามและอย่าเพียง แต่คาดการณ์ล่วงหน้าว่าผู้คนกำลังทำอะไรก่อนเวลาอันควร
d7samurai

4
@ d7samaurai: เห็นด้วยฉันยังไม่เห็นใครลองใช้int main(int argc, const std::vector<string>& argv)
Mark K Cowan

คำตอบ:


189

newควรหลีกเลี่ยงการใช้ C ++ กับ(นั่นคือการใช้อาร์เรย์แบบไดนามิก) มีปัญหาที่คุณต้องติดตามขนาดและคุณต้องลบออกด้วยตนเองและทำความสะอาดทุกประเภท

การใช้อาร์เรย์บนสแต็กก็ไม่ได้รับการสนับสนุนเนื่องจากคุณไม่มีการตรวจสอบช่วงและการผ่านอาร์เรย์ไปรอบ ๆ จะสูญเสียข้อมูลใด ๆ เกี่ยวกับขนาดของมัน (การแปลงอาร์เรย์เป็นตัวชี้) คุณควรใช้boost::arrayในกรณีนั้นซึ่งจะล้อมอาร์เรย์ C ++ ในคลาสขนาดเล็กและจัดเตรียมsizeฟังก์ชันและตัววนซ้ำเพื่อวนซ้ำ

ตอนนี้std :: vector เทียบกับอาร์เรย์ C ++ ดั้งเดิม (นำมาจากอินเทอร์เน็ต):

// Comparison of assembly code generated for basic indexing, dereferencing, 
// and increment operations on vectors and arrays/pointers.

// Assembly code was generated by gcc 4.1.0 invoked with  g++ -O3 -S  on a 
// x86_64-suse-linux machine.

#include <vector>

struct S
{
  int padding;

  std::vector<int> v;
  int * p;
  std::vector<int>::iterator i;
};

int pointer_index (S & s) { return s.p[3]; }
  // movq    32(%rdi), %rax
  // movl    12(%rax), %eax
  // ret

int vector_index (S & s) { return s.v[3]; }
  // movq    8(%rdi), %rax
  // movl    12(%rax), %eax
  // ret

// Conclusion: Indexing a vector is the same damn thing as indexing a pointer.

int pointer_deref (S & s) { return *s.p; }
  // movq    32(%rdi), %rax
  // movl    (%rax), %eax
  // ret

int iterator_deref (S & s) { return *s.i; }
  // movq    40(%rdi), %rax
  // movl    (%rax), %eax
  // ret

// Conclusion: Dereferencing a vector iterator is the same damn thing 
// as dereferencing a pointer.

void pointer_increment (S & s) { ++s.p; }
  // addq    $4, 32(%rdi)
  // ret

void iterator_increment (S & s) { ++s.i; }
  // addq    $4, 40(%rdi)
  // ret

// Conclusion: Incrementing a vector iterator is the same damn thing as 
// incrementing a pointer.

หมายเหตุ: หากคุณจัดสรรอาร์เรย์ด้วยnewและจัดสรรวัตถุที่ไม่ใช่คลาส (เช่นธรรมดาint) หรือคลาสที่ไม่มีตัวกำหนดที่ผู้ใช้กำหนดและคุณไม่ต้องการให้องค์ประกอบเริ่มต้นในตอนแรกการใช้newอาร์เรย์ที่จัดสรรแล้วจะมีข้อดีด้านประสิทธิภาพเนื่องจากstd::vectorเริ่มองค์ประกอบทั้งหมด ค่าเริ่มต้น (0 สำหรับ int ตัวอย่างเช่น) ในการก่อสร้าง (เครดิตถึง @bernie สำหรับการเตือนฉัน)


77
ใครเป็นผู้คิดค้นไวยากรณ์ AT&T เจ้ากรรม? เฉพาะในกรณีที่ฉันรู้ ... :)
Mehrdad Afshari

4
สิ่งนี้ไม่เป็นจริงสำหรับคอมไพเลอร์ Visual C ++ แต่สำหรับ GCC มันเป็น
toto

5
ประเด็นในคำตอบของฉันคือเวกเตอร์ไม่จำเป็นต้องช้ากว่าการใช้ตัวชี้การแก้ไข แน่นอนมันสามารถ (ง่ายต่อการบรรลุโดยเปิดใช้งานโหมด debug ด้วย) :)
23490 Johannes Schaub - litb

18
+1 สำหรับ"การทำดัชนีเวกเตอร์เป็นสิ่งเดียวกับการจัดทำดัชนีตัวชี้" และสำหรับข้อสรุปอื่น ๆ เช่นกัน
นาวาซ

3
@ Piotr99 ฉันจะไม่เถียงกับคุณ แต่เมื่อคุณเรียนรู้การชุมนุมหลังจากเรียนภาษาระดับสูงกว่าไวยากรณ์ของ Intel จะมีความรู้สึกมากกว่าย้อนหลังบางส่วนนำหน้า (ตัวเลข) เติมท้าย (คำแนะนำ) และปิดบัง (เข้าถึงหน่วยความจำ) ) ลักษณะของไวยากรณ์ AT&T
โคลจอห์นสัน

73

คำนำสำหรับคนที่ใช้เครื่องมือเพิ่มประสิทธิภาพไมโคร

โปรดจำไว้ว่า:

"โปรแกรมเมอร์เสียเวลาจำนวนมากที่คิดหรือกังวลเกี่ยวกับความเร็วของส่วนที่ไม่สำคัญของโปรแกรมและความพยายามเหล่านี้อย่างมีประสิทธิภาพมีผลกระทบเชิงลบอย่างมากเมื่อพิจารณาการดีบั๊กและการบำรุงรักษาเราควรลืมประสิทธิภาพเล็กน้อย 97% ของเวลา: การปรับให้เหมาะสมก่อนกำหนดเป็นรากฐานของความชั่วร้ายทั้งหมดแต่เราไม่ควรปล่อยให้โอกาสของเราผ่านไปในช่วงวิกฤตที่ 3% "

(ขอบคุณการเปลี่ยนแปลงสำหรับใบเสนอราคาเต็ม)

อย่าใช้อาร์เรย์ C แทนเวกเตอร์ (หรืออะไรก็ตาม) เพียงเพราะคุณเชื่อว่ามันเร็วกว่าที่ควรจะเป็นระดับที่ต่ำกว่า คุณจะผิด

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

สิ่งนี้กล่าวว่าเราสามารถกลับไปที่คำถามเดิม

อาร์เรย์แบบคงที่ / ไดนามิก?

คลาสอาเรย์ C ++ นั้นมีพฤติกรรมที่ดีกว่าอาเรย์ C ระดับต่ำเพราะพวกเขารู้มากเกี่ยวกับตัวเองและสามารถตอบคำถามที่อาร์เรย์ C ไม่สามารถทำได้ พวกเขาสามารถทำความสะอาดหลังจากที่ตัวเอง และที่สำคัญพวกเขามักจะเขียนโดยใช้เทมเพลตและ / หรือการทำอินไลน์ซึ่งหมายความว่าสิ่งที่ปรากฏในโค้ดจำนวนมากในการดีบั๊กจะแก้ไขเป็นโค้ดเพียงเล็กน้อยหรือไม่มีเลยที่สร้างขึ้นในรีลีสการสร้าง

สรุปแล้วมันมีสองประเภท:

อาร์เรย์แบบไดนามิก

การใช้ตัวชี้ไปยังอาร์เรย์ malloc-ed / new-ed จะเร็วที่สุดเท่ารุ่น std :: vector และปลอดภัยน้อยกว่ามาก (ดูบทความของ litb )

ดังนั้นใช้ std :: vector

อาร์เรย์แบบคงที่

การใช้อาร์เรย์คงที่จะดีที่สุด:

ดังนั้นใช้มาตรฐาน :: อาร์เรย์

หน่วยความจำเริ่มต้น

บางครั้งใช้vectorแทนของบัฟเฟอร์ดิบเกิดค่าใช้จ่ายที่สามารถมองเห็นได้เพราะvectorจะเริ่มต้นบัฟเฟอร์ที่ก่อสร้างในขณะที่รหัสแทนที่ไม่ได้เป็นข้อสังเกตBernieโดยในของเขาคำตอบ

หากเป็นกรณีนี้คุณสามารถจัดการได้โดยใช้ a unique_ptrแทนvectorหรือถ้ากรณีไม่ได้อยู่ใน codeline ของคุณให้เขียนคลาสbuffer_ownerที่จะเป็นเจ้าของหน่วยความจำนั้นและให้คุณเข้าถึงได้ง่ายและปลอดภัยรวมถึง โบนัสเช่นการปรับขนาด (โดยใช้realloc?) หรืออะไรก็ตามที่คุณต้องการ


1
ขอบคุณสำหรับการจัดการกับอาร์เรย์คงที่เช่นกัน - std :: vector นั้นไร้ประโยชน์หากคุณไม่ได้รับอนุญาตให้จัดสรรหน่วยความจำแบบไดนามิกด้วยเหตุผลด้านประสิทธิภาพ
Tom

10
เมื่อคุณพูดว่า "การใช้อาเรย์แบบคงที่จะเร็วที่สุดเท่าที่บูสต์เพิ่ม :: รุ่นอาเรย์" มันแสดงให้เห็นว่าคุณมีอคติอย่างไร มันควรจะเป็นสิ่งที่อยู่รอบ ๆ Boost: อาเรย์สามารถเร็วที่สุดเช่นอาเรย์แบบคงที่
toto

3
@toto: มันเป็นความเข้าใจผิด: คุณควรอ่านมันว่า "การใช้อาร์เรย์คงที่จะดีที่สุด ((เร็วที่สุดเท่าที่บูสต์เพิ่ม :: รุ่นอาร์เรย์) && (ปลอดภัยน้อยกว่ามาก)) ฉันจะแก้ไขโพสต์เพื่อชี้แจงเรื่องนี้ โดยวิธีการขอบคุณสำหรับประโยชน์ของข้อสงสัย
paercebal

1
สิ่งที่เกี่ยวกับ std :: array?
paulm

4
แสดงคำพูดแบบเต็มเสมอ "โปรแกรมเมอร์เสียเวลาจำนวนมากที่คิดหรือกังวลเกี่ยวกับความเร็วของส่วนที่ไม่สำคัญของโปรแกรมและความพยายามเหล่านี้อย่างมีประสิทธิภาพมีผลกระทบเชิงลบอย่างมากเมื่อพิจารณาการดีบั๊กและการบำรุงรักษาเราควรลืมประสิทธิภาพเล็กน้อย 97% ของเวลา: การปรับให้เหมาะสมก่อนกำหนดเป็นรากฐานของความชั่วร้ายทั้งหมด แต่เราไม่ควรปล่อยให้โอกาสของเราผ่านไปในช่วงวิกฤตที่ 3% " มิฉะนั้นมันจะกลายเป็นเสียงกัดที่ไร้ความหมาย
เปลี่ยนแปลง

32

เวกเตอร์เป็นอาร์เรย์ภายใต้ประทุน ประสิทธิภาพการทำงานเหมือนกัน

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

เมื่อเวกเตอร์เติมมันจะปรับขนาดตัวเองและนั่นอาจหมายถึงการจัดสรรอาร์เรย์ใหม่ตามด้วยตัวสร้างสำเนา n ตามด้วยการเรียก n destructor ประมาณ n ตามด้วยการลบอาร์เรย์

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

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


4
Pendantry: การแสดงมีขนาดใหญ่ O. std :: vector ที่เหมือนกันกับการทำบัญชีเล็กน้อยซึ่งน่าจะเสียเวลาเล็กน้อย OTOH คุณจะทำบัญชีมากเหมือนกันเมื่อกลิ้งอาร์เรย์แบบไดนามิกของคุณเอง
dmckee --- ผู้ดูแลอดีตลูกแมว

ใช่ฉันเข้าใจแล้ว. แม้ว่าแรงผลักดันของคำถามของเขาคืออะไรคือความแตกต่างของการแสดง ..... ฉันพยายามพูดถึงเรื่องนั้น
EvilTeach

std :: vector ของ Gcc เพิ่มความจุแบบหนึ่งต่อหนึ่งถ้าคุณเรียก push_back
bjhend

3
@bjhend แล้ว gcc std::vectorฟังดูไม่ได้มาตรฐานหรือเปล่า? ฉันเชื่อว่ามาตรฐานกำหนดให้ต้องvector::push_backมีการตัดจำหน่ายความซับซ้อนคงที่และการเพิ่มความจุ 1 โดยแต่ละรายการpush_backจะเป็นความซับซ้อน n ^ 2 หลังจากที่คุณทำการ reallocs - สันนิษฐานว่าการเพิ่มความจุแบบเอ็กซ์โปเนนเชียลบางอย่างเกิดขึ้นpush_backและinsertความล้มเหลวที่reserveจะนำไปสู่การเพิ่มขึ้นอย่างต่อเนื่องของปัจจัยในการคัดลอกเนื้อหาเวกเตอร์ ปัจจัยการเจริญเติบโต 1.5 ชี้แจงเวกเตอร์จะหมายถึง ~ 3x reserve()เป็นหลายสำเนาถ้าคุณล้มเหลวในการ
Yakk - Adam Nevraumont

3
@bjhend คุณผิด มาตรฐานห้ามการเติบโตแบบเอ็กซ์โปเนนเชียล: § 23.2.3 ย่อหน้าที่ 16 บอกว่า "ตารางที่ 101 แสดงรายการการดำเนินงานที่จัดเตรียมไว้สำหรับคอนเทนเนอร์แบบลำดับบางประเภท จะดำเนินการเพื่อที่จะใช้เวลาคงที่ตัดจำหน่าย " (ตาราง 101 เป็นรายการที่มี push_back อยู่ในนั้น) ตอนนี้กรุณาหยุดแพร่กระจาย FUD ไม่มีการใช้งานหลักที่ละเมิดข้อกำหนดนี้ ไลบรารี C ++ มาตรฐานของ Microsoft เติบโตขึ้นด้วยปัจจัย 1.5 เท่าและ GCC เติบโตขึ้นด้วยปัจจัย 2x
R. Martinho Fernandes

27

เพื่อตอบสนองต่อสิ่งที่Mehrdadกล่าวว่า:

อย่างไรก็ตามอาจมีบางกรณีที่คุณยังต้องการอาร์เรย์ เมื่อเชื่อมต่อกับรหัสระดับต่ำ (เช่นชุดประกอบ) หรือไลบรารีเก่าที่ต้องใช้อาร์เรย์คุณอาจไม่สามารถใช้เวกเตอร์ได้

ไม่จริงเลย เวกเตอร์ย่อยสลายอย่างเป็นอาร์เรย์ / พอยน์เตอร์ถ้าคุณใช้:

vector<double> vector;
vector.push_back(42);

double *array = &(*vector.begin());

// pass the array to whatever low-level code you have

สิ่งนี้ใช้ได้กับการใช้งาน STL ที่สำคัญทั้งหมด ในมาตรฐานถัดไปมันจะต้องทำงาน (แม้ว่ามันจะไม่เป็นไรวันนี้)


1
มาตรฐานปัจจุบันบอกว่าไม่มีสิ่งนั้น มันมีความหมายและมันจะถูกนำมาใช้เป็นที่เก็บข้อมูลอย่างต่อเนื่อง แต่มาตรฐานเพียงบอกว่ามันเป็นภาชนะเข้าถึงแบบสุ่ม (ใช้ตัววนซ้ำ) มาตรฐานต่อไปจะมีความชัดเจน
Frank Krueger

1
& * v.begin () ใช้กับ & โอเปอเรเตอร์กับผลลัพธ์ของการอ้างถึงตัววนซ้ำเท่านั้น การอ้างถึงสามารถส่งคืนชนิดใดก็ได้ การใช้ที่อยู่ของผู้ประกอบการสามารถกลับมาประเภทใดก็ได้ มาตรฐานไม่ได้กำหนดสิ่งนี้เป็นตัวชี้ในพื้นที่ต่อเนื่องของหน่วยความจำ
Frank Krueger

15
ข้อความดั้งเดิมของมาตรฐานปี 1998 ไม่ได้ต้องการ แต่มีภาคผนวกในปี 2003 ที่กล่าวถึงสิ่งนี้ดังนั้นจึงครอบคลุมโดยมาตรฐานจริงๆ herbalutter.wordpress.com/2008/04/07/…
Nemanja Trifunovic

2
C ++ 03 กล่าวอย่างชัดเจนว่า&v[n] == &v[0] + nถูกต้องให้nอยู่ในช่วงขนาด ย่อหน้าที่มีคำสั่งนี้ไม่ได้เปลี่ยนแปลงด้วย C ++ 11
bjhend

2
ทำไมไม่ใช้ std :: vector :: data ()?
paulm

15

คุณมีเหตุผลน้อยลงที่จะใช้อาร์เรย์ธรรมดาใน C ++ 11

มีอาร์เรย์ 3 แบบในธรรมชาติตั้งแต่เร็วที่สุดไปจนถึงช้าที่สุดขึ้นอยู่กับคุณสมบัติที่มี (แน่นอนว่าคุณภาพของการติดตั้งสามารถทำให้สิ่งต่าง ๆ รวดเร็วยิ่งขึ้นสำหรับกรณีที่ 3 ในรายการ):

  1. คงที่กับขนาดที่รู้จักกันในเวลารวบรวม ---std::array<T, N>
  2. แบบไดนามิกที่มีขนาดรู้จักกันในรันไทม์และไม่เคยปรับขนาด การเพิ่มประสิทธิภาพทั่วไปที่นี่คือว่าถ้าอาร์เรย์สามารถจัดสรรในสแต็คโดยตรง - ไม่สามารถใช้ได้ อาจdynarrayอยู่ใน C ++ TS หลังจาก C ++ 14 ใน C มี VLA
  3. แบบไดนามิกและปรับขนาดได้ที่รันไทม์ ---std::vector<T>

สำหรับ1.อาร์เรย์คงที่ธรรมดาที่มีจำนวนองค์ประกอบคงที่ให้ใช้std::array<T, N>ใน C ++ 11

สำหรับ2อาร์เรย์ขนาดคงที่ที่ระบุที่รันไทม์ แต่นั่นจะไม่เปลี่ยนขนาดของมันมีการสนทนาใน C ++ 14 แต่มันถูกย้ายไปยังข้อกำหนดทางเทคนิคและทำจาก C ++ 14 ในที่สุด

สำหรับ3. มักจะถามสำหรับหน่วยความจำในกองstd::vector<T> สิ่งนี้อาจมีผลกระทบด้านประสิทธิภาพแม้ว่าคุณสามารถใช้std::vector<T, MyAlloc<T>>เพื่อปรับปรุงสถานการณ์ด้วยตัวจัดสรรที่กำหนดเอง ประโยชน์ที่ได้รับเมื่อเทียบกับT mytype[] = new MyType[n];คือคุณสามารถปรับขนาดได้และจะไม่สลายตัวไปยังตัวชี้เหมือนกับที่อาร์เรย์ธรรมดาทำ

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


2
std :: dynarray. หลังจากตรวจสอบความเห็นร่างกายแห่งชาติถึง n3690 ส่วนประกอบไลบรารีนี้ได้รับการโหวตจากกระดาษทำงาน C ++ 14 เป็นข้อกำหนดทางเทคนิคแยกต่างหาก คอนเทนเนอร์นี้ไม่ได้เป็นส่วนหนึ่งของร่าง C ++ 14 ณ n3797 จากen.cppreference.com/w/cpp/container/dynarray
Mohamed El-Nakib

1
คำตอบที่ดีมาก โดยย่อและสรุป แต่ยังมีรายละเอียดมากกว่าใด ๆ
Mohamed El-Nakib

6

ไปกับ STL ไม่มีโทษประสิทธิภาพ อัลกอริทึมมีประสิทธิภาพมากและทำงานได้ดีในการจัดการรายละเอียดชนิดต่าง ๆ ที่พวกเราส่วนใหญ่จะไม่คิด


5

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


4
เนื่องจากเวกเตอร์นั้นต่อเนื่องกันมันก็ยังค่อนข้างง่ายที่จะเชื่อมต่อกับไลบรารีที่ต้องการอาร์เรย์
Greg Rogers

ใช่ แต่ถ้าคุณต้องการยุ่งกับสิ่งต่าง ๆ ภายในของเวกเตอร์จะมีประโยชน์น้อยกว่าในการใช้เวกเตอร์ อย่างไรก็ตามคำหลักคือ "อาจไม่"
Mehrdad Afshari

3
มีเพียงกรณีเดียวที่ฉันรู้ว่าไม่สามารถใช้เวกเตอร์ได้: หากขนาดเป็น 0 ดังนั้น & a [0] หรือ & * a.begin () จะไม่ทำงาน c ++ 1x จะแก้ไขด้วยการแนะนำ a.data () ฟังก์ชั่นซึ่งจะส่งกลับบัฟเฟอร์ภายในที่เก็บองค์ประกอบ
Johannes Schaub - litb

สถานการณ์เฉพาะในใจของฉันเมื่อฉันเขียนนั่นคืออาร์เรย์ตามกอง
Mehrdad Afshari

1
การเชื่อมต่อเวกเตอร์หรือภาชนะที่ต่อเนื่องใด ๆ กับ C: vec.data()สำหรับข้อมูลและvec.size()ขนาด มันง่ายมาก
Germán Diago

5

เกี่ยวกับผลงานของ Duli

บทสรุปคืออาร์เรย์ของจำนวนเต็มเร็วกว่าเวกเตอร์ของจำนวนเต็ม (5 เท่าในตัวอย่างของฉัน) อย่างไรก็ตามอาร์เรย์และเวกเตอร์มีการจัดเรียงความเร็วเดียวกันสำหรับข้อมูลที่ซับซ้อนมากขึ้น / ไม่จัดชิด


3

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

ในโหมดออพติไมซ์ผมคาดว่า stl vector จะเข้าหาประสิทธิภาพของอาเรย์ นี่คือเนื่องจากวิธีการเวกเตอร์จำนวนมากอยู่ในขณะนี้


นี่เป็นเรื่องสำคัญที่ต้องพูดถึง การดีบักการทำโปรไฟล์ STL นั้นช้ามาก และเป็นหนึ่งในเหตุผลที่ทำให้ทุกคน STL ทำงานช้า
Erik Aronesty

3

มีผลกระทบต่อประสิทธิภาพอย่างแน่นอนในการใช้std::vectorvs a raw array เมื่อคุณต้องการบัฟเฟอร์ที่ไม่ได้กำหนดค่าเริ่มต้น (เช่นใช้เป็นปลายทางสำหรับmemcpy()) std::vectorจะเริ่มต้นทุกองค์ประกอบของการใช้ constructor เริ่มต้น อาร์เรย์ดิบจะไม่

C ++ ข้อมูลจำเพาะสำหรับstd:vectorตัวสร้างการcountโต้แย้ง (มันเป็นรูปแบบที่สาม) ฯ :

`สร้างคอนเทนเนอร์ใหม่จากแหล่งข้อมูลที่หลากหลายโดยเลือกใช้การจัดสรรตัวจัดสรรของผู้ใช้

3) สร้างคอนเทนเนอร์ด้วยอินสแตนซ์ที่นับจำนวนเริ่มต้นของ T ไม่มีการทำสำเนา

ความซับซ้อน

2-3) การนับเชิงเส้น

อาเรย์ดิบไม่ต้องเสียค่าใช้จ่ายในการเริ่มต้นนี้

ดูเพิ่มเติมฉันจะหลีกเลี่ยง std :: vector <> เพื่อเริ่มต้นองค์ประกอบทั้งหมดได้อย่างไร


2

ความแตกต่างของประสิทธิภาพระหว่างทั้งสองนั้นขึ้นอยู่กับการใช้งานอย่างมาก - ถ้าคุณเปรียบเทียบ std :: vector ที่นำไปใช้อย่างไม่เหมาะสมกับการใช้งานอาร์เรย์ที่เหมาะสมอาร์เรย์จะชนะ แต่หมุนไปรอบ ๆ และเวกเตอร์จะชนะ ...

ตราบใดที่คุณเปรียบเทียบแอปเปิ้ลกับแอปเปิ้ล (ทั้งอาร์เรย์และเวกเตอร์มีจำนวนองค์ประกอบคงที่หรือทั้งสองได้รับการปรับขนาดแบบไดนามิก) ฉันจะคิดว่าความแตกต่างของประสิทธิภาพนั้นไม่มีความสำคัญตราบใดที่คุณปฏิบัติตาม อย่าลืมว่าการใช้คอนเทนเนอร์ C ++ มาตรฐานยังช่วยให้คุณสามารถใช้อัลกอริทึมก่อนรีดที่เป็นส่วนหนึ่งของไลบรารี C ++ มาตรฐานและส่วนใหญ่จะมีประสิทธิภาพที่ดีกว่าการใช้งานเฉลี่ยของอัลกอริทึมเดียวกันกับที่คุณสร้างขึ้นเอง .

ที่กล่าวว่า IMHO เวกเตอร์ชนะในสถานการณ์การดีบักด้วย debug STL เนื่องจากการใช้งาน STL ส่วนใหญ่ที่มีโหมดการดีบักที่เหมาะสมอย่างน้อยสามารถเน้น / cathc ข้อผิดพลาดทั่วไปที่ทำโดยคนเมื่อทำงานกับภาชนะมาตรฐาน

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


1
ฉันคิดว่าเพื่อให้เป็นไปตามข้อกำหนดด้านประสิทธิภาพ (O (1) การค้นหาและการแทรก) คุณเกือบต้องใช้ std :: vector <> โดยใช้อาร์เรย์แบบไดนามิก แน่นอนว่านี่เป็นวิธีที่ชัดเจนที่จะทำ
dmckee --- ผู้ดูแลอดีตลูกแมว

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

การใช้เวกเตอร์ที่ไม่ดีตามที่อธิบายไว้จะไม่เป็นไปตามมาตรฐาน ถ้าคุณต้องการอ้อมstd::dequeอาจใช้
Phil1970

1

หากคุณไม่จำเป็นต้องปรับขนาดแบบไดนามิกคุณมีหน่วยความจำโอเวอร์เฮดของการบันทึกความจุ (หนึ่งพอยน์เตอร์ / size_t) แค่นั้นแหละ.


1

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

ฉันประหลาดใจที่ไม่มีใครพูดถึงเรื่องนี้ - ไม่ต้องกังวลเกี่ยวกับประสิทธิภาพจนกว่าจะได้รับการพิสูจน์แล้วว่าเป็นปัญหาและเป็นมาตรฐาน


1

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


1

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


2
หากเรื่องนี้แสดงว่าคุณเห็นได้ชัดว่าคุณไม่ได้ใช้อาร์เรย์ที่มีขนาดแบบไดนามิกและเป็นเช่นนั้นอาร์เรย์ของคุณไม่จำเป็นต้องเปลี่ยนขนาด (ถ้าพวกเขาทำคุณจะต้องเก็บขนาดอย่างใด) ดังนั้นคุณอาจใช้ boost :: array ยกเว้นว่าฉันเข้าใจผิด - และอะไรทำให้คุณพูดว่าต้อง "เก็บขนาด" ไว้ที่ไหนซักแห่ง?
Arafangion

1

การทดสอบอย่างง่ายต่อไปนี้:

คำอธิบายการทดสอบประสิทธิภาพของ C ++ Array vs Vector

ขัดแย้งกับข้อสรุปจาก "การเปรียบเทียบรหัสแอสเซมบลีที่สร้างขึ้นสำหรับการทำดัชนีขั้นพื้นฐานการยกเลิกการลงทะเบียนและการดำเนินการเพิ่มในเวกเตอร์และอาร์เรย์ / พอยน์เตอร์"

จะต้องมีความแตกต่างระหว่างอาร์เรย์และเวกเตอร์ การทดสอบบอกว่า ... แค่ลองใช้รหัสก็มี ...


1

บางครั้งอาร์เรย์ดีกว่าเวกเตอร์ หากคุณจัดการชุดวัตถุที่มีความยาวคงที่อยู่เสมออาร์เรย์จะดีกว่า พิจารณาตัวอย่างโค้ดต่อไปนี้:

int main() {
int v[3];
v[0]=1; v[1]=2;v[2]=3;
int sum;
int starttime=time(NULL);
cout << starttime << endl;
for (int i=0;i<50000;i++)
for (int j=0;j<10000;j++) {
X x(v);
sum+=x.first();
}
int endtime=time(NULL);
cout << endtime << endl;
cout << endtime - starttime << endl;

}

โดยที่ vector version ของ X คือ

class X {
vector<int> vec;
public:
X(const vector<int>& v) {vec = v;}
int first() { return vec[0];}
};

และรุ่นอาร์เรย์ของ X คือ:

class X {
int f[3];

public:
X(int a[]) {f[0]=a[0]; f[1]=a[1];f[2]=a[2];}
int first() { return f[0];}
};

อาเรย์เวอร์ชันจะเป็น main () จะเร็วขึ้นเนื่องจากเราหลีกเลี่ยงค่าใช้จ่าย "ใหม่" ทุกครั้งในลูปด้านใน

(รหัสนี้ถูกโพสต์ไปยัง comp.lang.c ++ โดยฉัน)


1

หากคุณใช้เวกเตอร์เพื่อแสดงพฤติกรรมหลายมิติจะมีการแสดงที่ยอดเยี่ยม

เวกเตอร์ 2 มิติ + เป็นสาเหตุของการทำงานหรือไม่

สรุปสาระสำคัญคือมีค่าใช้จ่ายเล็กน้อยในแต่ละเวกเตอร์ย่อยที่มีข้อมูลขนาดและไม่จำเป็นต้องเป็นอนุกรมของข้อมูล (เช่นเดียวกับที่มีอาร์เรย์ c หลายมิติ) การขาดการทำให้เป็นอันดับอาจทำให้มีโอกาสในการเพิ่มประสิทธิภาพมากขึ้น หากคุณกำลังทำอาร์เรย์หลายมิติคุณควรขยาย std :: vector และหมุนฟังก์ชัน get / set / resize bits ของคุณเอง


0

สมมติว่าอาเรย์ที่มีความยาวคงที่ (เช่นint* v = new int[1000];vs std::vector<int> v(1000);กับขนาดของvการคงที่ไว้ที่ 1,000) การพิจารณาประสิทธิภาพเพียงอย่างเดียวที่สำคัญจริงๆ (หรืออย่างน้อยสำคัญกับฉันเมื่อฉันอยู่ในภาวะที่กลืนไม่เข้าคายไม่ออกที่คล้ายกัน) คือความเร็วในการเข้าถึง ธาตุ. ฉันค้นหารหัสเวกเตอร์ของ STL และนี่คือสิ่งที่ฉันพบ:

const_reference
operator[](size_type __n) const
{ return *(this->_M_impl._M_start + __n); }

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

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