- เนื่องจากภาระคำขอที่สูง Lichess จึงเปลี่ยนจากการสแกนบล็อกทั้งหมดมาเป็น การตรวจสอบความถูกต้องเมื่ออ่าน หลังจากการตรวจสอบ RAID ตามรอบของ เซิร์ฟเวอร์ 7-piece Syzygy tablebase ทำได้ไม่ทัน
- เพื่อย้าย tablebase ขนาด 17TiB โดยไม่ต้องหยุดให้บริการเป็นเวลาหลายชั่วโมง จึงเตรียมเซิร์ฟเวอร์ใหม่ และทดสอบด้วยการเล่นซ้ำล็อกคำขอจริงในสภาพแวดล้อม RAM 32GiB, NVMe 2×201GiB และ HDD 6×5.46TiB
- จากการเล่นซ้ำ คำขอ 1 ล้านรายการ ที่บันทึกจากโปรดักชันด้วยไคลเอนต์ขนาน 12 ตัว พบว่าคอขวดหลักไม่ใช่เวลาตอบสนองเฉลี่ย แต่เป็น tail latency ที่ผู้ใช้รับรู้ได้
- ในด้านการใช้งานจริง
pread(2) ได้เปรียบกว่า mmap ทั้งในการจัดการข้อผิดพลาดและ tail latency ส่วน hint สำหรับการเข้าถึงแบบสุ่มอย่าง POSIX_FADV_RANDOM และ MADV_RANDOM มักให้ผลตรงกันข้าม
- นำ table prefix ไปไว้บน SSD ที่มีพื้นที่จำกัด และทำ parallelization ของ probe ภายในคำขอ เพื่อลดการเข้าถึงดิสก์ที่ช้า พร้อมตรวจสอบว่าการปรับปรุงใน benchmark ส่งผลต่อเวลาตอบสนองในโปรดักชันด้วยหรือไม่
เปลี่ยนจากการตรวจสอบ RAID ทั้งหมดมาเป็นการตรวจสอบเมื่ออ่าน
- เซิร์ฟเวอร์ 7-piece Syzygy tablebase ของ Lichess ทำการตรวจสอบความถูกต้องของ RAID ตามรอบให้เสร็จได้ยากในช่วงที่มีคำขอ tablebase จำนวนมาก
- การตั้งค่าใหม่ใช้ dm-integrity on LVM โดยไม่ตรวจสอบบล็อกข้อมูลทั้งหมดตามรอบ แต่จะ ตรวจสอบทุกครั้งที่มีการอ่าน บล็อก
- เพื่อย้าย tablebase ขนาด 17TiB โดยไม่ต้องหยุดให้บริการเป็นเวลาหลายชั่วโมง จึงตั้งค่าเซิร์ฟเวอร์ใหม่แยกต่างหาก
- ก่อนสลับจริง สามารถรัน benchmark แบบควบคุม กับ tablebase ทั้งหมดได้
- จากนั้นจึงสลับไปยังเซิร์ฟเวอร์ใหม่ และปลดระวางเซิร์ฟเวอร์เดิม
การตั้งค่าเซิร์ฟเวอร์ใหม่
- RAM ยังคงเป็น 32GiB เท่าเดิม
- พื้นที่จัดเก็บเพิ่ม NVMe 2×201GiB ซึ่งไม่มีในเซิร์ฟเวอร์เดิม และกันพื้นที่ที่เหลือของดิสก์ 476GiB ไว้สำหรับ OS และพื้นที่ทำงาน
- HDD เพิ่มจากเดิม 5 ลูกเป็น HDD 6×5.46TiB
- ระบบปฏิบัติการคือ Debian bookworm และเคอร์เนลอยู่ในตระกูล
Linux 6.1.0-21-amd64
- I/O scheduler เริ่มต้นถูกเลือกเป็น
none สำหรับ NVMe และ mq-deadline สำหรับ HDD
การตั้งค่าและการมอนิเตอร์ RAID 5
- RAID 5 เหมาะกับเซิร์ฟเวอร์ tablebase เพราะกู้คืนจากดิสก์เสีย 1 ลูกได้ และกระจายการอ่านแบบสุ่มไปยังดิสก์หลายลูกได้
- การตั้งค่าเริ่มต้นเป็นดังนี้
lvcreate --type raid5 --raidintegrity y --raidintegrityblocksize 512 --name tables --size 21T vg-hdd
- ประสิทธิภาพในการทดสอบช่วงแรกถือว่าใช้ได้ แต่หากไม่มีการมอนิเตอร์ อาจพลาดปัญหาที่ดิสก์บางลูกไม่ได้มีส่วนร่วมในระดับเดียวกัน
- หากละ
--stripes จะไม่ได้ใช้ physical volume ทั้งหมดเป็นค่าเริ่มต้น
- จำเป็นต้องมี การมอนิเตอร์กิจกรรมการอ่านแยกตามดิสก์ เพื่อจับการตั้งค่า RAID ที่ผิดพลาด
คอขวดที่เห็นจากล็อกคำขอจริง
- ภายใต้เงื่อนไขปกติ เซิร์ฟเวอร์ได้รับ 10–35 คำขอต่อวินาที
- บันทึก คำขอ 1 ล้านรายการ ในสภาพแวดล้อมโปรดักชัน แล้วให้ไคลเอนต์ขนาน 12 ตัวส่งคำขอเหล่านั้นตามลำดับในสถานการณ์ที่เลือก
- table จะถูกเปิดแบบ lazy open และแคชของแอปพลิเคชันกับ OS จะค่อย ๆ ถูกเติม
- ตัดเวลาตอบสนอง 800,000 รายการแรก ออกในฐานะช่วง warm-up
- จากนั้นวิเคราะห์เวลาตอบสนองของ 200,000 คำขอ ถัดมา
- เวลาตอบสนองเฉลี่ยเร็วพอแล้ว แต่ tail latency สูง จึงกลายเป็นจุดโฟกัสของการปรับแต่ง
- กราฟ ECDF แสดงสัดส่วนคำขอที่เร็วกว่าค่าเวลาตอบสนองแต่ละค่า โดยแกน x เป็นสเกลลอการิทึม
- ในกราฟ มีการบวก 30ms ให้กับเวลาตอบสนองแต่ละค่า เพื่อสะท้อน ping time 30ms ของไคลเอนต์
- เป็นการจัดการเพื่อไม่ให้แกน x แบบลอการิทึมเน้นความต่างไม่กี่มิลลิวินาทีในช่วงค่าต่ำมากเกินไป
pread(2) ได้เปรียบกว่า mmap
- shakmaty-syzygy ซึ่งเป็น implementation ของ Syzygy tablebase มีอินเทอร์เฟซที่เปลี่ยนวิธีเปิดไฟล์ table และวิธีอ่านได้
- ตัวเลือกหลักมีสองแบบ
mmap: map ไฟล์ table เข้าหน่วยความจำ และเมื่อเข้าถึงพื้นที่หน่วยความจำนั้น การอ่านดิสก์จะเกิดขึ้นแบบโปร่งใส
pread(2): ทำ system call ทุกครั้งที่อ่าน และรายงานข้อผิดพลาดการอ่านผ่านค่าที่คืนกลับมา
- หลังจาก
mmap แล้วไม่ต้องใช้ system call เพิ่ม แต่เพราะการอ่านดูเหมือนการเข้าถึงหน่วยความจำทั่วไป จึงต้องจัดการข้อผิดพลาดด้วยวิธี out-of-band เช่น signal
- ใน implementation ของเซิร์ฟเวอร์ แค่การจัดการข้อผิดพลาดที่ทนทานกว่าก็เพียงพอที่จะทำให้การใช้
pread สมเหตุสมผล และใน benchmark สำหรับสถานการณ์ที่สนใจ ประสิทธิภาพของ pread ก็ดีกว่า
- สาเหตุที่เป็นไปได้อย่างหนึ่งคือ เมื่อเข้าถึงบล็อกข้อมูลเดี่ยวที่ถูก memory-mapped แล้วข้ามขอบเขตของ page อาจทำให้เกิดการอ่านดิสก์สองครั้ง
- ไม่จำเป็นต้องนำ
pread ไปใช้กับเอนจินหมากรุกทันที
- การใช้ tablebase ในแมตช์ของเอนจินมักเกิดขึ้นเมื่อสามารถวาง WDL table ทั้งหมดไว้บนสตอเรจที่เร็วพอได้
- ในกรณีนี้ ช่วงเวลาตอบสนองทั่วไปเร็วถึงระดับที่ไม่ปรากฏบนกราฟนั้น และ memory mapping ที่ลด overhead ของ system call จะดีกว่า
hint การเข้าถึงแบบสุ่มที่ให้ผลตรงกันข้าม
posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM) และ hint ที่สอดคล้องกันสำหรับ memory map โดยรวมแล้วส่วนใหญ่ ให้ผลตรงกันข้าม
POSIX_FADV_RANDOM เป็น hint ที่บอก OS ว่าการเข้าถึงไฟล์เป็นแบบสุ่ม และ automatic read-ahead มีแนวโน้มไม่เป็นประโยชน์ เพื่อช่วยลดแรงกดดันต่อ page cache
- รูปแบบการเข้าถึง tablebase เมื่อผู้คนวิเคราะห์ endgame อาจไม่ได้สุ่มเท่าที่คาด
- ในเอนจินหมากรุก probe อาจกระจายไปยัง endgame ที่เป็นไปได้ต่าง ๆ มากกว่า ดังนั้นผลลัพธ์อาจแตกต่างกัน
table prefix ที่จะวางบน SSD พื้นที่จำกัด
- table probe จะเข้ารหัสตำแหน่งเป็นดัชนีจำนวนเต็มก่อน โดยอิงจากข้อมูลการเข้ารหัสใน table header
- จากนั้นต้องหาบล็อกข้อมูลแบบบีบอัดที่มีผลลัพธ์ของดัชนีนั้น
- Syzygy มี sparse block length list ที่ชี้ไปใกล้รายการที่ถูกต้อง จากนั้นจึงใช้ block length list เพื่อหาบล็อกข้อมูลที่เกี่ยวข้อง
- ขนาดของ table section เป็นดังนี้
| Table section |
WDL |
DTZ |
Total |
| Headers and sparse block length lists |
38GiB |
9GiB |
47GiB |
| Block length lists |
274GiB |
64GiB |
339GiB |
| Compressed data blocks |
8433GiB |
8458GiB |
16891GiB |
- อาจใช้พื้นที่ SSD เป็น adaptive cache layer เพื่อแคช hot list entry และ data block ได้
- หากเป้าหมายคือการลด tail latency การวาง sparse block length list และ block length list ไว้บน SSD จะเหมาะสม เพราะคำนึงถึงกรณีแย่สุด
- การจัดวางนี้สามารถจำกัดการอ่านดิสก์ช้าต่อ table probe ได้สูงสุด 1 ครั้ง ไม่ว่าจะเป็น hot หรือ cold
- สำหรับเซิร์ฟเวอร์นี้ พื้นที่ SSD ไม่พอทำ RAID 1 mirroring และเพราะเป็นการปรับแต่งแบบเลือกใช้ได้ จึงยอมไม่ใช้ redundancy และใช้ RAID 0
parallelization ของ probe ภายในคำขอ
- คำขอ tablebase ทั่วไปของเอนจินหมากรุกคือคำขอสำหรับค่า WDL เดี่ยว
- ในอินเทอร์เฟซผู้ใช้ ต้องการแสดง ค่า DTZ สำหรับทุกตาเดิน
- เมื่อรวมถึงการแก้ capture ภายใน Syzygy แล้ว คำขอเฉลี่ยจะก่อให้เกิด 23 WDL probe และ 70 DTZ probe
- implementation เริ่มต้นทำ parallelization ของการประมวลผลคำขอเอง แต่ probe ภายในแต่ละคำขอทำแบบลำดับ
- parallelism ที่ละเอียดขึ้นสร้าง overhead ในช่วง latency ต่ำ แต่ ลด tail latency ได้มาก
- แม้ดิสก์จะไม่สามารถประมวลผลการอ่านขนานจำนวนมากในเชิงกายภาพได้จริง I/O scheduler ก็มีโอกาสวางแผนการอ่านให้แต่ละคำขอเสร็จเร็วขึ้น
- วิธีนี้ช่วยให้จัดลำดับการเข้าถึงดิสก์ที่เกี่ยวข้องได้ดีขึ้น เพื่อลดเวลารอจนกว่าหัวอ่านดิสก์จะไปถึงเซกเตอร์ของคำขอถัดไป
การยืนยันในโปรดักชันและข้อมูลต้นฉบับ
- ตรวจสอบด้วยกราฟเวลาตอบสนองว่า การปรับแต่งจากสถานการณ์ benchmark ช่วยในโปรดักชันจริงหรือไม่
- ข้อมูลต้นฉบับเผยแพร่ไว้ที่ lila-tablebase-bench
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
Lichess เป็นบริการที่ทำให้ต้องหยุดชื่นชมเหมือนไวน์ดี ๆ เป็นสิ่งที่ยอดเยี่ยมมากสำหรับชุมชนหมากรุก และผมใช้ทุกวันพร้อมกับได้รับแรงบันดาลใจจากฟีเจอร์และประสิทธิภาพของมันอยู่เสมอ
ยิ่งน่าทึ่งเข้าไปอีกเมื่อรู้ว่าเป็นทีมขนาด 1~2 คน ที่มีงบประมาณจำกัด
ส่วนที่แสดง ECDF ที่บวก 30ms ให้กับเวลาตอบสนองแต่ละครั้งน่าสนใจมาก
การบวกค่าคงที่อาจดูเหมือนทำขึ้นแบบฝืน ๆ แต่จริง ๆ แล้วเป็นวิธีดูผลลัพธ์จากมุมมองของไคลเอนต์ที่มี ping 30ms และช่วยไม่ให้แกน x แบบสเกลลอการิทึมไปขยายความต่างไม่กี่ ms ในช่วงค่าต่ำมากเกินไป อาจเป็นเทคนิคมาตรฐานก็ได้ แต่ดูเหมือนทริกที่ค่อนข้างฉลาด
สงสัยว่าจำเป็นต้องลดต้นทุนจริง ๆ หรือเปล่า หรือมีเหตุผลอื่นที่ทำให้ใส่ SSD 20TB ลงในเครื่องเดียวแล้วจบไม่ได้ SSD 4TB ก็ราว ๆ 300 ดอลลาร์ และไดรฟ์ SFF ของ HP หรือ Dell ก็ไม่ได้แพงกว่านั้นมากนัก
น่าจะเป็นเพราะเขาสนใจการทดสอบและการเพิ่มประสิทธิภาพเองด้วย และถ้ามองจากมุมผลิตภัณฑ์ ก็คงเอาเวลาที่มีจำกัดไปใช้กับโปรเจกต์อื่นมากกว่า
องค์กรตั้งอยู่ในฝรั่งเศส ซึ่งไม่รู้ว่าส่งผลต่อต้นทุนอย่างไรบ้าง แต่ก็น่าพูดถึง
ถ้าจะหาเหตุผลให้การลงทุนเวลาคุ้มกว่านี้ ก็คงต้องเป็นโปรเจกต์ที่ประสบการณ์ผู้ใช้แย่กว่านี้ หรือเป็นกรณีองค์กรแสวงหากำไรที่ยอมรับว่ามีโอกาสหาเงินจากที่อื่นและไม่ค่อยสนใจความเจ็บปวดของลูกค้า
ดูเหมือนเป็นผลรวมของฮาร์ดแวร์ที่ทรงพลังและถูกเกินไป กับคนขี้เกียจที่อยากบอกว่า “วันนี้พอแค่นี้” เสียมากกว่า เขาก็พูดกันไม่ใช่หรือว่าให้ภูมิใจกับงานของตัวเอง
การเพิ่มประสิทธิภาพนี้มีตัวเลือกบางอย่างที่น่าสงสัย เหตุผลของการเพิ่มประสิทธิภาพคือมีกิจกรรม I/O มากเกินไปจน การตรวจสอบ RAID ทำไม่เสร็จ
จากบทความอย่างเดียว ยังไม่ชัดเจนว่าการตรวจสอบ RAID กับข้อมูล 17TiB เคยเสร็จจริงหรือไม่ แทนที่จะทำแบบนั้น เขาปิดการตรวจสอบ RAID ตามรอบ แล้วเปลี่ยนเป็นตรวจข้อผิดพลาดเป็นรายหน้าเมื่ออ่านข้อมูล ซึ่งสองวิธีนี้ไม่เหมือนกัน และถ้าเป็นข้อมูลสำคัญก็ควรใช้ทั้งคู่
ถ้าพบความเสียหายก็ต่อเมื่อพยายามอ่านข้อมูล ความเสียหายของข้อมูลเก่าอาจค้างอยู่ต่อไป และอาจเลยระยะเวลาเก็บสำรองจนกู้คืนต้นฉบับไม่ได้ นอกจากนี้ยังมีเรื่องที่เปลี่ยนไปใช้ RAID 0 ด้วย ซึ่งแม้จะเป็นตัวเลือกที่เร็วที่สุด แต่ก็เท่ากับเชื่อค่อนข้างมากว่าชุด NVMe นั้นจะรับโหลดแบบนั้นไหว
หวังว่าจะมีการสำรองข้อมูลไว้อย่างดี ทางออกที่ดีคือเปิดเซิร์ฟเวอร์ชั่วคราว กู้คืนแบ็กอัปและตรวจสอบข้อมูลทั้งหมด จากนั้นถ้าสำเร็จก็จะได้ตรวจยืนยันขั้นตอนแบ็กอัป/กู้คืนและความสมบูรณ์ของไฟล์ไปพร้อมกันด้วย ถึงอย่างนั้นก็ควรมีพื้นที่พอให้การตรวจสอบ RAID บนเซิร์ฟเวอร์หลักทำจนเสร็จ และไม่ควรใช้ RAID 0 ด้วยเหตุผลด้านประสิทธิภาพ
นี่เป็นชุดข้อมูลที่หาได้อย่างอิสระ เพียงแต่มีขนาดค่อนข้างใหญ่ https://en.wikipedia.org/wiki/Endgame_tablebase อธิบายได้ดีกว่า ดังนั้นจึงไม่ได้สำรองข้อมูลด้วย
lishogi ก็มีเหมือนกัน แต่ตอนนี้ยังมีขนาดเล็ก จึงยังไม่ถึงขั้นต้องใช้การเพิ่มประสิทธิภาพแบบนี้
ในบรรดาหมากรุกสายพันธุ์ต่าง ๆ โชงิน่าสนุกที่สุด ส่วนเซี่ยงฉีไม่ถึงขั้นนั้น
สงสัยว่า lichess หมายถึง lich ผู้หญิงหรือเปล่า แบบ baron/baroness อะไรทำนองนั้น
ถ้าพูดให้เคร่งครัด lich เพศชายคือ “werlich” ส่วน lich เพศหญิงคือ “wiflich” และรูปพหูพจน์เติม “-en” อย่างไรก็ตาม เพศมักไม่ค่อยเกี่ยวกับอันเดด ดังนั้นรูปกลางจึงถูกใช้มากอย่างท่วมท้น
“lichess” เป็นการผสมรากศัพท์เยอรมันกับฝรั่งเศสที่ประหลาด จนไม่สามารถแยกจากคำอื่น ๆ ในภาษาอังกฤษได้อย่างเป็นธรรมชาติ
แม้จะไม่ใช่การเปรียบเทียบที่ยุติธรรม แต่ คุณภาพวิศวกรรม ของทีม Lichess ทำให้ทึ่งจริง ๆ คู่แข่งหลักคุยเรื่องการย้ายไป GCP แต่ก็ยังเจอปัญหาล่มซ้ำ ๆ ตามความนิยมที่เพิ่มขึ้น และผมคิดว่ามีพนักงานมากกว่าประมาณ 100 เท่า
จุดอ่อนของ Lichess เคยเป็นแอปมือถือ แต่ v2 ที่ทำใหม่ด้วย Flutter แม้ยังเป็นเบต้า ก็ถือว่าดีมากแล้ว
และควรจำไว้ด้วยว่า Thibault รับค่าตอบแทนของตัวเองไม่ถึงปีละ 60,000 ดอลลาร์
แต่สงสัยว่าควรออกเสียง Lichess อย่างไร Lie chess หรือ Le chess หรือ League chess