ข้อดีและข้อเสียของ HugePages

ข้อดีและข้อเสียของ HugePages

แปลบทความที่จัดทำขึ้นสำหรับนักศึกษารายวิชา "ผู้ดูแลระบบลินุกซ์".

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

ส่วนที่ 1: การตรวจสอบว่ามีการเปิดใช้งาน hugepages บน Linux (Original ที่นี่)

ปัญหา:
คุณต้องตรวจสอบว่า HugePages เปิดใช้งานอยู่ในระบบของคุณหรือไม่

การแก้ปัญหา:
มันค่อนข้างง่าย:

cat /sys/kernel/mm/transparent_hugepage/enabled

คุณจะได้รับสิ่งนี้:

always [madvise] never

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

แมดไวซ์ หมายความว่า transparent hugepages เปิดใช้งานเฉพาะพื้นที่หน่วยความจำที่ร้องขอการใช้เพจขนาดใหญ่อย่างชัดเจน แมดไวส์(2).

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

ไม่เคย หมายความว่า transparent hugepages จะไม่รวมอยู่แม้ว่าจะร้องขอโดยใช้ madvise ก็ตาม หากต้องการข้อมูลเพิ่มเติม โปรดติดต่อ เอกสาร เคอร์เนลลินุกซ์

วิธีเปลี่ยนค่าเริ่มต้น

ตัวเลือก 1: เปลี่ยนแปลงโดยตรง sysfs (หลังจากรีบูตพารามิเตอร์จะกลับสู่ค่าเริ่มต้น):

echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled

ตัวเลือก 2: เปลี่ยนค่าเริ่มต้นของระบบโดยการคอมไพล์เคอร์เนลใหม่ด้วยการกำหนดค่าที่แก้ไข (แนะนำให้ใช้ตัวเลือกนี้เฉพาะในกรณีที่คุณใช้เคอร์เนลแบบกำหนดเอง):

  • หากต้องการตั้งค่าเป็นค่าเริ่มต้นเสมอ ให้ใช้:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • หากต้องการตั้งค่า madvise เป็นค่าเริ่มต้น ให้ใช้:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

ส่วนที่ 2: ข้อดีและข้อเสียของ HugePages

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

โปรดทราบว่าเรากำลังพูดถึงระบบ x64 แบบ 86 บิตที่ใช้ Linux และฉันเพียงแต่สันนิษฐานว่าระบบรองรับหน้าขนาดใหญ่แบบโปร่งใส (เนื่องจากไม่มีข้อเสียที่หน้าใหญ่จะไม่ถูกเขียนทับ) เช่นเดียวกับในกรณีของ Linux สมัยใหม่เกือบทุกรุ่น สิ่งแวดล้อม.

ฉันจะแนบคำอธิบายทางเทคนิคเพิ่มเติมในลิงก์ด้านล่าง

หน่วยความจำเสมือน

หากคุณเป็นโปรแกรมเมอร์ C++ คุณจะรู้ว่าวัตถุในหน่วยความจำมีที่อยู่เฉพาะ (ค่าตัวชี้)

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

วิธีนี้มีข้อดีหลายประการ แต่ข้อดีที่สำคัญที่สุดคือ:

  • ประสิทธิภาพ (ด้วยเหตุผลหลายประการ);
  • การแยกโปรแกรม กล่าวคือ ไม่มีโปรแกรมใดสามารถอ่านจากหน่วยความจำของโปรแกรมอื่นได้

เพจคืออะไร?

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

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

กระบวนการนี้มีความโปร่งใสในการสตรีม ซึ่งหมายความว่าไม่จำเป็นต้องอ่านโดยตรงจาก HDD/SSD ขนาดของเพจปกติคือ 4096 ไบต์ ขนาด Hugepages คือ 2 เมกะไบต์

บัฟเฟอร์ที่เกี่ยวข้องกับการแปล (TLB)

เมื่อโปรแกรมเข้าถึงเพจของหน่วยความจำ CPU จะต้องรู้ว่าเพจฟิสิคัลใดที่จะอ่านข้อมูลจาก (นั่นคือ มีการแมปที่อยู่เสมือน)

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

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

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

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

Hugepages มาช่วยเหลือ

แล้วเราจะทำอย่างไรเพื่อหลีกเลี่ยงการล้นของ TLB? (เราถือว่าโปรแกรมยังคงต้องการหน่วยความจำจำนวนเท่าเดิม)

นี่คือที่มาของ Hugepages แทนที่จะเป็น 4096 ไบต์ที่ต้องใช้เพียงรายการ TLB รายการเดียว ตอนนี้รายการ TLB หนึ่งรายการสามารถชี้ไปที่ขนาดมหึมา 2 เมกะไบต์ได้ สมมติว่า TLB มี 512 รายการ หากไม่มี Hugepages เราก็สามารถจับคู่ได้:

4096 b⋅512=2 MB

แล้วเราจะเปรียบเทียบกับพวกเขาได้อย่างไร:

2 MB⋅512=1 GB

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

การปลอมแปลงเพจขนาดใหญ่

เคอร์เนลจะตรวจสอบความถี่ในการใช้งานแต่ละเพจหน่วยความจำโดยอัตโนมัติ หากมีหน่วยความจำกายภาพ (RAM) ไม่เพียงพอ เคอร์เนลจะย้ายเพจที่มีความสำคัญน้อยกว่า (ใช้งานน้อยกว่า) ไปยังฮาร์ดดิสก์เพื่อเพิ่ม RAM บางส่วนสำหรับเพจที่สำคัญกว่า
โดยหลักการแล้ว เช่นเดียวกับ Hugepages อย่างไรก็ตาม เคอร์เนลสามารถสลับได้เฉพาะทั้งหน้าเท่านั้น ไม่ใช่แต่ละไบต์

สมมติว่าเรามีโปรแกรมดังนี้:

char* mymemory = malloc(2*1024*1024); // Возьмем это за одну Hugepage!
// Заполним mymemory какими-либо данными
// Сделаем много других вещей,
// которые приведут к подмене страницы mymemory
// ...
// Запросим доступ только к первому байту
putchar(mymemory[0]); 

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

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

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

การจัดสรรในหน่วยความจำ

หากคุณเขียน C คุณจะรู้ว่าคุณสามารถขอหน่วยความจำจำนวนเล็กน้อย (หรือเกือบใหญ่โดยพลการ) จากฮีปโดยใช้ malloc(). สมมติว่าคุณต้องการหน่วยความจำ 30 ไบต์:

char* mymemory = malloc(30);

สำหรับโปรแกรมเมอร์ อาจดูเหมือนว่าคุณกำลัง "ขอ" หน่วยความจำ 30 ไบต์จากระบบปฏิบัติการและส่งคืนพอยน์เตอร์ไปยังหน่วยความจำเสมือนบางส่วน แต่จริงๆแล้ว malloc () เป็นเพียงฟังก์ชัน C ที่เรียกใช้จากภายในฟังก์ชัน brk และ sbrk เพื่อขอหรือเพิ่มหน่วยความจำจากระบบปฏิบัติการ

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

ในขณะเดียวกัน ทุกอย่างก็เกิดขึ้นโดยไม่มีใครสังเกตเห็น แล้วทำไมคุณถึงต้องกังวลด้วย? แต่เนื่องจากความท้าทาย free() ไม่ได้หมายความว่าอย่างนั้น หน่วยความจำจะต้องส่งคืนไปยังระบบปฏิบัติการทันที.

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

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

การเลือกใช้งาน hugepages

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

โชคดีที่คุณสามารถใช้ madvise()เพื่อเปิดใช้งานการเพจขนาดใหญ่เฉพาะสำหรับพื้นที่หน่วยความจำที่จะมีประโยชน์เท่านั้น

ขั้นแรก ตรวจสอบว่าหน้าใหญ่กำลังทำงานในโหมด madvise() โดยใช้ คำแนะนำ ในตอนต้นของบทความ

จากนั้นใช้ madvise()เพื่อบอกเคอร์เนลอย่างชัดเจนว่าจะใช้ hugepages ได้ที่ไหน

#include <sys/mman.h>
// Аллоцируйте большое количество памяти, которую будете использовать
size_t size = 256*1024*1024;
char* mymemory = malloc(size);
// Просто включите hugepages…
madvise(mymemory, size, MADV_HUGEPAGE);
// … и задайте следующее
madvise(mymemory, size, MADV_HUGEPAGE | MADV_SEQUENTIAL)

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

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

อ่านอะไร?

มีคำถาม? เขียนในความคิดเห็น!

ที่มา: will.com

เพิ่มความคิดเห็น