คอนเทนเนอร์ที่มีประสิทธิภาพที่สุดในการจัดเก็บวัตถุเกมแบบไดนามิกคืออะไร [ปิด]


20

ฉันเป็นนักกีฬาคนแรกและฉันรู้เกี่ยวกับคอนเทนเนอร์ประเภทต่างๆมากมาย แต่ฉันต้องการค้นหาคอนเทนเนอร์ที่มีประสิทธิภาพที่สุดสำหรับการจัดเก็บวัตถุไดนามิกที่จะเพิ่มและลบออกจากเกมค่อนข้างบ่อย EX-กระสุน

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

ฉันกำลังเขียนสิ่งนี้ใน c ++ โดยวิธี

ฉันยังคิดวิธีแก้ปัญหาที่ฉันคิดว่าจะใช้ได้

ในการเริ่มต้นฉันจะจัดสรรเวกเตอร์ที่มีขนาดใหญ่ .. พูดได้ 1,000 วัตถุ ฉันจะติดตามดัชนีที่เพิ่มล่าสุดในเวกเตอร์นี้เพื่อให้ฉันรู้ว่าจุดสิ้นสุดของวัตถุอยู่ที่ไหน จากนั้นฉันจะสร้างคิวที่จะเก็บดัชนีของวัตถุทั้งหมดที่ "ลบ" จากเวกเตอร์ (จะไม่มีการลบจริงฉันจะรู้ว่าสล็อตนั้นฟรี) ดังนั้นหากคิวว่างเปล่าฉันจะเพิ่มดัชนีล่าสุดที่เพิ่มเข้าไปในเวกเตอร์ + 1 มิฉะนั้นฉันจะเพิ่มดัชนีของเวกเตอร์ที่อยู่ด้านหน้าของคิว


ภาษาใดที่คุณกำหนดเป้าหมาย
Phill.Zitt

คำถามนี้ยากเกินไปที่จะตอบโดยไม่ต้องเจาะจงมากขึ้นรวมถึงแพลตฟอร์มฮาร์ดแวร์ภาษา / เฟรมเวิร์ก ฯลฯ
PlayDeezGames

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

2
มีคำถามในคำถามนี้หรือไม่?
เทรเวอร์พาวเวลล์

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

คำตอบ:


33

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

คุณควรเก็บวัตถุเหล่านั้น (ไม่ใช่ตัวชี้ไปยังวัตถุเหล่านั้น) ในอาร์เรย์ / เวกเตอร์

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

คุณต้องการหลีกเลี่ยงการจัดสรรหน่วยความจำและการจัดสรรคืน มันช้ามากแม้จะมีตัวจัดสรรหน่วยความจำที่รวดเร็ว ฉันเคยเห็นเกมได้รับ 10x FPS ชนเพียงแค่ลบการจัดสรรหน่วยความจำไม่กี่ร้อยแต่ละเฟรม ดูเหมือนจะไม่เลวร้ายขนาดนั้น แต่ก็เป็นได้

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

ตัวอย่างเช่นในการนำวัตถุเกมออกคุณสามารถใช้ swap-and-pop ดำเนินการได้อย่างง่ายดายด้วยบางสิ่งเช่น:

std::swap(objects[index], objects.back());
objects.pop_back();

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

ที่สำคัญคุณสามารถค้นหาวัตถุด้วยการค้นหาตารางอย่างง่าย ๆ จากความเสถียรที่ไม่เหมือนใครคือการใช้โครงสร้างแผนที่สล็อต

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

แมปสล็อตต้องการเลเยอร์ทางอ้อมสองชั้น แต่ทั้งคู่เป็นการค้นหาอาร์เรย์อย่างง่ายพร้อมดัชนีคงที่ พวกเขามีความรวดเร็ว เร็วจริงๆ.

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

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

เมื่อคุณทำลายวัตถุคุณทำ swap-and-pop ตามปกติ แต่คุณต้องเพิ่มหมายเลขรุ่นด้วย คุณยังเพิ่มดัชนีรายการทางอ้อม (ส่วนหนึ่งของ ID เฉพาะของวัตถุ) ไปยังรายการว่าง เมื่อย้ายวัตถุซึ่งเป็นส่วนหนึ่งของ swap-and-pop คุณจะต้องอัปเดตรายการในรายการการอ้อมไปยังตำแหน่งใหม่

ตัวอย่างรหัสหลอก:

Object:
  int index
  int version
  other data

SlotMap:
  Object objects[]
  int slots[]
  int freelist[]
  int count

  Get(id):
    index = indirection[id.index]
    if objects[index].version = id.version:
      return &objects[index]
    else:
      return null

  CreateObject():
    index = freelist.pop()

    objects[count].index = id
    objects[count].version += 1

    indirection[index] = count

    Object* object = &objects[count].object
    object.initialize()

    count += 1

    return object

  Remove(id):
    index = indirection[id.index]
    if objects[index].version = id.version:
      objects[index].version += 1
      objects[count - 1].version += 1

      swap(objects[index].data, objects[count - 1].data)

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

แท็กเวอร์ชันช่วยให้คุณสามารถเก็บ ID ไปยังวัตถุที่อาจถูกลบ ตัวอย่างเช่นคุณมี ID (10,1) วัตถุที่มีดัชนี 10 ถูกลบ (เช่นสัญลักษณ์แสดงหัวข้อย่อยของคุณกระทบกับวัตถุและถูกทำลาย) วัตถุที่อยู่ในตำแหน่งของหน่วยความจำในรายการวัตถุหลักนั้นมีหมายเลขรุ่นของมันกระแทกให้ (10,2) หากคุณพยายามค้นหา (10,1) อีกครั้งจาก stale ID การค้นหาจะส่งคืนออบเจ็กต์นั้นผ่านดัชนี 10 แต่จะเห็นว่าหมายเลขเวอร์ชันนั้นเปลี่ยนไปดังนั้น ID จะไม่ถูกต้องอีกต่อไป

นี่คือโครงสร้างข้อมูลที่เร็วที่สุดที่คุณสามารถทำได้ด้วยรหัสที่เสถียรซึ่งอนุญาตให้วัตถุย้ายในหน่วยความจำซึ่งเป็นสิ่งสำคัญสำหรับการใช้งานข้อมูลและการเชื่อมโยงแคช นี่เร็วกว่าการใช้งานตารางแฮชที่เป็นไปได้ ตารางแฮชอย่างน้อยที่สุดต้องคำนวณแฮช (คำแนะนำมากกว่าการค้นหาตาราง) จากนั้นจะต้องทำตามเชนแฮช (อาจเป็นรายการที่เชื่อมโยงในกรณีที่น่ากลัวของ std :: unordered_map หรือรายการ open-addressed ใน การใช้ตารางแฮชที่ไม่โง่ใด ๆ และจากนั้นจะต้องทำการเปรียบเทียบค่ากับแต่ละคีย์ (ไม่แพงกว่า แต่เป็นไปได้น้อยกว่าแพงกว่าการตรวจสอบแท็กเวอร์ชัน) ตารางแฮชที่ดีมาก (ไม่ใช่หนึ่งในการนำไปใช้งานของ STL ใด ๆ เนื่องจาก STL สั่งให้ตารางแฮชที่ปรับให้เหมาะกับกรณีการใช้งานที่แตกต่างจากเกมที่คุณเกี่ยวกับรายการวัตถุเกม) อาจบันทึกในทิศทางเดียว

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

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

ขอโทษที่เขียนขึ้น; ฉันไม่รู้สึกว่ามันเป็นคำอธิบายที่ชัดเจนที่สุด มันสายและยากที่จะอธิบายโดยไม่ต้องใช้เวลามากกว่าที่ฉันมีในตัวอย่างโค้ด


1
คุณกำลังปิดการค้าขาย deref พิเศษและมีการปันส่วนสูง / ต้นทุนฟรี (สลับ) ทุกการเข้าถึงสำหรับที่เก็บข้อมูล 'กระชับ' จากประสบการณ์ของฉันกับวิดีโอเกมนั่นเป็นการค้าที่ไม่ดี :) แน่นอนว่า YMMV
Jeff Gates

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

1
จากประสบการณ์ของฉัน: (1) วิดีโอเกมถูกตัดสินจากประสิทธิภาพของเคสที่แย่ที่สุดไม่ใช่ประสิทธิภาพของเคสโดยเฉลี่ย (2) โดยปกติคุณมีการวนซ้ำ 1 ครั้งต่อการรวบรวมต่อเฟรมดังนั้นการบีบอัดเพียง 'ทำให้กรณีที่เลวร้ายที่สุดของคุณไม่บ่อย' (3) คุณมักจะมี allocs / จำนวนมากในเฟรมเดียวค่าใช้จ่ายสูงหมายความว่าคุณจำกัดความสามารถนั้น (4) คุณมี derefs ที่ไม่ จำกัด ต่อเฟรม (ในเกมที่ฉันได้ทำรวมถึง Diablo 3 บ่อยครั้งที่ deref นั้นเป็น op perf cost ที่สูงที่สุดหลังจากการปรับให้เหมาะสมปานกลาง> 5% ของโหลดเซิร์ฟเวอร์) ฉันไม่ได้ตั้งใจที่จะยกเลิกการแก้ปัญหาอื่น ๆ เพียงแค่ชี้ให้เห็นประสบการณ์และเหตุผลของฉัน!
Jeff Gates

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

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

12

อาร์เรย์ขนาดคงที่ (หน่วยความจำเชิงเส้น) ที่
มีรายการฟรีภายใน (O (1) จัดสรร / ฟรีบ่งชี้ที่มีเสถียรภาพ)
พร้อมกับคีย์อ้างอิงที่อ่อนแอ (การใช้ช่องใส่รหัสทำให้คีย์
ใช้งานไม่ได้)

struct DataArray<T>
{
  void Init(int count); // allocs items (max 64k), then Clear()
  void Dispose();       // frees items
  void Clear();         // resets data members, (runs destructors* on outstanding items, *optional)

  T &Alloc();           // alloc (memclear* and/or construct*, *optional) an item from freeList or items[maxUsed++], sets id to (nextKey++ << 16) | index
  void Free(T &);       // puts entry on free list (uses id to store next)

  int GetID(T &);       // accessor to the id part if Item

  T &Get(id)            // return item[id & 0xFFFF]; 
  T *TryToGet(id);      // validates id, then returns item, returns null if invalid.  for cases like AI references and others where 'the thing might have been deleted out from under me'

  bool Next(T *&);      // return next item where id & 0xFFFF0000 != 0 (ie items not on free list)

  struct Item {
    T item;
    int id;             // (key << 16 | index) for alloced entries, (0 | nextFreeIndex) for free list entries
  };

  Item *items;
  int maxSize;          // total size
  int maxUsed;          // highest index ever alloced
  int count;            // num alloced items
  int nextKey;          // [1..2^16] (don't let == 0)
  int freeHead;         // index of first free entry
};

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

ถาม: "ทำไมไม่ใช้อาร์เรย์แบบไดนามิก" ตอบ: อาร์เรย์แบบไดนามิกทำให้เกิดปัญหา ตัวอย่างง่ายๆ:

foreach(Foo *foo in array)
  if (ShouldSpawnBaby(*foo))
    Foo &baby = array.Alloc();
    foo->numBabies++; // crash!

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

และฉันไม่สามารถพูดได้มากพอ: จริง ๆ แล้วนี่คือสิ่งที่ดีที่สุดที่เคยมีมา (ถ้าคุณไม่เห็นด้วยให้โพสต์ทางออกที่ดีกว่าของคุณ! Caveat - ต้องแก้ไขปัญหาที่แสดงไว้ที่ด้านบนของโพสต์นี้: หน่วยความจำเชิงเส้น / การวนซ้ำ, O (1) จัดสรร / ฟรีดัชนีที่มั่นคง มีเหตุผลที่น่าทึ่งว่าทำไมคุณไม่ต้องการหนึ่งในนั้น)


คุณหมายถึงอะไรกับอาร์เรย์แบบไดนามิก ? ฉันถามสิ่งนี้เพราะDataArrayดูเหมือนว่าการจัดสรรอาเรย์แบบไดนามิกใน ctor ดังนั้นมันอาจมีความหมายที่แตกต่างกับความเข้าใจของฉัน
Eonil

ฉันหมายถึงอาร์เรย์ที่ปรับขนาด / memmove ระหว่างการใช้งาน (ตรงข้ามกับการสร้าง) เวกเตอร์ stl เป็นตัวอย่างของสิ่งที่ฉันเรียกอาร์เรย์แบบไดนามิก
Jeff Gates

@JeffGates ชอบคำตอบนี้จริงๆ ยอมรับอย่างเต็มที่อีกครั้งยอมรับกรณีที่เลวร้ายที่สุดเป็นค่าใช้จ่ายกรณีมาตรฐาน การสำรองรายการลิงค์ฟรีโดยใช้อาร์เรย์ที่มีอยู่นั้นสวยงามมาก คำถามที่ 1: วัตถุประสงค์ของการใช้งานสูงสุด Q2: วัตถุประสงค์ของการจัดเก็บดัชนีในบิตต่ำของ id สำหรับรายการที่ปันส่วนคืออะไร ทำไมไม่ 0 Q3: วิธีนี้จัดการรุ่นเอนทิตี หากไม่เป็นเช่นนั้นฉันขอแนะนำให้ใช้บิตที่มีลำดับต่ำของ Q2 สำหรับการนับการสร้าง ushort - ขอบคุณ
วิศวกร

1
A1: แม็กซ์ที่ใช้ช่วยให้คุณสามารถกำหนดซ้ำได้ นอกจากนี้คุณยังตัดจำหน่ายต้นทุนการก่อสร้างใด ๆ A2: 1) คุณมักจะไปจากรายการ -> id 2) มันทำให้เปรียบเทียบราคาถูก / ชัดเจน A3: ฉันไม่แน่ใจว่า 'รุ่น' หมายถึงอะไร ฉันจะตีความสิ่งนี้ว่า 'คุณแยกความแตกต่างรายการที่ 5 ที่จัดสรรในช่อง 7 จากรายการที่ 6 ได้อย่างไร' ที่ 5 และ 6 เป็นรุ่น รูปแบบที่เสนอใช้หนึ่งตัวนับทั่วโลกสำหรับช่องทั้งหมด (เราเริ่มตัวนับนี้ด้วยจำนวนที่แตกต่างกันสำหรับแต่ละอินสแตนซ์ DataArray เพื่อแยกรหัสได้ง่ายขึ้น) ฉันแน่ใจว่าคุณสามารถคำนวณบิตต่อการติดตามไอเท็มได้อีกครั้งสำคัญ
Jeff Gates

1
@JeffGates - ฉันรู้ว่านี่เป็นหัวข้อเก่า แต่ฉันชอบความคิดนี้คุณช่วยให้ข้อมูลบางอย่างเกี่ยวกับผลงานภายในของ void Free (T &) มากกว่า void Free (id) ได้ไหม
TheStatehz

1

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

หากคุณมักจะลบวัตถุและสร้างมันขึ้นมาใหม่ฉันขอแนะนำให้คุณดูว่ามีการนำวัตถุไปใช้อย่างไร

แก้ไข: เหตุใดจึงสับสนกับช่องและอะไรที่ไม่ได้ ทำไมไม่ใช้สแต็คแล้วนำรายการสุดท้ายออกแล้วนำมาใช้ซ้ำ ดังนั้นเมื่อคุณเพิ่มหนึ่งคุณจะทำ ++ เมื่อคุณป๊อปอัพที่คุณทำ - เพื่อติดตามดัชนีสิ้นสุดของคุณ


สแตกง่ายไม่จัดการกรณีที่รายการถูกลบตามลำดับโดยพลการ
Jeff Gates

เพื่อความยุติธรรมเป้าหมายของเขาไม่ชัดเจนอย่างแน่นอน อย่างน้อยก็ไม่ใช่สำหรับฉัน
Sidar

1

มันขึ้นอยู่กับเกมของคุณ ภาชนะบรรจุแตกต่างกันว่าการเข้าถึงองค์ประกอบเฉพาะนั้นรวดเร็วเพียงใดการลบองค์ประกอบออกไปอย่างรวดเร็วเพียงใดและการเพิ่มองค์ประกอบนั้นเร็วเพียงใด


  • std :: vector - การเข้าถึงและการเอาออกและเพิ่มไปยังจุดสิ้นสุดอย่างรวดเร็วนั้นรวดเร็ว การนำออกจากจุดเริ่มต้นและตรงกลางช้า
  • std :: list - การวนซ้ำในรายการนั้นไม่ช้ากว่าเวกเตอร์มากนัก แต่การเข้าถึงจุดเฉพาะของรายการนั้นช้า (เพราะการวนซ้ำนั้นเป็นสิ่งเดียวที่คุณสามารถทำได้กับรายการ) การเพิ่มและลบรายการต่าง ๆ ได้อย่างรวดเร็ว ค่าใช้จ่ายหน่วยความจำส่วนใหญ่ แบบไม่ต่อเนื่อง
  • std :: deque - การเข้าถึงและการเอาออก / เพิ่มไปยังจุดสิ้นสุดและจุดเริ่มต้นอย่างรวดเร็วทำได้อย่างรวดเร็ว แต่อยู่ตรงกลาง

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

หากคุณมีเอนทิตี้มากมายจริง ๆ คุณควรดู Space Partitioning


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