mmap () เทียบกับการอ่านบล็อค


185

ฉันกำลังทำงานกับโปรแกรมที่กำลังประมวลผลไฟล์ที่อาจมีขนาด 100GB ขึ้นไป ไฟล์มีชุดของบันทึกความยาวแปรผัน ฉันได้ติดตั้งและใช้งานเป็นครั้งแรกและตอนนี้ฉันกำลังมองไปที่การปรับปรุงประสิทธิภาพโดยเฉพาะอย่างยิ่งในการทำ I / O ที่มีประสิทธิภาพมากขึ้นเนื่องจากไฟล์อินพุตได้รับการสแกนหลายครั้ง

มีกฎง่ายๆในการใช้mmap()กับการอ่านในบล็อกผ่านfstreamห้องสมุดของ C ++ ? สิ่งที่ฉันต้องการจะทำคืออ่านบล็อกขนาดใหญ่จากดิสก์ไปยังบัฟเฟอร์ประมวลผลระเบียนที่สมบูรณ์จากบัฟเฟอร์แล้วอ่านเพิ่มเติม

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

ฉันจะตัดสินใจเลือกระหว่างสองตัวเลือกนี้ได้อย่างไร กฎของหัวแม่มือใด ๆ (เช่นmmap()เร็วกว่า 2x) หรือการทดสอบอย่างง่าย?


1
นี้เป็นที่น่าสนใจอ่าน: medium.com/@sasha_f/...ในการทดลองmmap()คือ 2-6 ครั้งเร็วกว่าการใช้ syscalls read()เช่น
mplattner

คำตอบ:


208

ฉันพยายามค้นหาคำสุดท้ายใน mmap / อ่านประสิทธิภาพบน Linux และฉันเจอโพสต์ที่ดี ( ลิงค์ ) ในรายชื่อผู้รับจดหมาย Linux kernel มันมาจากปี 2000 ดังนั้นจึงมีการปรับปรุง IO และหน่วยความจำเสมือนในเคอร์เนลตั้งแต่นั้นมา แต่ก็อธิบายได้อย่างชัดเจนว่าทำไมmmapหรือreadอาจจะเร็วกว่าหรือช้ากว่า

  • การเรียกใช้mmapมีค่าใช้จ่ายมากกว่าread(เช่นเดียวกับepollมีค่าใช้จ่ายมากกว่าpollซึ่งมีค่าใช้จ่ายมากกว่าread) การเปลี่ยนการแม็พหน่วยความจำเสมือนเป็นการดำเนินการที่ค่อนข้างแพงในโปรเซสเซอร์บางตัวด้วยเหตุผลเดียวกันกับที่การสลับระหว่างกระบวนการที่แตกต่างนั้นมีราคาแพง
  • ระบบ IO สามารถใช้แคชดิสก์ได้อยู่แล้วดังนั้นหากคุณอ่านไฟล์คุณจะกดแคชหรือพลาดไม่ว่าคุณจะใช้วิธีใด

อย่างไรก็ตาม

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

การสนทนาของ mmap / read ทำให้ฉันนึกถึงการอภิปรายเกี่ยวกับประสิทธิภาพสองอย่าง:

  • โปรแกรมเมอร์ Java บางคนตกใจที่พบว่าการปิดกั้น I / O มักจะช้ากว่าการปิดกั้น I / O ซึ่งทำให้รู้สึกที่สมบูรณ์แบบถ้าคุณรู้ว่าการปิดกั้น I / O นั้นต้องการ syscalls มากขึ้น

  • โปรแกรมเมอร์เครือข่ายอื่นบางคนตกใจที่รู้ว่าepollมักจะช้ากว่าpollซึ่งทำให้รู้สึกสมบูรณ์แบบถ้าคุณรู้ว่าการจัดการepollต้องใช้ syscalls มากขึ้น

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

(ขออภัยที่ไม่ได้ตอบคำถามนี้ แต่ฉันกำลังมองหาคำตอบและคำถามนี้ยังคงปรากฏที่ด้านบนของผลลัพธ์ Google)


โปรดทราบว่าการใช้คำแนะนำใด ๆ ตามฮาร์ดแวร์และซอฟต์แวร์จากยุค 2000 โดยไม่มีการทดสอบในวันนี้จะเป็นวิธีที่น่าสงสัยมาก ในขณะที่ข้อเท็จจริงจำนวนมากเกี่ยวกับmmapvs read()ในเธรดนั้นยังคงเป็นจริงเหมือนในอดีตที่ผ่านมาประสิทธิภาพโดยรวมไม่สามารถตัดสินได้โดยการเพิ่มข้อดีข้อเสีย แต่โดยการทดสอบกับการกำหนดค่าฮาร์ดแวร์เฉพาะ ตัวอย่างเช่นเป็นที่ถกเถียงกันอยู่ว่า "การเรียก mmap นั้นมีค่าใช้จ่ายมากกว่าการอ่าน" - ใช่mmapต้องเพิ่มการแมปลงในตารางหน้ากระบวนการ แต่readต้องคัดลอกไบต์ที่อ่านทั้งหมดจากเคอร์เนลไปยังพื้นที่ผู้ใช้
BeeOnRope

ผลที่สุดคือฮาร์ดแวร์ของฉัน (ปัจจุบันคือประมาณปี 2018) ของฉันmmapมีค่าใช้จ่ายต่ำกว่าreadการอ่านขนาดใหญ่กว่า (4 KiB) ตอนนี้มันเป็นความจริงที่ว่าถ้าคุณต้องการเข้าถึงข้อมูลแบบสุ่มและแบบสุ่มmmapก็ดีจริง ๆ - แต่การสนทนาไม่จำเป็นจริง: mmapอาจยังดีที่สุดสำหรับการเข้าถึงแบบลำดับเช่นกัน
BeeOnRope

1
@BeeOnRope: คุณอาจสงสัยว่ามีคำแนะนำตามฮาร์ดแวร์และซอฟต์แวร์จากยุค 2000 แต่ฉันสงสัยในเรื่องของมาตรฐานที่ไม่มีวิธีการและข้อมูล หากคุณต้องการทำให้เคสmmapเร็วขึ้นฉันคาดว่าจะเห็นเครื่องมือทดสอบทั้งหมดอย่างน้อย (ซอร์สโค้ด) พร้อมผลการทดสอบแบบตารางและหมายเลขรุ่นโปรเซสเซอร์
Dietrich Epp

@BeeOnRope: โปรดจำไว้ว่าเมื่อคุณทำการทดสอบบิตของระบบหน่วยความจำเช่นนี้ microbenchmarks สามารถหลอกลวงได้มากเนื่องจาก TLB flush สามารถส่งผลเสียต่อประสิทธิภาพการทำงานของโปรแกรมที่เหลือและผลกระทบนี้จะไม่ปรากฏขึ้นหาก คุณวัดเพียง mmap เท่านั้น
Dietrich Epp

2
@DietrichEpp - ใช่ฉันจะรอบรู้ในลักษณะพิเศษ TLB โปรดทราบmmapว่าไม่ได้ล้าง TLB ยกเว้นในกรณีที่ผิดปกติ (แต่munmapอาจ) การทดสอบของฉันรวมถึงไมโครบุ๊กมาร์ค (รวมถึงmunmap) และ "ในแอปพลิเคชัน" ที่ทำงานในกรณีใช้งานจริง แน่นอนใบสมัครของฉันไม่เหมือนกับใบสมัครของคุณดังนั้นผู้คนควรทดสอบในพื้นที่ ยังไม่ชัดเจนว่าmmapได้รับความนิยมจากเกณฑ์มาตรฐานขนาดเล็ก: read()ยังได้รับการเพิ่มขึ้นอย่างมากเนื่องจากบัฟเฟอร์ปลายทางด้านผู้ใช้ยังคงอยู่ใน L1 ซึ่งอาจไม่เกิดขึ้นในแอปพลิเคชันขนาดใหญ่ ดังนั้นใช่ "มันซับซ้อน"
BeeOnRope

47

ค่าประสิทธิภาพหลักคือ disk i / o "mmap ()" เร็วกว่าไอเท็มอย่างแน่นอน แต่ความแตกต่างอาจไม่ชัดเจนเนื่องจากดิสก์ i / o จะควบคุมการทำงานของคุณ

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

ฉันไม่แนะนำให้แยก mmap'ing แต่ละเร็กคอร์ดอย่างแน่นอนเว้นแต่ว่า "เรคคอร์ด" ของคุณมีขนาดใหญ่มาก - ซึ่งจะช้ามากอย่างน่ากลัวต้องใช้ระบบ 2 สายสำหรับแต่ละเร็กคอร์ดและอาจสูญเสียเพจออกจากแคชหน่วยความจำดิสก์ .... .

ในกรณีของคุณฉันคิดว่า mmap () istream และการโทรแบบ open () / read () ระดับต่ำจะเหมือนกันทั้งหมด ฉันจะแนะนำ mmap () ในกรณีเหล่านี้:

  1. มีการเข้าถึงแบบสุ่ม (ไม่ใช่ลำดับ) ภายในไฟล์และ
  2. ทุกอย่างลงตัวพอดีกับหน่วยความจำหรือมีตำแหน่งอ้างอิงภายในไฟล์เพื่อให้สามารถจับคู่บางหน้าเข้ากับหน้าอื่น ๆ วิธีนี้ทำให้ระบบปฏิบัติการใช้ RAM ที่มีอยู่เพื่อให้เกิดประโยชน์สูงสุด
  3. หรือหากกระบวนการหลายกระบวนการกำลังอ่าน / ทำงานกับไฟล์เดียวกันดังนั้น mmap () นั้นยอดเยี่ยมเพราะกระบวนการทั้งหมดแชร์เพจฟิสิคัลเดียวกัน

(btw - ฉันรัก mmap () / MapViewOfFile ()


ข้อดีของการเข้าถึงแบบสุ่ม: นี่อาจเป็นหนึ่งในสิ่งที่ทำให้ฉันเข้าใจ
Ben Collins

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

@MvG: คุณเข้าใจประเด็นเกี่ยวกับดิสก์ i / o หรือไม่? หากไฟล์เหมาะกับพื้นที่ที่อยู่ แต่ไม่ใช่หน่วยความจำและคุณมีการเข้าถึงแบบสุ่มคุณสามารถมีการเข้าถึงระเบียนทุกครั้งที่ต้องการย้ายหัวดิสก์และค้นหาหรือการดำเนินการกับหน้า SSD ซึ่งจะเป็นหายนะต่อประสิทธิภาพ
Tim Cooper

3
ดิสก์ i / o กว้างยาวควรเป็นอิสระจากวิธีการเข้าถึง หากคุณมีการเข้าถึงไฟล์ที่มีขนาดใหญ่กว่าแรมแบบสุ่มทั้ง mmap และ find + read นั้นจะมีการผูกดิสก์อย่างรุนแรง มิฉะนั้นทั้งสองจะได้รับประโยชน์จากแคช ฉันไม่เห็นขนาดไฟล์เมื่อเทียบกับขนาดหน่วยความจำเป็นอาร์กิวเมนต์ที่แข็งแกร่งในทิศทางใดทิศทางหนึ่ง ในทางกลับกันขนาดไฟล์เทียบกับพื้นที่ที่อยู่เป็นอาร์กิวเมนต์ที่แข็งแกร่งมากโดยเฉพาะอย่างยิ่งสำหรับการเข้าถึงแบบสุ่มอย่างแท้จริง
MvG

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

43

mmap เป็นวิธีที่เร็วขึ้น คุณอาจเขียนมาตรฐานง่ายๆเพื่อพิสูจน์ด้วยตัวคุณเอง:

char data[0x1000];
std::ifstream in("file.bin");

while (in)
{
  in.read(data, 0x1000);
  // do something with data
}

เมื่อเทียบกับ:

const int file_size=something;
const int page_size=0x1000;
int off=0;
void *data;

int fd = open("filename.bin", O_RDONLY);

while (off < file_size)
{
  data = mmap(NULL, page_size, PROT_READ, 0, fd, off);
  // do stuff with data
  munmap(data, page_size);
  off += page_size;
}

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

หากทำได้คุณอาจลองแบ่งข้อมูลออกเป็นหลายไฟล์ซึ่งอาจเป็น mmap () - ทั้งหมดโดยสิ้นเชิงแทนที่จะเป็นส่วนหนึ่ง (ง่ายกว่า)

สองสามเดือนที่ผ่านมาฉันมีการใช้งานครึ่งหนึ่งของ mmap หน้าต่างแบบเลื่อน () - คลาสสตรีมสำหรับ boost_iostreams แต่ไม่มีใครใส่ใจและฉันยุ่งกับสิ่งอื่น ๆ น่าเสียดายที่ฉันลบโครงการเก่าที่ยังสร้างไม่เสร็จเมื่อไม่กี่สัปดาห์ที่ผ่านมาและนั่นเป็นหนึ่งในเหยื่อ :-(

อัปเดต : ฉันควรเพิ่มข้อแม้ที่เกณฑ์มาตรฐานนี้จะดูแตกต่างกันมากใน Windows เพราะ Microsoft ใช้แคชไฟล์ที่ดีซึ่งทำในสิ่งที่คุณจะทำกับ mmap เป็นอันดับแรก เช่นสำหรับไฟล์ที่เข้าถึงบ่อยคุณสามารถทำ std :: ifstream.read () และมันจะเร็วเท่า mmap เพราะแคชไฟล์จะทำการแมปหน่วยความจำสำหรับคุณแล้วและมันโปร่งใส

การปรับปรุงครั้งสุดท้าย : ดูผู้คน: ในหลาย ๆ แพลตฟอร์มที่แตกต่างกันของระบบปฏิบัติการรวมทั้งไลบรารีมาตรฐานดิสก์และดิสก์และลำดับชั้นหน่วยความจำฉันไม่สามารถบอกได้อย่างชัดเจนว่าการเรียกระบบที่mmapถูกมองว่าเป็นกล่องดำจะเร็วขึ้นเสมอ readกว่า นั่นไม่ได้เป็นความตั้งใจของฉันอย่างแน่นอนแม้ว่าคำพูดของฉันอาจตีความได้เช่นนั้น ในที่สุดประเด็นของฉันคือโดยทั่วไปแล้วหน่วยความจำที่แมป i / o จะเร็วกว่า i-o ที่ใช้ไบต์ นี้ยังคงเป็นจริง หากคุณพบการทดลองที่ไม่มีความแตกต่างระหว่างทั้งสองดังนั้นคำอธิบายเดียวที่สมเหตุสมผลสำหรับฉันคือแพลตฟอร์มของคุณใช้การแมปหน่วยความจำภายใต้ฝาครอบในลักษณะที่เป็นประโยชน์ต่อประสิทธิภาพการโทรread. วิธีเดียวที่จะเป็นบางอย่างที่คุณกำลังใช้หน่วยความจำที่แมป I / O mmapในทางแบบพกพาไปใช้งานที่ หากคุณไม่สนใจเกี่ยวกับการพกพาและคุณสามารถพึ่งพาลักษณะเฉพาะของแพลตฟอร์มเป้าหมายของคุณการใช้readอาจเหมาะสมโดยไม่ต้องเสียสละประสิทธิภาพใด ๆ

แก้ไขเพื่อล้างรายการคำตอบ: @jbl:

mmap หน้าต่างบานเลื่อนฟังดูน่าสนใจ คุณช่วยพูดเพิ่มอีกนิดได้ไหม?

แน่นอน - ฉันกำลังเขียนไลบรารี C ++ สำหรับ Git (เป็น libgit ++ หากคุณต้องการ) และฉันพบปัญหาที่คล้ายกันกับเรื่องนี้: ฉันต้องเปิดไฟล์ขนาดใหญ่ (ใหญ่มาก) และไม่มีประสิทธิภาพเป็นสุนัขทั้งหมด (ตามที่ควรจะเป็นด้วยstd::fstream)

Boost::Iostreamsมีแหล่งที่มา mapped_file แล้ว แต่ปัญหาคือมันเป็นmmapping ทั้งไฟล์ซึ่ง จำกัด คุณไว้ที่ 2 ^ (คำพูด) สำหรับเครื่อง 32 บิต 4GB ไม่ใหญ่พอ มันไม่สมเหตุสมผลที่จะคาดหวังว่าจะมี.packไฟล์ใน Git ที่ใหญ่กว่านั้นมากดังนั้นฉันต้องอ่านไฟล์ในกลุ่มโดยไม่ต้องใช้ไฟล์ i / o ปกติ ภายใต้หน้าปกของBoost::Iostreamsฉันใช้งานซอร์สซึ่งเป็นอีกมุมมองหนึ่งของการโต้ตอบระหว่างstd::streambufและstd::istreamกับ นอกจากนี้คุณยังสามารถลองใช้วิธีการที่คล้ายกันโดยเพียงแค่การสืบทอดstd::filebufเป็นmapped_filebufและในทำนองเดียวกันสืบทอดเข้าstd::fstream a mapped_fstreamเป็นการโต้ตอบระหว่างคนสองคนที่ถูกต้องยาก Boost::Iostreams มีบางงานที่ทำเพื่อคุณและยังมีตะขอสำหรับตัวกรองและโซ่ดังนั้นฉันคิดว่ามันจะมีประโยชน์มากกว่าที่จะใช้มันในแบบนั้น


3
RE: ไฟล์แคช mmaped บน Windows ตรง: เมื่อเปิดใช้งานการบัฟเฟอร์ไฟล์หน่วยความจำเคอร์เนลจะแมปไฟล์ที่คุณกำลังอ่านอยู่ภายในอ่านลงในบัฟเฟอร์นั้นและคัดลอกกลับเข้าไปในกระบวนการของคุณ ราวกับว่าหน่วยความจำของคุณทำการแมปด้วยตัวคุณเองยกเว้นขั้นตอนการทำสำเนาเพิ่มเติม
Chris Smith

6
ฉันไม่เห็นด้วยกับคำตอบที่ยอมรับ แต่ฉันเชื่อว่าคำตอบนี้ผิด ฉันทำตามคำแนะนำของคุณและลองใช้รหัสของคุณบนเครื่อง 64 บิต Linux และ mmap () ไม่เร็วไปกว่าการใช้ STL ในทางทฤษฎีแล้วฉันไม่คาดหวังว่า 'mmap ()' จะเร็วกว่านี้ (หรือช้ากว่า)
Tim Cooper

3
@Tim Cooper: คุณอาจพบกระทู้นี้ ( markmail.org/message/ … ) ที่น่าสนใจ สังเกตสองสิ่ง: mmap ไม่ได้รับการปรับให้เหมาะสมใน Linux และต้องมีการใช้ madvise ในการทดสอบเพื่อให้ได้ผลลัพธ์ที่ดีที่สุด
Ben Collins

9
เรียนเบ็น: ฉันได้อ่านลิงค์นั้นแล้ว หาก 'mmap ()' ไม่เร็วขึ้นใน Linux และ MapViewOfFile () ไม่เร็วขึ้นใน Windows คุณสามารถอ้างได้ว่า "mmap เร็วขึ้น" นอกจากนี้สำหรับเหตุผลทางทฤษฎีฉันเชื่อว่า mmap () ไม่เร็วสำหรับการอ่านตามลำดับ - คุณมีคำอธิบายใดที่ตรงกันข้าม
Tim Cooper

11
เบ็นทำไมต้องรำคาญกับmmap()ไฟล์ทีละหน้า? หาก a size_tมีความจุเพียงพอที่จะเก็บขนาดของไฟล์ (เป็นไปได้มากในระบบ 64- บิต) ดังนั้นเพียงแค่mmap()ไฟล์ทั้งหมดในการโทรครั้งเดียว
Steve Emmerson

39

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

mmap ดูเหมือนเวทมนตร์

การกรณีที่ไฟล์ที่มีอยู่แล้วอย่างเต็มที่ที่แคช1เป็นพื้นฐาน2 , mmapอาจจะดูสวยมากเช่นมายากล :

  1. mmap เพียงต้องการ 1 การเรียกระบบเพื่อ (อาจ) แมปไฟล์ทั้งหมดหลังจากนั้นไม่จำเป็นต้องเรียกระบบเพิ่มเติมอีก
  2. mmap ไม่ต้องการสำเนาข้อมูลไฟล์จากเคอร์เนลไปยังพื้นที่ผู้ใช้
  3. mmapอนุญาตให้คุณเข้าถึงไฟล์ "ในฐานะหน่วยความจำ" รวมถึงการประมวลผลด้วยเทคนิคขั้นสูงที่คุณสามารถทำได้กับหน่วยความจำเช่นคอมไพเลอร์อัตโนมัติ - เวกเตอร์, ซิมอินทรินอิน, การดึงข้อมูลล่วงหน้า

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

ก็สามารถ

mmap ไม่ใช่เวทมนต์เพราะ ...

mmap ยังคงทำงานต่อหน้าได้

ต้นทุนหลักที่ซ่อนอยู่ของmmapvs read(2)(ซึ่งจริงๆแล้วเป็น syscall ระดับ OS ที่เทียบเท่ากับการอ่านบล็อก ) คือmmapคุณจะต้อง "ทำงานบางอย่าง" สำหรับหน้า 4K ทุกหน้าในพื้นที่ผู้ใช้แม้ว่ามันอาจจะถูกซ่อนโดย กลไกความผิดหน้า

ตัวอย่างเช่นการนำไปใช้โดยทั่วไปที่เพียงแค่mmapไฟล์ทั้งหมดจะต้องมีข้อบกพร่องดังนั้น 100 GB / 4K = 25 ล้านข้อบกพร่องเพื่ออ่านไฟล์ 100 GB ตอนนี้สิ่งเหล่านี้จะเป็นความผิดพลาดเล็กน้อยแต่ความผิดพลาดของหน้าเว็บถึง 25 พันล้านเพจนั้นยังไม่เร็วนัก ค่าใช้จ่ายของความผิดเล็กน้อยอาจเป็นใน 100s ของ nanos ในกรณีที่ดีที่สุด

mmap อาศัยประสิทธิภาพ TLB อย่างมาก

ตอนนี้คุณสามารถส่งผ่านMAP_POPULATEไปmmapยังบอกให้ตั้งค่าตารางหน้าทั้งหมดก่อนที่จะกลับมาดังนั้นจึงไม่ควรมีข้อบกพร่องของหน้าในขณะที่เข้าถึง ตอนนี้มีปัญหาเล็ก ๆ น้อย ๆ ว่ามันยังอ่านไฟล์ทั้งหมดลงใน RAM, ซึ่งจะระเบิดขึ้นถ้าคุณพยายามที่จะแมไฟล์ 100GB - แต่ขอไม่สนใจว่าตอนนี้3 เคอร์เนลต้องทำงานต่อหน้าเพื่อตั้งค่าตารางหน้าเหล่านี้ (แสดงเป็นเวลาเคอร์เนล) ปลายนี้ขึ้นเป็นค่าใช้จ่ายที่สำคัญในmmapวิธีการและมันสัดส่วนกับขนาดของไฟล์ (เช่นจะไม่ได้รับความสำคัญน้อยกว่าขนาดของไฟล์ที่เติบโต) 4

ในที่สุดแม้ในพื้นที่ของผู้ใช้ที่เข้าถึงการแมปดังกล่าวจะไม่ฟรี (เทียบกับบัฟเฟอร์หน่วยความจำขนาดใหญ่ที่ไม่ได้มาจากไฟล์mmap) - แม้เมื่อตั้งค่าหน้าตารางแล้วการเข้าถึงหน้าใหม่แต่ละครั้งก็จะเกิดขึ้น ในเชิงแนวคิดต้องมี TLB miss เนื่องจากการmmapอ้างถึงไฟล์หมายถึงการใช้แคชหน้าและหน้า 4K ของไฟล์คุณจึงต้องเสียค่าใช้จ่าย 25 ล้านครั้งสำหรับไฟล์ 100GB

ตอนนี้ต้นทุนที่แท้จริงของการพลาด TLB เหล่านี้ขึ้นอยู่กับฮาร์ดแวร์ของคุณอย่างน้อยที่สุดดังต่อไปนี้: (a) จำนวน TLB 4K ที่คุณเข้าร่วมและวิธีการแคชการแปลที่เหลือทำงานอย่างมีประสิทธิภาพ (b) ความสัมพันธ์กับฮาร์ดแวร์ ด้วย TLB - เช่นสามารถดึงการเรียกหน้าเพจล่วงหน้าได้หรือไม่ (c) ความรวดเร็วและความขนานของฮาร์ดแวร์ในการเดินหน้า ในโปรเซสเซอร์ x86 Intel ระดับไฮเอนด์ที่ทันสมัยฮาร์ดแวร์การเดินหน้าโดยทั่วไปมีความแข็งแกร่งมาก: มีวอล์กเกอร์เพจขนานกันอย่างน้อย 2 เพจการเดินเพจสามารถเกิดขึ้นพร้อมกันกับการดำเนินการต่อเนื่องและการดึงฮาร์ดแวร์ล่วงหน้าสามารถเรียกเพจได้ ดังนั้นผลกระทบ TLB ต่อโหลดการอ่านแบบสตรีมจึงค่อนข้างต่ำ - และโหลดดังกล่าวมักจะทำงานในลักษณะเดียวกันโดยไม่คำนึงถึงขนาดหน้ากระดาษ ฮาร์ดแวร์อื่น ๆ มักจะแย่กว่านั้นมาก!

อ่าน () หลีกเลี่ยงข้อผิดพลาดเหล่านี้

read()syscall ซึ่งเป็นสิ่งที่โดยทั่วไปรองรับ "บล็อกอ่าน" ชนิดสายนำเสนอเช่นใน C, C ++ และภาษาอื่น ๆ ที่มีข้อเสียอย่างหนึ่งหลักที่ทุกคนเป็นอย่างดีตระหนักถึง:

  • การread()เรียกใช้ N ไบต์ทุกครั้งต้องคัดลอก N ไบต์จากเคอร์เนลไปยังพื้นที่ผู้ใช้

ในอีกทางหนึ่งก็เป็นการหลีกเลี่ยงค่าใช้จ่ายส่วนใหญ่ - คุณไม่จำเป็นต้องแมปในหน้าเว็บขนาด 25 ล้าน 4K ลงในพื้นที่ผู้ใช้ โดยปกติคุณสามารถmallocบัฟเฟอร์ขนาดเล็กบัฟเฟอร์เดียวในพื้นที่ผู้ใช้และนำกลับมาใช้ซ้ำสำหรับการreadโทรทั้งหมดของคุณ ทางด้านเคอร์เนลแทบจะไม่มีปัญหากับหน้า 4K หรือ TLB ที่พลาดเพราะ RAM ทั้งหมดมักจะถูกแมปเชิงเส้นโดยใช้หน้าขนาดใหญ่มากสองสามหน้า (เช่น 1 GB หน้าใน x86) ดังนั้นเพจที่อยู่ในแคชหน้าจะถูกครอบคลุม มีประสิทธิภาพมากในพื้นที่เคอร์เนล

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

การทำงานพิเศษต่อหน้าโดยนัยmmapมีค่าใช้จ่ายมากกว่างานต่อไบต์ของการคัดลอกเนื้อหาไฟล์จากเคอร์เนลไปยังพื้นที่ผู้ใช้โดยนัยread()หรือไม่?

ในหลาย ๆ ระบบพวกเขามีความสมดุลโดยประมาณ โปรดทราบว่าแต่ละสเกลนั้นมีคุณลักษณะที่แตกต่างกันโดยสิ้นเชิงของฮาร์ดแวร์และสแต็ค

โดยเฉพาะอย่างยิ่งmmapวิธีการจะค่อนข้างเร็วเมื่อ:

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

... ในขณะที่read()วิธีการจะค่อนข้างเร็วเมื่อ:

  • read()syscall มีประสิทธิภาพสำเนาที่ดี เช่นcopy_to_userประสิทธิภาพที่ดีในด้านเคอร์เนล
  • เคอร์เนลมีวิธีที่มีประสิทธิภาพ (เทียบกับ userland) ในการแมปหน่วยความจำเช่นใช้หน้าใหญ่เพียงไม่กี่หน้าพร้อมการสนับสนุนฮาร์ดแวร์
  • เคอร์เนลมี syscalls ที่รวดเร็วและวิธีเก็บรักษารายการ TLB ของเคอร์เนลทั่ว syscalls

ปัจจัยดังกล่าวข้างต้นฮาร์ดแวร์แตกต่างกันอย่างดุเดือดข้ามแพลตฟอร์มที่แตกต่างกันแม้จะอยู่ในครอบครัวเดียวกัน (เช่นภายใน x86 รุ่นและโดยเฉพาะอย่างยิ่งกลุ่มตลาด) และแน่นอนทั่วสถาปัตยกรรม (เช่น ARM VS x86 VS PPC)

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

  • การเพิ่มข้อผิดพลาดตามที่อธิบายไว้ข้างต้นซึ่งช่วยmmapกรณีโดยไม่MAP_POPULATEได้
  • การเพิ่มcopy_to_userเมธอดของพา ธ ด่วนในarch/x86/lib/copy_user_64.Sเช่นใช้REP MOVQเมื่อมันเร็วซึ่งช่วยเรื่องread()นี้ได้จริง

อัปเดตหลังจาก Spectre และ Meltdown

การลดช่องโหว่ Spectre และ Meltdown ทำให้ค่าใช้จ่ายในการโทรระบบเพิ่มขึ้นอย่างเห็นได้ชัด ในระบบที่ฉันวัดค่าใช้จ่ายของการเรียกระบบ "ไม่ทำอะไรเลย" (ซึ่งเป็นการประเมินค่าใช้จ่ายที่แท้จริงของการเรียกระบบนอกเหนือจากงานจริงใด ๆ ที่ทำโดยการโทร) ไปจากประมาณ 100 ns ในแบบทั่วไป ระบบ Linux ที่ทันสมัยประมาณ 700 ns นอกจากนี้ขึ้นอยู่กับระบบของคุณการแยกการแก้ไขตารางหน้าโดยเฉพาะสำหรับ Meltdown สามารถมีผลต่อเนื่องเพิ่มเติมนอกเหนือจากค่าโทรระบบโดยตรงเนื่องจากจำเป็นต้องโหลดรายการ TLB ซ้ำ

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

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


1สิ่งนี้มากหรือน้อยก็รวมถึงกรณีที่ไฟล์ไม่ได้ถูกแคชอย่างสมบูรณ์เพื่อเริ่มต้นด้วย แต่เมื่อ OS อ่านล่วงหน้าดีพอที่จะทำให้มันปรากฏขึ้นมา (เช่นหน้ามักจะถูกแคชเมื่อคุณ ต้องการ). นี่เป็นปัญหาที่ลึกซึ้ง แต่เพราะวิธีการทำงานข้างหน้าอ่านมักจะค่อนข้างแตกต่างระหว่างmmapและreadสายและสามารถปรับเปลี่ยนต่อไปโดย "คำแนะนำ" โทรตามที่อธิบายใน2

2 ... เพราะถ้าไฟล์ไม่ถูกเก็บไว้พฤติกรรมของคุณจะถูกครอบงำโดยความกังวลของ IO อย่างสมบูรณ์รวมถึงความเห็นอกเห็นใจของรูปแบบการเข้าถึงของคุณกับฮาร์ดแวร์ที่อยู่ภายใต้ - และความพยายามทั้งหมดของคุณควรจะทำให้มั่นใจได้ว่า เป็นไปได้เช่นผ่านการใช้madviseหรือการfadviseโทร (และการเปลี่ยนแปลงระดับแอปพลิเคชันใด ๆ ที่คุณสามารถทำได้เพื่อปรับปรุงรูปแบบการเข้าถึง)

3คุณสามารถหลีกเลี่ยงสิ่งนั้นได้เช่นโดยเรียงลำดับmmapไอเอ็นจีในหน้าต่างที่มีขนาดเล็กลงให้พูดว่า 100 MB

4ในความเป็นจริงมันกลับกลายเป็นMAP_POPULATEวิธี (อย่างน้อยหนึ่งชุดฮาร์ดแวร์ / OS) เพียงเล็กน้อยเร็วกว่าไม่ได้ใช้อาจเป็นเพราะเคอร์เนลใช้faultaround - ดังนั้นจำนวนจริงของความผิดพลาดเล็กน้อยจะลดลงโดยปัจจัย 16 หรือไม่ก็.


4
ขอขอบคุณที่ให้คำตอบที่เหมาะสมยิ่งขึ้นสำหรับปัญหาที่ซับซ้อนนี้ ดูเหมือนว่าคนส่วนใหญ่จะเห็นได้ชัดว่า mmap นั้นเร็วขึ้น แต่ในความเป็นจริงมันไม่ได้เกิดขึ้นบ่อยนัก ในการทดลองของฉันการเข้าถึงฐานข้อมูลขนาดใหญ่ 100GB แบบสุ่มด้วยดัชนีในหน่วยความจำกลายเป็นเร็วกว่าด้วย pread () แม้ว่าฉันจะ Malloc'ing buffer สำหรับการเข้าถึงแต่ละล้านครั้ง และดูเหมือนว่า LOF ของผู้คนในอุตสาหกรรมได้สังเกตเห็นเหมือนกัน
Caetano Sauer

5
ใช่มันขึ้นอยู่กับสถานการณ์เป็นอย่างมาก หากคุณอ่านมีขนาดเล็กพอและเมื่อเวลาผ่านไปคุณมักจะอ่านไบต์เดียวกันซ้ำ ๆmmapจะได้รับประโยชน์ผ่านไม่ได้เนื่องจากจะหลีกเลี่ยงการโทรเคอร์เนลคงที่ค่าใช้จ่าย ในทางกลับกันmmapก็เพิ่มความดัน TLB และทำให้ช้าลงสำหรับเฟส "อุ่นเครื่อง" ที่มีการอ่านไบต์เป็นครั้งแรกในกระบวนการปัจจุบัน (แม้ว่าจะยังอยู่ในหน้าเพจ) เนื่องจากอาจทำ ทำงานได้มากกว่าreadตัวอย่างเช่นหน้า "ติดขัด" รอบข้าง ... และสำหรับแอปพลิเคชันเดียวกัน "วอร์มอัพ" เป็นสิ่งที่สำคัญ! @CaetanoSauer
BeeOnRope

ฉันคิดว่าที่คุณพูดว่า "... แต่ข้อบกพร่องของหน้าเว็บกว่า 25 พันล้านเพจนั้นยังคงไม่เร็วนัก ... " คุณควรอ่าน "... แต่ข้อบกพร่องของหน้าเว็บถึง 25 ล้านเพจนั้นยังไม่เร็วพอ ... " . ฉันไม่ได้เป็นบวก 100% ดังนั้นฉันจึงไม่แก้ไขโดยตรง
Ton van den Heuvel

7

ฉันขอโทษเบ็นคอลลินส์สูญเสียรหัสที่มาของ windows mmap แบบเลื่อน นั่นเป็นเรื่องดีที่มีใน Boost

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

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

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

ทั้งหมดนี้ใช้งานได้ดีภายใต้ Windows ด้วยการใช้ CreateFileMapping () และ MapViewOfFile () (และ GetSystemInfo () เพื่อรับ SYSTEM_INFO.dwAllocationGranularity --- ไม่ใช่ SYSTEM_INFO.dwPageSize)


ฉันเพิ่ง googled และพบข้อมูลเล็กน้อยนี้เกี่ยวกับ dwAllocationGranularity - ฉันใช้ dwPageSize และทุกอย่างก็พัง ขอบคุณ!
wickedchicken

4

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

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


3

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

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


3

ผมยอมรับว่าไฟล์ mmap'd I / O เป็นไปได้เร็วขึ้น แต่ในขณะที่การเปรียบเทียบรหัสของคุณไม่ควรเช่นเคาน์เตอร์จะค่อนข้างที่ดีที่สุด?

Ben Collins เขียนว่า:

char data[0x1000];
std::ifstream in("file.bin");

while (in)
{
    in.read(data, 0x1000);
    // do something with data 
}

ฉันขอแนะนำให้ลอง:

char data[0x1000];
std::ifstream iifle( "file.bin");
std::istream  in( ifile.rdbuf() );

while( in )
{
    in.read( data, 0x1000);
    // do something with data
}

และยิ่งไปกว่านั้นคุณอาจลองทำให้ขนาดบัฟเฟอร์มีขนาดเท่ากับหน้าหนึ่งของหน่วยความจำเสมือนในกรณีที่ 0x1000 ไม่ใช่ขนาดหน้าหนึ่งของหน่วยความจำเสมือนบนเครื่องของคุณ ... IMHO mmap'd ไฟล์ I / O ชนะ แต่สิ่งนี้จะทำให้สิ่งต่างๆใกล้ชิดยิ่งขึ้น


2

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


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

2

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


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

มันทำงานได้อย่างสมบูรณ์แบบเมื่อคุณมีแผนผังต้นไม้ที่มีความเสถียรและชัดเจน จากนั้นคุณสามารถส่งทุกอย่างไปยัง struct ที่เกี่ยวข้องของคุณและติดตามตัวชี้ไฟล์ภายในโดยเพิ่มออฟเซตของ "mmap start address" ทุกครั้ง สิ่งนี้คล้ายกับระบบไฟล์ที่ใช้ inodes และ
แผนผัง

1

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


อ๋อ ฉันได้คิดเกี่ยวกับเรื่องนี้และอาจจะลองในรุ่นต่อมา การสำรองห้องพักเดียวที่ฉันมีคือการประมวลผลนั้นสั้นกว่าเวลาแฝงของ I / O ดังนั้นจึงอาจไม่มีประโยชน์มากนัก
jbl

1

ฉันคิดว่าสิ่งที่ยิ่งใหญ่ที่สุดเกี่ยวกับ mmap นั้นมีศักยภาพสำหรับการอ่านแบบอะซิงโครนัสด้วย:

    addr1 = NULL;
    while( size_left > 0 ) {
        r = min(MMAP_SIZE, size_left);
        addr2 = mmap(NULL, r,
            PROT_READ, MAP_FLAGS,
            0, pos);
        if (addr1 != NULL)
        {
            /* process mmap from prev cycle */
            feed_data(ctx, addr1, MMAP_SIZE);
            munmap(addr1, MMAP_SIZE);
        }
        addr1 = addr2;
        size_left -= r;
        pos += r;
    }
    feed_data(ctx, addr1, r);
    munmap(addr1, r);

ปัญหาคือฉันไม่สามารถหา MAP_FLAGS ที่ถูกต้องเพื่อให้คำใบ้ว่าหน่วยความจำนี้ควรซิงค์จากไฟล์โดยเร็ว ฉันหวังว่า MAP_POPULATE จะให้คำแนะนำที่ถูกต้องสำหรับ mmap (เช่นจะไม่พยายามโหลดเนื้อหาทั้งหมดก่อนกลับจากการโทร แต่จะทำใน async. ด้วย feed_data) อย่างน้อยก็ให้ผลลัพธ์ที่ดีขึ้นด้วยการตั้งค่าสถานะนี้แม้ด้วยตนเองระบุว่ามันไม่ทำอะไรเลยโดยไม่ต้อง MAP_PRIVATE ตั้งแต่ 2.6.23


2
คุณต้องการposix_madviseให้WILLNEEDธงมีคำแนะนำขี้เกียจที่จะเติมเต็ม
ShadowRanger

@ShadowRanger ฟังดูสมเหตุสมผล แม้ว่าฉันจะอัปเดต man page ให้ชัดเจนว่าposix_madviseเป็นการโทรแบบ async นอกจากนี้ยังเป็นการอ้างอิงที่ดีmlockสำหรับผู้ที่ต้องการรอจนกว่าหน่วยความจำทั้งหมดจะพร้อมใช้งานโดยไม่มีข้อบกพร่องของหน้า
ony
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.