ชนิดของ echoing ข้อเสนอแนะของ Kylotan แต่ฉันอยากจะแนะนำให้แก้ปัญหานี้ในระดับโครงสร้างข้อมูลเมื่อเป็นไปได้ไม่ใช่ระดับตัวจัดสรรที่ต่ำกว่าหากคุณสามารถช่วยได้
นี่คือตัวอย่างง่ายๆของวิธีที่คุณสามารถหลีกเลี่ยงการจัดสรรและปล่อยFoos
ซ้ำ ๆ โดยใช้อาร์เรย์ที่มีรูพร้อมองค์ประกอบที่เชื่อมโยงเข้าด้วยกัน (การแก้ปัญหานี้ในระดับ "คอนเทนเนอร์" แทนที่จะเป็นระดับ "ตัวจัดสรร"):
struct FooNode
{
explicit FooNode(const Foo& ielement): element(ielement), next(-1) {}
// Stores a 'Foo'.
Foo element;
// Points to the next foo available; either the
// next used foo or the next deleted foo. Can
// use SoA and hoist this out if Foo doesn't
// have 32-bit alignment.
int next;
};
struct Foos
{
// Stores all the Foo nodes.
vector<FooNode> nodes;
// Points to the first used node.
int first_node;
// Points to the first free node.
int free_node;
Foos(): first_node(-1), free_node(-1)
{
}
const FooNode& operator[](int n) const
{
return data[n];
}
void insert(const Foo& element)
{
int index = free_node;
if (index != -1)
{
// If there's a free node available,
// pop it from the free list, overwrite it,
// and push it to the used list.
free_node = data[index].next;
data[index].next = first_node;
data[index].element = element;
first_node = index;
}
else
{
// If there's no free node available, add a
// new node and push it to the used list.
FooNode new_node(element);
new_node.next = first_node;
first_node = data.size() - 1;
data.push_back(new_node);
}
}
void erase(int n)
{
// If the node being removed is the first used
// node, pop it from the used list.
if (first_node == n)
first_node = data[n].next;
// Push the node to the free list.
data[n].next = free_node;
free_node = n;
}
};
บางสิ่งที่มีผลกระทบนี้: รายการดัชนีที่เชื่อมโยงเดี่ยวพร้อมรายการฟรี ลิงก์ดัชนีช่วยให้คุณสามารถข้ามองค์ประกอบที่ถูกลบลบองค์ประกอบในเวลาคงที่และยังเรียกคืน / นำมาใช้ซ้ำ / เขียนทับองค์ประกอบฟรีพร้อมการแทรกเวลาคงที่ ในการทำซ้ำโครงสร้างคุณทำสิ่งนี้:
for (int index = foos.first_node; index != -1; index = foos[index].next)
// do something with foos[index]
และคุณสามารถวางโครงสร้างข้อมูลแบบ "ลิงก์อาร์เรย์ของหลุม" ข้างต้นโดยใช้เทมเพลตการวางตำแหน่งใหม่และการเรียกใช้ dtor แบบแมนนวลเพื่อหลีกเลี่ยงข้อกำหนดสำหรับการมอบหมายการคัดลอกทำให้มันเรียกใช้ destructors เมื่อองค์ประกอบถูกเอาออก เลือกที่จะรักษาตัวอย่าง C-like ให้ชัดเจนยิ่งขึ้นเพื่อแสดงแนวคิดและเพราะฉันขี้เกียจมาก
ที่กล่าวว่าโครงสร้างนี้มีแนวโน้มที่จะลดลงในพื้นที่เชิงพื้นที่หลังจากที่คุณลบและแทรกสิ่งที่มากไป / จากตรงกลาง เมื่อถึงจุดนั้นnext
ลิงก์อาจทำให้คุณเดินไปมาตามเวกเตอร์ได้การโหลดข้อมูลที่เคยถูกขับไล่ออกจากแคชในการเดินทางตามลำดับเดียวกัน (นี่เป็นสิ่งที่หลีกเลี่ยงไม่ได้กับโครงสร้างข้อมูลหรือตัวจัดสรรที่อนุญาตให้มีการลบเวลาคงที่โดยไม่ต้องสับองค์ประกอบ ช่องว่างจากตรงกลางที่มีการแทรกเวลาคงที่และไม่ต้องใช้อะไรเช่นชุดบิตขนานหรือremoved
ธง) ในการกู้คืนแคชที่เป็นมิตรคุณสามารถใช้วิธีคัดลอก ctor และสลับเช่นนี้:
Foos(const Foos& other)
{
for (int index = other.first_node; index != -1; index = other[index].next)
insert(foos[index].element);
}
void Foos::swap(Foos& other)
{
nodes.swap(other.nodes):
std::swap(first_node, other.first_node);
std::swap(free_node, other.free_node);
}
// ... then just copy and swap:
Foos(foos).swap(foos);
ตอนนี้เวอร์ชันใหม่ใช้งานแคชได้อีกครั้งเพื่อทำการสำรวจ อีกวิธีคือจัดเก็บรายการดัชนีแยกต่างหากลงในโครงสร้างและจัดเรียงดัชนีเป็นระยะ อีกประการหนึ่งคือการใช้บิตเซ็ตเพื่อระบุว่าจะใช้ดัชนีใด ซึ่งจะทำให้คุณผ่านบิตเซ็ตตามลำดับ (เพื่อทำสิ่งนี้ได้อย่างมีประสิทธิภาพตรวจสอบ 64- บิตในแต่ละครั้งเช่นใช้ FFS / FFZ) บิตเซ็ตนั้นมีประสิทธิภาพมากที่สุดและไม่รบกวนซึ่งต้องการเพียงบิตบิตต่อองค์ประกอบเพื่อระบุว่ามีการใช้งานใดและจะถูกลบออกแทนที่จะใช้next
ดัชนี32- บิตแต่ใช้เวลามากในการเขียนดี จะเร็วสำหรับการสำรวจเส้นทางถ้าคุณกำลังตรวจสอบทีละบิต - คุณต้อง FFS / FFZ เพื่อค้นหาบิตที่ตั้งค่าหรือไม่ตั้งค่าทันทีในหมู่ 32+ บิตในแต่ละครั้งเพื่อกำหนดช่วงของดัชนีที่ถูกครอบครองอย่างรวดเร็ว)
โซลูชันที่เชื่อมโยงนี้โดยทั่วไปแล้วเป็นวิธีที่ง่ายที่สุดในการติดตั้งและไม่ล่วงล้ำ (ไม่จำเป็นต้องแก้ไขFoo
เพื่อเก็บremoved
ค่าสถานะบางอย่าง) ซึ่งเป็นประโยชน์ถ้าคุณต้องการทำให้คอนเทนเนอร์นี้ทำงานกับชนิดข้อมูลใด ๆ หากคุณไม่สนใจว่า 32 บิต ค่าใช้จ่ายต่อองค์ประกอบ
ฉันควรสร้างพูลหน่วยความจำสำหรับการจัดสรรแบบไดนามิกหรือไม่จำเป็นต้องกังวลกับสิ่งนี้? เกิดอะไรขึ้นถ้าแพลตฟอร์มเป้าหมายเป็นอุปกรณ์เคลื่อนที่
จำเป็นต้องมีคำที่แข็งแกร่งและฉันลำเอียงทำงานในพื้นที่ที่มีประสิทธิภาพที่สำคัญมากเช่น raytracing การประมวลผลภาพการจำลองอนุภาคและการประมวลผลตาข่าย แต่มันค่อนข้างแพงมากที่จะจัดสรรและปล่อยวัตถุเล็ก ๆ ที่ใช้สำหรับการประมวลผลที่เบามากเช่นกระสุน และอนุภาคทีละตัวเทียบกับตัวจัดสรรหน่วยความจำอเนกประสงค์ขนาดวัตถุประสงค์ ระบุว่าคุณควรจะสามารถสรุปโครงสร้างข้อมูลข้างต้นในหนึ่งหรือสองวันเพื่อเก็บสิ่งที่คุณต้องการฉันคิดว่ามันเป็นการแลกเปลี่ยนที่คุ้มค่าที่จะกำจัดค่าใช้จ่ายในการจัดสรรฮีป / การจัดสรรคืนทันทีจากการจ่ายสำหรับทุกสิ่งเล็ก ๆ ด้านบนของการลดต้นทุนการจัดสรร / การจัดสรรคืนคุณจะได้รับตำแหน่งที่ดีขึ้นของการอ้างอิงภายในผลลัพธ์
เป็นสิ่งที่จอชกล่าวถึงเกี่ยวกับ GC ผมยังไม่ได้ศึกษา C # 's การดำเนินงาน GC ค่อนข้างเป็นอย่างใกล้ชิดเป็น Java แต่ allocators GC มักจะมีการจัดสรรเบื้องต้นมันเร็วมากเพราะใช้ตัวจัดสรรแบบลำดับซึ่งไม่สามารถเพิ่มหน่วยความจำจากตรงกลางได้ (เกือบเหมือนกับสแต็กคุณไม่สามารถลบสิ่งที่อยู่ตรงกลางได้) จากนั้นจ่ายสำหรับค่าใช้จ่ายแพงจริง ๆ แล้วอนุญาตให้ลบวัตถุแต่ละรายการในเธรดแยกต่างหากโดยการคัดลอกหน่วยความจำและล้างหน่วยความจำที่จัดสรรไว้ก่อนหน้าทั้งหมด (เช่นทำลายทั้งกองในคราวเดียวในขณะที่คัดลอกข้อมูลไปยังโครงสร้างที่เชื่อมโยง) แต่เนื่องจากมันทำในเธรดแยกต่างหากจึงไม่จำเป็นต้องทำให้เธรดแอปพลิเคชันของคุณค้างอยู่บ่อยนัก อย่างไรก็ตามนั่นมีค่าใช้จ่ายแอบแฝงที่สำคัญมากของระดับทางอ้อมเพิ่มเติมและการสูญเสียทั่วไปของ LOR หลังจากรอบ GC เริ่มต้น เป็นอีกกลยุทธ์หนึ่งที่เร่งการจัดสรรให้เร็วขึ้น - ทำให้ราคาถูกลงในการเรียกเธรด เพื่อที่คุณจะต้องมีการอ้อมสองระดับเพื่ออ้างอิงวัตถุของคุณแทนที่จะเป็นหนึ่งเนื่องจากพวกเขาจะได้รับการสับในหน่วยความจำระหว่างเวลาที่คุณจัดสรรครั้งแรกและหลังจากรอบแรก
กลยุทธ์อื่นในหลอดเลือดดำที่คล้ายกันซึ่งง่ายกว่าที่จะใช้ใน C ++ เพียงแค่ไม่ต้องกังวลกับการปล่อยวัตถุของคุณในเธรดหลักของคุณ เพียงแค่เพิ่มและเพิ่มและเพิ่มไปยังจุดสิ้นสุดของโครงสร้างข้อมูลซึ่งไม่อนุญาตให้นำสิ่งต่าง ๆ ออกจากกลาง อย่างไรก็ตามทำเครื่องหมายสิ่งเหล่านั้นที่จำเป็นต้องลบออก จากนั้นเธรดที่แยกจากกันสามารถดูแลงานที่มีราคาแพงในการสร้างโครงสร้างข้อมูลใหม่โดยไม่ต้องใช้องค์ประกอบที่ถูกลบออกและจากนั้นแลกเปลี่ยนอะตอมมิกใหม่กับอะตอมเก่าเช่นอะตอมของต้นทุนการจัดสรรและการปล่อยองค์ประกอบจำนวนมากสามารถส่งผ่านไปยัง แยกเธรดถ้าคุณสามารถทำการสันนิษฐานได้ว่าการขอลบองค์ประกอบนั้นไม่จำเป็นต้องได้รับความพึงพอใจทันที ที่ไม่เพียง แต่ทำให้การถูกกว่าเท่าที่หัวข้อของคุณมีความกังวล แต่ทำให้การจัดสรรถูกกว่า เนื่องจากคุณสามารถใช้โครงสร้างข้อมูลที่เรียบง่ายกว่าและไม่น่าไว้วางใจซึ่งไม่ต้องจัดการกับกรณีการลบจากกลาง มันเหมือนภาชนะที่ต้องการเพียงpush_back
ฟังก์ชั่นสำหรับการแทรก, clear
ฟังก์ชั่นเพื่อลบองค์ประกอบทั้งหมด, และswap
เพื่อแลกเปลี่ยนเนื้อหากับภาชนะขนาดกะทัดรัดใหม่ไม่รวมองค์ประกอบที่ถูกลบออก; มันเท่าที่กลายพันธุ์ไป