การใช้ตัวเลือก `TCP_NODELAY` อย่างต่อเนื่อง
(brooker.co.za)- ปัญหาเรื่องความหน่วงในระบบกระจายมักถูกแก้ได้เพียงแค่เปิดใช้
TCP_NODELAYซ้ำแล้วซ้ำเล่า ซึ่งสะท้อนว่าพฤติกรรมพื้นฐานของ TCP อาจไม่สอดคล้องกับเวิร์กโหลดสมัยใหม่ - อัลกอริทึมของ Nagle ถูกสร้างขึ้นใน RFC896 ปี 1984 เพื่อลดต้นทุนส่วนหัวของแพ็กเก็ต TCP ขนาดเล็ก โดยทำงานด้วยการไม่ส่งเซกเมนต์ใหม่ก่อนจะได้รับ ACK
- เมื่อใช้ร่วมกับ delayed ACK ฝั่งหนึ่งจะรอ ACK ขณะที่อีกฝั่งรอข้อมูลตอบกลับหรือตัวจับเวลา ทำให้ไม่เหมาะกับแอปพลิเคชันแบบ pipeline ที่ไวต่อความหน่วง
- แม้ RTT ภายในดาต้าเซ็นเตอร์จะอยู่ที่ราว 500μs แต่เซิร์ฟเวอร์สมัยใหม่สามารถประมวลผลได้มากภายในเวลานั้น จึงไม่ชัดเจนว่าการเลื่อนการส่งออกไปหนึ่ง RTT จะให้ประโยชน์คุ้มค่าแค่ไหน
- ระบบกระจายสมัยใหม่มี TLS, การเข้ารหัส, การทำ serialization และขนาดข้อความระดับแอปพลิเคชันที่ใหญ่ขึ้น ทำให้ปัญหาแพ็กเก็ตขนาด 1 ไบต์ลดลง และในสภาพแวดล้อมที่ไวต่อความหน่วง การปิดใช้อัลกอริทึมของ Nagle ดูเป็นทางเลือกที่เป็นธรรมชาติมากกว่า
การตั้งค่าที่ควรดูก่อนเมื่อดีบักความหน่วง
- เมื่อเกิดปัญหาความหน่วงในระบบกระจาย มักจะตรวจดูก่อนว่าเปิดใช้
TCP_NODELAYอยู่หรือไม่ - นักพัฒนาระบบกระจายจำนวนมากเสียเวลากับปัญหาที่แก้ได้ด้วยตัวเลือกซ็อกเก็ตง่าย ๆ เพียงตัวเดียวนี้มานาน
- การที่เรื่องนี้เกิดซ้ำ ๆ บ่งชี้ว่าพฤติกรรมพื้นฐานของ TCP อาจไม่เหมาะกับระบบกระจายในปัจจุบัน หรืออัลกอริทึมของ Nagle เองอาจล้าสมัยแล้ว
ปัญหาที่อัลกอริทึมของ Nagle พยายามแก้
- RFC896 เป็นเอกสารปี 1984 ที่กล่าวถึงปัญหาแพ็กเก็ตขนาดเล็ก
- ในเวลานั้น เมื่อส่งข้อมูลที่เข้ามาทีละตัวอักษร เช่น การพิมพ์จากคีย์บอร์ด ผ่าน TCP จะเกิดความไม่มีประสิทธิภาพจากการที่ข้อมูล 1 ไบต์ มีส่วนหัว 40 ไบต์ ติดไปด้วยทุกครั้ง
- ข้อมูลจริง 1 ไบต์ต้องแบกรับส่วนหัว 40 ไบต์ คิดเป็น overhead 4000%
- แม้จะพอทนได้ในโหลดเบา ๆ แต่ไม่ดีต่อ throughput ของเครือข่าย
- เป้าหมายของอัลกอริทึมของ Nagle คือเพิ่ม throughput โดยเฉลี่ยต้นทุนส่วนหัวของ TCP ให้คุ้มขึ้น
- แพ็กเก็ตเล็กมักเกิดจากแอปพลิเคชันที่โต้ตอบกับมนุษย์อย่างเชลล์ หรือจากอิมพลีเมนเทชันที่ส่งข้อมูลเข้ามายังเคอร์เนลทีละน้อยผ่านการเรียก
writeหลายครั้ง
- แพ็กเก็ตเล็กมักเกิดจากแอปพลิเคชันที่โต้ตอบกับมนุษย์อย่างเชลล์ หรือจากอิมพลีเมนเทชันที่ส่งข้อมูลเข้ามายังเคอร์เนลทีละน้อยผ่านการเรียก
- หลักการสำคัญคือ หากข้อมูลที่ส่งไปก่อนหน้ายังไม่ได้รับ ACK จะยังไม่ส่งข้อมูลใหม่ออกไปทันทีเป็น TCP segment แยกต่างหาก
- มักมีการอธิบายอัลกอริทึมของ Nagle ควบคู่กับตัวจับเวลา แต่ตัว RFC896 เองไม่ได้ใช้ตัวจับเวลาแยกต่างหากนอกเหนือจาก round-trip time (RTT) ของเครือข่าย
ความหน่วงที่เกิดเมื่อรวมกับ delayed ACK
- delayed ACK คือการไม่ส่งการยืนยันรับแพ็กเก็ตทันที แต่รอจนกว่าจะมีข้อมูลจะส่งกลับหรือจนกว่าตัวจับเวลาจะหมด
- RFC813 เป็นเอกสารยุคแรกในปี 1982 ที่เสนอการหน่วง ACK โดยกล่าวถึงว่าสามารถตั้งตัวจับเวลาให้ผู้รับเลื่อนการส่ง ACK ไปก่อนแล้วค่อยส่งภายหลังได้ในบางสถานการณ์
- RFC1122 ทำให้ delayed ACK เป็นทางการมากขึ้น
- ฟีเจอร์ทั้งสองอย่างต่างมีเหตุผลในตัวเอง แต่เมื่อใช้ร่วมกันอาจสร้างความหน่วงได้
- อัลกอริทึมของ Nagle รอ การได้รับ ACK ก่อนจะส่งข้อมูลเพิ่ม
- delayed ACK เลื่อน การส่ง ACK ออกไปจนกว่าข้อมูลตอบกลับจะพร้อมหรือตัวจับเวลาหมด
- แม้จะช่วยให้เติมแพ็กเก็ตได้เต็มขึ้น แต่ไม่เหมาะกับแอปพลิเคชันแบบ pipeline ที่ไวต่อความหน่วง
- ความเห็นของ John Nagle บน Hacker News ก็มองว่าปัญหานี้ไม่ได้อยู่ที่การป้องกัน tinygram แต่เป็นการรวมกันของการหน่วง ACK กับตัวจับเวลาแบบคงที่
- นี่เป็นตัวอย่างที่ฟีเจอร์โปรโตคอลสองอย่างซึ่งต่างก็สมเหตุสมผล เมื่อนำมารวมกันกลับก่อให้เกิดพฤติกรรมที่ไม่ต้องการ และเป็นเหตุผลว่าทำไมการออกแบบโปรโตคอลจึงยาก
จุดที่ไม่สอดคล้องกับระบบกระจายสมัยใหม่
- ต่อให้ไม่มี delayed ACK พฤติกรรมของอัลกอริทึมของ Nagle ก็อาจยังไม่ตรงกับสิ่งที่ระบบกระจายสมัยใหม่ต้องการ
- ในสภาพแวดล้อมปัจจุบัน RTT เองก็เป็นต้นทุนที่มองข้ามไม่ได้
- RTT เดี่ยวภายในดาต้าเซ็นเตอร์โดยทั่วไปอยู่ที่ประมาณ 500μs
- RTT ระหว่างดาต้าเซ็นเตอร์ในรีเจียนเดียวกันอยู่ที่ระดับหลาย ms
- เส้นทางทั่วโลกอาจสูงถึงหลายร้อย ms
- เซิร์ฟเวอร์สมัยใหม่สามารถทำงานได้มากภายในเวลาเพียงไม่กี่ร้อย μs จึงยากจะบอกว่าการชะลอการส่งข้อมูลออกไปหนึ่ง RTT นั้นให้ประโยชน์ที่ชัดเจน
- เหตุผลดั้งเดิมของอัลกอริทึมของ Nagle คือการลด overhead ของส่วนหัว 40 เท่า ที่เกิดจากแพ็กเก็ตขนาด 1 ไบต์
- ฐานข้อมูลกระจายและระบบกระจายสมัยใหม่โดยทั่วไปไม่ได้ส่งแพ็กเก็ตขนาด 1 ไบต์
- ตัวข้อมูลที่แอปพลิเคชันส่งมีขนาดใหญ่กว่าเดิม
- มี overhead เพิ่มจากโปรโตคอลอย่าง TLS
- ยังมี overhead จากการเข้ารหัสและ serialization เพิ่มเข้ามาอีก
- ปัญหาที่ต้องหลีกเลี่ยงข้อความขนาดเล็กยังคงสำคัญ แต่ภาระนี้ได้ย้ายไปอยู่ที่ชั้นแอปพลิเคชันอย่างมีประสิทธิภาพแล้ว
- การส่งข้อมูลที่ห่อด้วย JSON ทีละ 1 ไบต์นั้นไม่มีประสิทธิภาพอยู่แล้ว ไม่ว่าจะมีอัลกอริทึมของ Nagle หรือไม่ก็ตาม
เหตุผลที่มอง TCP_NODELAY เป็นตัวเลือกตั้งต้น
- หากกำลังสร้างระบบกระจายที่ไวต่อความหน่วงบนฮาร์ดแวร์ระดับดาต้าเซ็นเตอร์สมัยใหม่ ก็สามารถเปิดใช้
TCP_NODELAYเพื่อปิดใช้อัลกอริทึมของ Nagle ได้ - เมื่อพิจารณาทราฟฟิกของระบบสมัยใหม่ องค์ประกอบของแอปพลิเคชัน และสมรรถนะของฮาร์ดแวร์ อัลกอริทึมของ Nagle อาจไม่จำเป็นอีกต่อไป
- จึงสามารถยืนในจุดยืนได้ว่า
TCP_NODELAYควรเป็นค่าเริ่มต้น - โค้ดที่เรียก
writeทุกไบต์อาจทำงานช้าลงภายใต้ค่าเริ่มต้นแบบTCP_NODELAY - หากประสิทธิภาพสำคัญ โค้ดลักษณะนั้นควรถูกแก้ที่อิมพลีเมนเทชันของแอปพลิเคชัน แทนที่จะพึ่งพาอัลกอริทึมของ Nagle
TCP_QUICKACK เป็นเพียงตัวเลือกเสริมมากกว่า
TCP_QUICKACKอาจถูกพูดถึงในฐานะทางเลือก แต่ด้วยข้อจำกัดด้าน portability และความหมายที่ค่อนข้างเฉพาะ จึงยากจะเลือกใช้เป็นอันดับแรก- ควรตรวจสอบความหมายจาก Linux tcp man page โดยตรง
- ปัญหาที่ใหญ่กว่าคือ
TCP_QUICKACKไม่ได้แก้ปัญหารากฐานที่เคอร์เนลอาจเก็บข้อมูลไว้นานเกินกว่าที่โปรแกรมตั้งใจ - ถ้าโปรแกรมเรียก
write()ก็ย่อมคาดหวังว่าwrite()จะถูกดำเนินการจริง
1 ความคิดเห็น
ความเห็นจาก Hacker News
ตลอดอาชีพการทำงานได้แก้ปัญหาความหน่วงที่เกิดจาก อัลกอริทึม Nagle มาหลายครั้งแล้ว จนตอนนี้มันกลายเป็นสิ่งแรกที่นึกสงสัย
ตัวตรรกะเองก็สมเหตุสมผล แต่ไม่เหมาะกับ workload บางประเภท และมองว่าควรให้นักวิศวกรเลือกอย่างชัดเจนตอนสร้าง socket แทนที่จะปล่อยให้เป็นค่าเริ่มต้นของระบบปฏิบัติการ
ปัญหาไม่ใช่ว่ามันเป็นออปชันที่ดีหรือแย่ แต่คือมีการตั้งค่าที่เปลี่ยนวิธีส่งข้อมูลอย่างค่อนข้างรุนแรงอยู่ ทั้งที่หลายคนไม่รู้ด้วยซ้ำว่ามันมีอยู่
จนถึงตอนนี้เจอบั๊กทุกครั้ง
ตัวอย่าง: https://cloud-haskell.atlassian.net/browse/DP-108 หรือ https://github.com/agentm/curryer/issues/3
แต่ผมไม่เห็นด้วยกับคำว่า “ไม่ใช่ออปชันดี/แย่”
มันคือ heuristic ฝั่ง kernel ที่มีไว้ “ซ่อมแบบมหัศจรรย์” ให้แอปพลิเคชันที่เขียนมาไม่ดี และอย่างที่บทความบอก แอปพลิเคชันที่ปกติไม่ควรทำ
write()system call เครือข่ายทีละ 1 ไบต์ซอฟต์แวร์แบบนั้นควรถูกแก้
กรณีที่ฟีเจอร์นี้ยังพอฟังขึ้นคือถ้าคุณเป็นผู้ดูแลระบบ kernel และอยู่ในสถานการณ์หายากที่แก้ซอฟต์แวร์บนเครื่องไม่ได้ด้วยเหตุผลอย่างการเมืองในทีม
นอกเหนือจากนั้น มันทำให้ซอฟต์แวร์ที่ปกติดีอยู่แล้วซับซ้อนขึ้น
หมายความว่าเราต้องปิดเวทมนตร์ประหลาดที่ใส่มาเพื่อเพิ่ม throughput ให้ซอฟต์แวร์ที่เขียนไม่ดีเพียงเล็กน้อยอย่างชัดเจน และสำหรับซอฟต์แวร์ที่เขียนมาถูกต้อง มันกลับสร้างความหน่วงใหญ่แบบคาดไม่ถึง
John Nagle บอกในเธรดที่ลิงก์จากที่นี่ว่า delayed ACK แย่กว่า ซึ่งผมเห็นด้วย
แต่รูปแบบ Send/Send/Receive ที่อัลกอริทึม Nagle ทำให้แย่ลงนั้นเป็นกรณีใช้งานที่สมเหตุสมผลและพบบ่อยมาก และครอบคลุมทุกอย่างที่ทำ pipeline RPC บน TCP
ผมคิดว่าทั้ง delayed ACK และอัลกอริทึม Nagle ควรถูกปิดไว้เป็นค่าเริ่มต้น
ชื่อก็ควรเป็นประมาณ TCP_DELAY และเปิดใช้เฉพาะตอนที่ไม่อยากทำ buffering พื้นฐานใน user space เองเท่านั้น
คนไม่ควรจำเป็นต้องรู้เรื่องแบบนี้ และพฤติกรรมค่าเริ่มต้นก็ควรเป็นแบบที่ไม่ทำให้แปลกใจ
writeแย่ ๆ ตัวเลือกเปิด TCP_DELAY ก็ดูประหลาดทีเดียวเพราะมันแปลว่าต้องการวิศวกรซอฟต์แวร์ที่ฉลาดพอจะรู้จักออปชันนี้ แต่ไม่ฉลาดพอจะแบ่งการเรียก
writeให้ดี หรือสร้าง buffering แบบ Nagle ที่เหมาะกับแอปของตัวเองได้ถ้าไม่มีอะไรอย่าง
io_uringมาชดเชยต้นทุนของ system call ฝั่ง user space จะทำงานได้ดีกว่าถ้าจำไม่ผิด นั่นคือแรงจูงใจหลัก
ข้อสรุปดูแปลก ๆ หน่อย อัลกอริทึม Nagle เป็นความพยายามทำ batching อย่างชัดเจน และไม่ว่าฮาร์ดแวร์ เครือข่าย แอปพลิเคชัน หรือ use case จะเป็นแบบไหน บางสถานการณ์ batching ก็ย่อมดีกว่า
ทุกวันนี้งานคอมพิวติ้งจำนวนมากก็ยังใช้ batching และแอปเครือข่ายก็ได้ประโยชน์
โปรโตคอลระดับสูงรุ่นใหม่กว่าอย่าง QUIC ก็ทำ batching การเขียน และแทบจะย้ายการจัดการการเชื่อมต่อและข้อผิดพลาดแบบอิสระของ TCP ขึ้นไปไว้ใน user space เพื่อให้โปรโตคอลดันข้อมูลเข้าแอปพลิเคชันได้เร็วที่สุด และให้ตัวแอปเป็นผู้รับผิดชอบการเชื่อมต่อและข้อผิดพลาดของแต่ละสตรีม แทนที่จะเป็น host TCP/IP stack หรือ router
ถ้าวันหนึ่งเครือข่ายกลับไปอิ่มตัวอีกแบบสมัยก่อน อัลกอริทึม Nagle ก็จะกลับมาในรูปแบบดัดแปลงของ QUIC และน่าจะอยู่ลึกลงไปในโค้ดแอปมากขึ้น เป็นลักษณะรอส่งแพ็กเก็ต QUIC จนกว่าจะถึงเกณฑ์บางอย่าง
ทุกอย่างในเทคโนโลยีจะถูกประดิษฐ์ซ้ำเมื่อฮาร์ดแวร์หรือซอฟต์แวร์ไปชนคอขวด เพราะประสิทธิภาพของทั้งสองฝั่งไม่ได้โตด้วยอัตราเท่ากัน สุดท้ายก็เลยเป็นแบบนั้นเสมอ
นอกจากแบนด์วิดท์แล้ว อัลกอริทึม Nagle ก็ยังมีประโยชน์ในกรณีที่อิ่มตัวด้วยจำนวนแพ็กเก็ตต่อวินาทีเพราะมีแพ็กเก็ตเล็ก ๆ มากเกินไป
มันทำให้สามารถเข้าถึงบริการผ่าน teletyper ทางกายภาพได้ แต่ก็ทำให้ TCP ไม่รู้ขอบเขตของข้อความ และถึงตอนนี้แม้จะพอยัดความรู้นั้นกลับเข้าไปได้บางส่วน ซอฟต์แวร์ยุคแรกก็ไม่ได้มีแบบนั้น
ในทางกลับกัน โปรโตคอลที่ไม่ใช่ TCP จำนวนมากอย่าง QUIC, SCTP และ TP4 ให้ขอบเขตข้อความอย่างชัดเจน
อินเทอร์เฟซกับระบบจึงไม่ใช่พอร์ตอนุกรมที่จำลองขึ้น แต่เป็นแบบอิงข้อความที่อย่างมากก็แค่ต้องประกอบกลับ
โปรโตคอลไม่มีบริบทมากพอจะทำ batching ได้อย่างถูกต้อง
ในทางกลับกัน จะปิด delayed ACK ดีไหม
ปัญหาเกิดจากพฤติกรรมผิดปกติที่เกิดเมื่อการป้องกันแพ็กเก็ตขนาดเล็กกับ delayed ACK มีปฏิสัมพันธ์กัน
ตัวเลือกที่เปิดเผยสำหรับปิดการป้องกันแพ็กเก็ตขนาดเล็กคือ
TCP_NODELAYแล้ว delayed ACK จะปิดอย่างไรได้บ้างเวลาที่อยาก benchmark ทั้งสี่แบบผสมเพื่อดูว่าแบบไหนเข้ากันได้ดีที่สุดน่ะ
ลองค้นดูนิดหน่อยพบว่า Linux มี socket option
TCP_QUICKACKแต่ต้องตั้งค่าใหม่ทุกครั้งที่มีการรับข้อมูลยังมี
/proc/sys/net/ipv4/tcp_delack_minและ/proc/sys/net/ipv4/tcp_ato_minด้วยส่วน FreeBSD มี
net.inet.tcp.delayed_ackและnet.inet.tcp.delacktimeTCP_QUICKACKแก้ปัญหาในรูปแบบที่แย่ที่สุดได้ แต่ไม่ได้แก้ปัญหาทั้งหมดNagle algorithm ยังอาจรอก่อนได้สูงสุดหนึ่งรอบเวลาไป-กลับก่อนส่งข้อมูล และถ้าทำตาม RFC ก็แทบไม่มีประโยชน์นอกจากเพิ่มความหน่วง
TCP_QUICKACKต้องตั้งทุกครั้งที่มีการรับข้อมูลนี่ เขาคิดอะไรกันอยู่จะอยากปิดไว้แค่บางช่วงเวลาทำไม
quickack 1ที่ท้าย route เพื่อปิด delayed ACK ของเส้นทางนั้นได้ในโลกที่แบนด์วิดท์มีจำกัด และขนาดต่ำสุดของแพ็กเก็ตคือ 64 ไบต์พร้อมทั้งยังต้องมี inter-frame gap การส่ง TCP packet ต่อหนึ่งไบต์ข้อมูลถือเป็นการสิ้นเปลืองแบนด์วิดท์อย่างมหาศาล
บนเครือข่าย Ethernet ส่วนใหญ่ ทุกวันนี้ขนาดต่ำสุดก็ยังเป็นแบบนั้น และการส่ง ACK เปล่า ๆ ก็เช่นกัน
แต่จุดยืนพื้นฐานของฉันคือ ไม่ใช่
TCP_NODELAYแต่เป็นแค่ TCPผมยังไม่ค่อยเชื่อกับตรรกะที่ว่า Nagle ไม่จำเป็นอีกต่อไป
Telnet อาจไม่สำคัญแล้วในปัจจุบัน แต่ก็น่าจะยังมีแอปพลิเคชันจำนวนมากที่ทำอะไรประมาณนี้อยู่
write(fd, "Host: "),write(fd, hostname),write(fd, "\r\n"),write(fd, "Content-type: ")เป็นต้นถึงจะไม่ใช่ overhead 40 เท่า แต่ก็น่าจะเป็นประมาณ 5 เท่าได้
เวลาจะเขียนลงไฟล์ก็คงไม่ทำแบบนี้แล้วคาดหวังประสิทธิภาพแบบปาฏิหาริย์ ทั้งที่ระบบปฏิบัติการก็มีบัฟเฟอร์ของมันเองอยู่แล้ว
ไม่มีเหตุผลที่จะคาดหวังต่างออกไปเวลาเขียนลง socket และแต่เดิม Nagle ก็ไม่ได้ช่วยคุณจาก overhead ของ system call อยู่แล้ว
ยืนยันได้ทั้งจากการอ่านโค้ดและจากการสังเกตพฤติกรรมด้วย
stracewrite(2)เล็ก ๆ ดูจะเป็นแนวทางเดียวที่สมเหตุสมผล ดังนั้นรูปแบบนี้คงไม่ได้พบบ่อยมากแล้วฝั่งเซิร์ฟเวอร์มักต้องใช้ asynchronous I/O เพื่อให้ scale ได้ดี และฝั่งไคลเอนต์เองการค้างรอ network call ก็เป็นประสบการณ์ที่ไม่ดี
โดยเฉพาะในทุกวันนี้ที่เครือข่ายเปลี่ยนแปลงบ่อยและหลุดนอกระยะได้ง่ายก็ยิ่งเป็นเช่นนั้น
ต่อให้ไม่นับเรื่องเครือข่าย system call ก็มีต้นทุนค่อนข้างสูง จึงส่งผลเสียต่อประสิทธิภาพ
สงสัยว่ามีวิธีที่ดีในการเปิด TCP_NODELAY ให้กับ socket เมื่อเราเข้าถึง source ของแอปพลิเคชันไม่ได้หรือไม่
หาไม่เจอทั้งการตั้งค่า kernel แบบถาวร หรือคำสั่งที่ใช้เปลี่ยนทีหลัง
แม้จะปิด delayed ACK ได้ด้วยการใส่
quickack 1ใน routing table แต่การเปิด TCP_NODELAY จากภายนอกแอปพลิเคชันดูยากเป็นพิเศษช่วงนี้ผมกำลังเจอปัญหาตามที่อธิบายไว้นี้แบบตรงตัว ระหว่างแอปพลิเคชันที่ผมเป็นเจ้าของกับแอปพลิเคชัน closed-source ที่มันโต้ตอบด้วย
socket(2)น่าจะใช้ได้ไหมคือเรียกฟังก์ชันจริงก่อน จากนั้นค่อย
setsockoptอะไรทำนองนั้น แล้วคืน socket ที่แก้ไขแล้วกลับไปจากเดิม
your_app —> serverก็ให้กลายเป็นyour_app -> localhost_socat -> serversocat มีตัวเลือก command line สำหรับตั้งค่า
tcp_nodelayแต่ก็ต้องเกลี้ยกล่อมให้แอป closed-source ไปเชื่อมต่อกับ localhost ให้ได้
ถ้ามันทำ DNS lookup ก็อาจบังคับให้เชื่อมต่อ localhost ได้ด้วย entry ใน
/etc/hostsแอปจะคุยกับ socat ผ่าน local socket ดังนั้น
tcp_nodelayฝั่งแอปจึงไม่ส่งผลptraceเพื่อเรียกsetsockoptได้ล่ะ/proc//fd/แล้วตั้งค่า socket option จากตรงนั้นอาจใช้ได้ แม้ผมจะยังไม่ได้ทดสอบLD_PRELOADราว ๆ 15 ปีก่อนผมเคยเล่น MMO ที่เน้นความเรียลไทม์มากเกมหนึ่ง และการสื่อสารทั้งหมดใช้ TCP
เวลากดปุ่มแล้วการกระทำของตัวเองยังไม่แสดงบนหน้าจอจนกว่าจะมี response packet กลับมา
สุดท้ายเด็ก ๆ ที่เล่นเกมนี้กัน รวมถึงผมเอง ต่างก็พบว่าถ้าเปิด TCP_NODELAY เกมจะลื่นขึ้นมาก
โดยเฉพาะผู้เล่นฝั่ง California ที่อยู่ใกล้กับเกมเซิร์ฟเวอร์จะเห็นผลชัดมาก
ผลข้างเคียงที่น่าสนใจคือ ก่อนเปลี่ยนถ้า TCP stream ค้าง เกมจะค้างไปพักหนึ่งก่อนจะเล่นเหตุการณ์รับเข้าที่พลาดไปแบบเร็วมาก
ซึ่งโดยมากก็คือฉากที่ผมตายนั่นเอง
แต่หลังเปลี่ยนมันกลับกลายเป็นว่าการเชื่อมต่อหลุดไปเลยแทน
ตอนของพอดแคสต์ Oxide and Friends ที่เกี่ยวข้อง: https://www.youtube.com/watch?v=mqvVmYhclAg
ถ้าใช้ภาษาสมัยใหม่ที่เปิด TCP_NODELAY เป็นค่าเริ่มต้นอย่าง Go ก็ไม่เข้าข่ายนี้ :-)
https://github.com/golang/go/issues/57530
ไม่เคยรู้มาก่อน
แค่ใช้ไลบรารีเครือข่ายที่ “ทันสมัย” ไม่ได้หรือ
ไม่ได้เป็นแบบนั้นทุกครั้ง บางทีก็เป็น DNS
ฝุ่นจากใกล้ไซต์ก่อสร้างไปเกาะในช่องว่างระหว่างเลเซอร์กับใยแก้วนำแสงของเราเตอร์ ทำให้สัญญาณถูกลดทอนลงมากพอจนเห็น packet loss 40~50%
หลังจากไล่หาจุดที่สูญหาย NOC ก็ส่งอีเมลไปหาผู้ให้บริการทรานสปอร์ตเจ้านั้น และหนึ่งวันต่อมาช่างเทคนิคที่ถูกส่งไปหน้างานก็ตอบกลับมาเล่าเรื่องนี้
แต่โดยปกติก็ยังแพตช์อ้อมได้ เลยไม่ใช่เรื่องใหญ่มาก
TCP_NODELAYหรือ stream bufferingWeb ซึ่งเป็นระบบที่ซับซ้อนมากก็ล้มเหลวได้เพราะแคชเหมือนกัน