55 GiB/s FizzBuzz (2021)
(codegolf.stackexchange.com)- High throughput Fizz Buzz ของ Code Golf Stack Exchange เป็นการทดลองด้านประสิทธิภาพที่แข่งกันว่าใครจะสร้างผลลัพธ์ FizzBuzz แล้วส่งผ่าน pipe ได้เร็วแค่ไหน โดยให้คะแนนจาก throughput ของ
<program> | pv > /dev/null - เอาต์พุตที่ใช้ได้ต้องเป็น ASCII ล้วน, ขึ้นบรรทัดใหม่ด้วย
\n, ใช้ Fizz/Buzz/FizzBuzz อย่างถูกต้อง, ต้องพิมพ์ต่อเนื่องอย่างน้อยถึง2^58หรือแทบจะไม่สิ้นสุด และห้ามใช้ กลเม็ด null byte ที่ทำให้pvตรวจจับได้เพียงอย่างเดียว - โค้ดระดับบน ๆ เลิกใช้
% 3,% 5, และลูปprintfแล้วหันไปใช้การคลี่แพตเทิร์น 15 บรรทัด, การจัดการตัวเลขความกว้างคงที่, บัฟเฟอร์ขนาดใหญ่, การทำงานขนาน, และ เอาต์พุตแบบ zero-copy เพื่อลดคอขวด - คำตอบ assembly ที่แสดงสำหรับ x86-64+AVX2 ตั้งอยู่บนสมมติฐานว่าใช้ Linux และ AVX2 โดยผสาน
vmsplice, L2 cache, huge page, และอินเทอร์พรีเตอร์ FizzBuzz bytecode เข้าด้วยกัน จนทำได้ราว 61GiB/s - ผลลัพธ์แกว่งมากตาม CPU, pipe buffer, เวอร์ชันของ
pv, CPU affinity, และการตั้งค่า mitigation ต่าง ๆ โดยบนเครื่องของผู้ตั้งคำถาม คะแนนสูงสุดที่สรุปไว้คือ C++ implementation ของ David Frank ที่ราว 1.7Tb/s
ขีดจำกัด throughput ที่ถูกไล่ด้วย FizzBuzz
- แก่นสำคัญไม่ใช่โจทย์ FizzBuzz เอง แต่คือการดูว่าในการสร้างข้อความที่ง่ายมาก ๆ นั้น ระหว่าง การคำนวณของ CPU, การคัดลอกหน่วยความจำ, pipe I/O, และขอบเขตเคอร์เนล อะไรจะกลายเป็นคอขวดก่อน
- ตัวอย่าง C แบบตรงไปตรงมาที่ใช้เป็นฐานอ้างอิงใช้
% 3,% 5, และprintfซึ่งบนเครื่องทั่วไปทำได้ราว 170MiB/s - ผู้ตั้งคำถามเคยเห็น implementation ที่ทำได้เกิน 3GiB/s บนเครื่องเดียวกัน และต้องการให้ชุมชนลองสำรวจเพดาน throughput ที่สูงกว่านั้น
- คะแนนวัดบนเดสก์ท็อปของผู้ตั้งคำถาม
- AMD 5950x, 16C/32T
- RAM 64GB 3200MHz
- ปิดใช้งาน CPU mitigations
- ตารางแยกตามภาษามี asm 60.8GiB/s, C 20.9GiB/s, Julia 15.5GiB/s, Go 6.8GiB/s, Java 5.8GiB/s, Rust 3.4GiB/s, Ruby 1.7GiB/s, Python 0.5GiB/s เป็นต้น
- คำตอบ x86-64+AVX2 assembly ของ ais523 มุ่งเป้าประสิทธิภาพสูงสุดแบบเธรดเดียว โดยทำได้ราว 31GiB/s บนเครื่องของผู้เขียน และราว 61GiB/s ในการสรุปผลของผู้ตั้งคำถาม
- C++ implementation ของ David Frank เป็นคะแนนสูงสุดปัจจุบันตามเนื้อหาของคำถามที่ราว 1.7 Terrabit/s และยังระบุว่า implementation C++20 อีกตัวหนึ่งทำได้ 283GB/s บน AMD Ryzen 9 7700X
กติกาเอาต์พุตและเงื่อนไขของเบนช์มาร์ก
- เงื่อนไขของเอาต์พุตที่ใช้ได้ค่อนข้างเข้มงวด
- เอาต์พุตต้องเป็น FizzBuzz ที่ถูกต้องเป๊ะ
- ASCII ต้องใช้ 1 ไบต์ต่ออักขระ
- อนุญาตให้ขึ้นบรรทัดใหม่ด้วย
\nเท่านั้น - ไม่อนุญาต
\r\n - เอาต์พุตต้องต่อเนื่องไปถึงค่าที่ใหญ่มากอย่างน้อย
2^58
- วิธีวัดเบนช์มาร์กเองก็ส่งผลต่อ throughput เช่นกัน
- มีการพูดถึงว่า
pvและ Linux pipe buffer ใช้บัฟเฟอร์ 64K เป็นค่าปริยาย - การจัดวาง sibling core ของ CPU อาจทำให้เส้นทาง L2 cache ระหว่างโปรแกรมที่สร้างข้อมูลกับโปรแกรมที่บริโภคข้อมูลต่างกัน
- สามารถใช้
tasksetบังคับการจัดวาง CPU เพื่อเปรียบเทียบได้
- มีการพูดถึงว่า
แนวทาง optimization ของ implementation แบบ assembly
- หัวใจของ implementation แบบ assembly คือการลด ต้นทุนการคัดลอกเอาต์พุตมากกว่าต้นทุนการคำนวณ
- หากใช้
writeจะมีต้นทุนการคัดลอกจาก user space ไป kernel space สูง - ผู้เขียนระบุว่าถ้าเปลี่ยนไปใช้
writeประสิทธิภาพจะตกเหลือหนึ่งในห้า vmspliceทำให้ pipe อ้างอิงบัฟเฟอร์ของโปรแกรมได้โดยตรง จึงลดการคัดลอก
- หากใช้
- การคำนวณ FizzBuzz แบ่งเป็น 3 ช่วง
- ช่วงแรก hardcode สตริงเริ่มต้นไว้
- ช่วงที่สองจัดการตัวเลข 2–5 หลักด้วยรูทีน AVX2 ที่ค่อนข้างตรงไปตรงมา
- ช่วงที่สามจัดการช่วง 6–18 หลักด้วยอินเทอร์พรีเตอร์ FizzBuzz bytecode
- ลูปหลักของช่วงที่สามตั้งเป้าสร้างเอาต์พุต 64 ไบต์ต่อ 4 clock cycle
- bytecode 1 ไบต์สร้างเอาต์พุต 1 ไบต์
- โหลด bytecode 32 ไบต์ แล้วใช้
vpshufb,vpsubbฯลฯ เพื่อสร้างเอาต์พุต 32 ไบต์ - หมายเลขบรรทัดคำนวณด้วยค่าประมาณและการแก้ไขจาก bytecode แทนการแปลงตัวเลขแบบทั่วไปในทุกบรรทัด
- implementation นี้พึ่งพาแพลตฟอร์มอย่างมาก
- ต้องใช้ Linux ที่ไม่เก่าเกินไป
- ต้องเป็นโปรเซสเซอร์ x86-64 ที่รองรับ AVX2
- หาก standard output ไม่ใช่ pipe จะขึ้น error ตั้งแต่เริ่ม
- ผู้เขียนระบุว่าในบางกรณีอาจให้เอาต์พุตผิดหากอยู่ในโครงสร้าง pipe ที่มี
spliceแทรกอยู่
กลยุทธ์ร่วมที่ implementation ภาษาอื่นเลือกใช้
- implementation ใน C, Go, Java, Rust, Python, Julia, Ruby, C# ก็วนกลับมาใช้แนวทาง optimization คล้ายกัน
- คลี่แพตเทิร์น FizzBuzz 15 บรรทัด
- ลดจำนวนครั้งของการแปลงตัวเลขเป็นสตริง
- รวมข้อมูลแล้วเขียนออกทีเดียวด้วยบัฟเฟอร์ใหญ่
- ทำงานสร้างข้อมูลแบบขนานด้วย thread หรือ goroutine
- ใช้ barrier, channel, queue, mutex เพื่อรักษาลำดับเอาต์พุต
- ผลลัพธ์นี้จึงมองเป็นการเปรียบเทียบ “ประสิทธิภาพของภาษา” แบบตรง ๆ ได้ยาก
- เวอร์ชันของ
pv, ขนาด pipe, การใช้vmspliceได้หรือไม่, CPU affinity, huge page, การ optimize ของคอมไพเลอร์, และการ inline ของmemcpyล้วนเปลี่ยน throughput ได้มาก - มีคอมเมนต์ว่าบางคำตอบเร็วมากบนบางเครื่อง แต่ไม่สามารถทำซ้ำตัวเลขเดิมได้ในสภาพแวดล้อมอื่น
- เวอร์ชันของ
ขอบเขตที่ไม่ได้ครอบคลุม
- ในบันทึกอินพุตมีการระบุว่ามี source chunk บางส่วนถูกตัดออกเพราะข้อจำกัดด้านความยาวและต้นทุน จึงไม่ได้ครอบคลุมคำตอบทั้ง 46 รายการและโค้ดหรือคอมเมนต์ทั้งหมดแบบครบถ้วน
- ส่วนที่ถูกละไว้ อาจรวมถึง submission รายภาษาบางส่วน, เนื้อหาโค้ดยาว ๆ, บันทึกการจูนอย่างละเอียด, และการถกเถียงในคอมเมนต์
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
สิ่งที่น่าประทับใจที่สุดคือ Linux สามารถทำให้ข้อมูลที่ถูก pipe จากโปรแกรมหนึ่งไปยังอีกโปรแกรมหนึ่งคงอยู่แค่ใน L2 cache ได้ โดยไม่ต้องแตะหน่วยความจำหลักเลย
มองว่านี่เป็นงานออกแบบสถาปัตยกรรมระบบที่น่าทึ่ง เพราะส่วนต่างๆ หลายส่วนของเคอร์เนล Linux ทั่วไปทำงานประสานกันจนเกิด fast path แบบนี้ได้
สงสัยว่าใน Mach port ของ Mac OSX หรือ Named Pipes ของ Windows จะทำแบบนี้ได้ไหม
สมกับชื่อผู้ใช้ “ais523 - high effort answers” จริงๆ เพราะถึงแม้จะเป็นคนที่รันโปรแกรมไม่ได้ ก็ยังเขียนคอมเมนต์ที่ลงแรงมาก และข้อสรุปคือ
“โปรแกรมน่าจะถูกคอมไพล์โดยบังเอิญในสถานะที่เปิด ASLR อยู่ ในกรณีนี้ dynamic linker จะไม่เคารพการจัดแนว 4MiB ของ BSS segment ทำให้
.alignของผมถูกมองข้ามไปในทางปฏิบัติ และนั่นน่าจะเป็นสาเหตุของบั๊ก”ทุกครั้งที่บทความนี้ถูกดันกลับขึ้นมา ผมจะกลับมาหัวเราะกับคอมเมนต์นี้เสมอ
“@chx: ผมมีวิทยานิพนธ์ปริญญาโทอยู่แล้วนะ อันนี้ยากกว่าอีก – ais523 - high effort answers Oct 29, 2021 at 1:17”
ลองทำเวอร์ชันง่ายๆ แบบไม่ optimize ด้วย Rust, Python3 และ C โดยใช้แค่ if/else/while ธรรมดากับการพิมพ์ออกมาตรฐาน
Rust -> 23.2MiB/s
Python3 -> 28.6MiB/s
C -> 238MiB/s
สงสัยว่ามีใครรู้ไหมว่าทำไม ประสิทธิภาพของ Rust ถึงไปอยู่ในช่วงใกล้กับ Python3 ทั้งที่คิดว่าน่าจะใกล้ C มากกว่า
ถ้าจะให้ได้ประสิทธิภาพใกล้ C ก็น่าจะต้องจัดการล็อกนี้เอง
let mut lock = stdout().lock();write!(lock, "hello world").unwrap();และต้องตั้งค่า ขนาดบัฟเฟอร์ ของ standard output ให้เท่ากับของ C ด้วย
https://ismailmaj.github.io/tinkering-with-fizz-buzz-and-con...
ในโปรแกรม C ที่นับเลขโดยหน่วงเวลา 1ms คอลัมน์ที่สองคือเวลาที่ผ่านไปนับจาก
read()ครั้งก่อน$ ./out | rtss4.7ms 4.7ms | 14.7ms | 24.7ms | 34.7ms | 44.8ms exit status: 0จะเห็นว่ามันเขียนออกมาทีเดียวทั้งหมด ถ้าจัดสรรเทอร์มินัลให้ก็จะออกมาทีละบรรทัด
$ rtss --pty ./out0.8ms 0.8ms | 11.9ms 1.1ms | 23.0ms 1.1ms | 34.1ms 1.1ms | 44.3ms exit status: 0Rust ไม่มีพฤติกรรมแบบปรับตามสภาพนี้สำหรับ output จึงทำงานเหมือนผลลัพธ์แบบที่สองเสมอ ไม่ว่าจะเป็นเทอร์มินัลหรือไม่ก็ตาม
ในเชิงเทคนิค standard output จะถูกครอบด้วย LineWriter(https://doc.rust-lang.org/std/io/struct.LineWriter.html) เสมอ และถ้าเจอการเขียนที่มีขึ้นบรรทัดใหม่ก็จะ flush ทันที ถ้าต้องการ throughput สูงสุดก็ควรครอบด้วย BufWriter เพื่อเขียนหลายบรรทัดรวมกัน
--releaseและ C ต้องคอมไพล์ด้วย-O3ทำให้อดสงสัยไม่ได้ว่าถ้าทุกอย่างเขียนด้วยแอสเซมบลีทั้งหมดจะเร็วได้แค่ไหน
ในงานพัฒนาเสียง การเขียน โค้ด DSP ด้วยแอสเซมบลีเป็นเรื่องที่พบได้บ่อยมาก
ต้องอาศัยการวิจัยอัลกอริทึมแบบสุดขั้ว ความรู้เชิงลึกเกี่ยวกับ Linux system call และการปรับแต่งเฉพาะแพลตฟอร์มรวมกันจึงจะทำได้ ผู้เขียน Alex Smith พูดไว้ว่า:
“@chx: ผมมีวิทยานิพนธ์ปริญญาโทอยู่แล้ว งานนี้ยากกว่านั้นอีก”
มันเป็นคนละระดับกับสิ่งที่สรุปง่าย ๆ ว่า “แค่ทำด้วยแอสเซมบลีก็พอ” โดยสิ้นเชิง
“โอเค ผมเรียนอยู่ที่ University of Washington แล้วก็ไปทำงานที่บริษัทชื่อ Geoworks และเขียนโปรแกรมภาษาแอสเซมบลีอยู่ 5 ปี พวกเราที่ Geoworks เขียนทั้งระบบปฏิบัติการ ไลบรารี ไดรเวอร์ แอป ทุกอย่างของเดสก์ท็อประบบปฏิบัติการด้วยแอสเซมบลีทั้งหมด เป็น 8086 assembly! แถมไม่ใช่แอสเซมบลีที่ดีด้วย! มีรีจิสเตอร์แค่สี่ตัว! ถ้านับถึง 386 ก็มีรีจิสเตอร์ si ด้วย แต่มันน่ากลัวมาก
ที่จริงพวกเราค่อนข้างชอบมันนะ เพราะมันเป็น object-oriented assembly นี่แหละเรื่องน่าขันจริง ๆ ของทั้งหมดนี้ ว่าคนเราสามารถโน้มน้าวตัวเองให้ชอบอะไรสักอย่างได้มากแค่ไหน สำหรับพวกเรา C++ คือความเสื่อมโทรมแบบโรมันขั้นสุด ราวกับไปอาเจียนเพื่อจะได้กลับมากินต่อ พวกเขามี IF! ส่วนพวกเรามี jump CX zero! พวกเขามี ‘object’! พวกเราก็มีเหมือนกัน แต่พวกเขามีไวยากรณ์ไว้รองรับ มันดูอ่อนแอมาก และตอนนั้นเรารู้ว่าเราสามารถสร้างโค้ดที่เร็วกว่าคอมไพเลอร์ไหน ๆ ได้ และเราก็ทำได้จริง!
แล้วเกิดอะไรขึ้นล่ะ? บริษัทล้มละลาย ทำไมเหรอ? เรื่องนี้ผมน่าจะเห็นต่างจากทุกคนที่ Geoworks และผมก็รู้ว่าจริง ๆ แล้วอาจมีแค่ผมคนเดียวที่เชื่อแบบนี้ แต่เหตุผลก็คือเราเขียนภาษาแอสเซมบลี 8086 ไป 15 ล้านบรรทัด เรามีเครื่องมือที่ยอดเยี่ยม ระดับโลกเลย เชื่อผมเถอะ ของแบบนั้นจำเป็นมาก แต่เมื่อถึงจุดหนึ่ง...
ปัญหาคือ ลองนึกภาพมดตัวหนึ่งพยายามเดินเป็นเส้นตรงข้ามพื้นโรงรถดู มันเดินเป็นเส้นตรงไม่ได้ เรามองเห็นภาพกว้างกว่าเลยรู้ มดจะเดินวนไปมา ปรับให้เหมาะเฉพาะจุดกับก้อนหินก้อนนี้ แล้วค่อยไปทางนั้น
ตอนเราเขียนระบบภาษาแอสเซมบลีขนาดยักษ์ก็เป็นแบบนั้น สุดท้าย Microsoft ก็ออกแพลตฟอร์มอุปกรณ์พกพาที่เร็วกว่าเรามาก ผมเข้าไปพร้อมดีบักเกอร์แล้วคิดว่า ‘อะไรกันเนี่ย? ทำไมเป็นแบบนี้? การเรนเดอร์ช้ามาก กระตุกสุด ๆ’ พอเข้าไปดูพบว่าทุกครั้งที่รีเฟรชหน้าจอ title bar บางอันถูกเรนเดอร์ซ้ำ 140 ครั้ง และไม่ได้มีแค่ title bar เท่านั้น ทุกอย่างถูกเรียกหลายรอบหมด
เพราะเราไม่สามารถมองเห็นได้อีกต่อไประบบทำงานอย่างไร!
ระบบที่เล็กไม่เพียงแค่ปรับแต่งได้ง่ายกว่า แต่ยังปรับแต่งได้จริงด้วย หมายความว่าสามารถทำ global optimization ได้”
http://steve-yegge.blogspot.com/2008/05/dynamic-languages-st...
ทุกวันนี้เว็บไซต์และซอฟต์แวร์ราว 99% น่าจะทำให้เร็วขึ้นได้อย่างน้อย 50% ด้วยการเปลี่ยนแปลงพื้นฐานง่าย ๆ ถ้าใส่ใจกับประสิทธิภาพของแอปพลิเคชันสักหน่อย เช่น การทำ caching ให้ถูกต้อง การปรับแต่ง asset การแทนที่ third-party library ที่เทอะทะด้วย native call พื้นฐานที่ทำงานเดียวกัน และการตั้งค่าเซิร์ฟเวอร์กับฐานข้อมูลให้เหมาะสม
อีกไม่กี่ปี AI อาจให้การ optimize แบบ one-click กับ repository ได้ โดยนำ best practices มาใช้ หรือไม่ก็เขียนโค้ดเดิมใหม่เป็นแอสเซมบลีที่เร็วกว่าให้เลย
ข้อจำกัดของทรัพยากร สามารถทำให้เกิดความชัดเจนของโฟกัสได้
แม้การทดลองนี้จะทั้งสนุกและให้ความรู้ แต่ก็ดูมีข้อบกพร่องอยู่เล็กน้อย แทนที่จะวัดว่าแก้ปัญหาที่ซับซ้อนได้เร็วแค่ไหน มันกลับทดสอบปัญหารอบข้างเป็นหลัก คือประสิทธิภาพในการดึงหน่วยความจำออกจากโปรเซสหนึ่งแล้วส่งต่อไปยังอีกโปรเซสหนึ่ง
เลยดูเหมือนว่าโปรเซสที่สองกำลังเขียนลงคอนโซลหรือไฟล์อยู่ตลอด แต่ในทางเทคนิคแล้วไม่ใช่แบบนั้น การรัน
pv >/dev/nullโดยแก่นแท้แล้วแทบจะไม่ทำอะไรเลย และ system callwriteก็คืนค่ากลับแทบจะทันทีvmspliceเป็นความสามารถคล้าย shared memory ที่ทำให้อีกโปรเซสเข้าถึงบัฟเฟอร์/หน่วยความจำของโปรเซสหนึ่งได้ เป็นไปได้มากว่าข้อกำหนดการแข่งขันช่วงแรกจะกำกวม จึงดูไม่ชัดเจนว่าตามกติกาถือว่าใช้ได้หรือไม่“เขียนโปรแกรม FizzBuzz รันมัน แล้ว pipe เอาต์พุตไปที่
| pv > /dev/nullยิ่ง throughput สูงก็ยิ่งทำได้ดี”“เอาต์พุตของโปรแกรมต้องเป็น fizzbuzz ที่ถูกต้องทั้งหมด ห้ามโกงด้วยการแทรก null byte คั่นระหว่างเอาต์พุตที่ถูกต้อง เช่น null byte ที่มองไม่เห็นบนคอนโซลแต่ไปถูกนับใน throughput ของ pv”
และ
vmsplice(2)ก็สร้าง byte stream ลงบน stdout pipe จริง ๆ โดยที่pv(1)สามารถ splice มันไปยัง/dev/nullหรือcat(1)จะคัดลอกมันไปยังเทอร์มินัลก็ได้ไม่ใช่มีแค่ผลงานชิ้นนี้ที่ใช้
vmsplice(2)ผู้ส่งผลงานคนอื่นก็พบเช่นกันว่านี่ไม่ใช่วิธีวิเศษอะไร แม้จะข้ามกำแพง I/O ไปได้แล้ว ก็ยังเหลืองานอีกมากในการสร้างหน้าข้อมูลเอาต์พุตให้เร็วที่สุดเท่าที่จะทำได้โค้ดส่วนใหญ่ติดคอขวดที่ หน่วยความจำและ I/O ปัญหาที่ซับซ้อนส่วนมากก็มักติดที่ความเร็วในการย้ายข้อมูลจากที่หนึ่งไปอีกที่หนึ่ง ไม่ค่อยติดที่การคำนวณข้อมูล แม้ในฐานะคนที่ใช้เวลาทั้งวันกับการปรับแต่ง GPU assembly ก็ยังพบว่า ต่อให้มีกรณีที่การคำนวณเป็นคอขวดจริง ๆ ซึ่งก็เกิดไม่บ่อย พอปรับแต่งส่วนนั้นเสร็จแล้ว สุดท้ายหน่วยความจำก็จะกลายเป็นคอขวดอยู่ดี
ตัวอย่างเช่น มีการแทนค่าด้วยบิตอย่างชาญฉลาดที่ทำให้การทดในเลขฐานสิบเกิดขึ้นได้อย่างเป็นธรรมชาติ
ข้อกำหนดการแข่งขันช่วงแรกก็ไม่ได้กำกวมอะไรนักในประเด็นนี้ เพราะระบุชัดให้วัด throughput ด้วย
| pv > /dev/nullและยังบอกไว้ด้วยว่า:“อนุญาตการปรับแต่งเฉพาะสถาปัตยกรรม / assembly ด้วย นี่ไม่ใช่การแข่งขันจริงจังอะไร แค่อยากเห็นว่าผู้คนจะดัน fizz buzz ไปได้สุดแค่ไหน แม้มันจะทำงานได้เฉพาะในสภาพแวดล้อม/แพลตฟอร์มพิเศษก็ตาม”
การเข้าใจวิธีจัดการ memory I/O และ file I/O ให้มีประสิทธิภาพ เป็นทักษะที่เกี่ยวข้องกับทุกโปรแกรมและโปรแกรมเมอร์
มีการบอกให้บันทึกเป็น
fizzbuzz.Sเลยสงสัยว่าความต่างระหว่างนามสกุล .S กับ .s คืออะไรตามคู่มือ:
file.sโค้ดแอสเซมเบลอร์
file.Sfile.sxโค้ดแอสเซมเบลอร์ที่ต้องผ่าน preprocessing
.S) หรือไม่ (.s)ไม่แน่ใจว่าใน toolchain สมัยใหม่ยังมีความต่างอยู่อีกไหม
.Sใช้กับไฟล์แอสเซมบลีที่มนุษย์เขียนเอง ซึ่งมักถูกติดตามใน git ส่วน.sใช้กับแอสเซมบลีที่เครื่องสร้างขึ้นและเขียนทับได้ถ้าจำเป็น.Sแต่ถ้าคุณสั่งให้สร้างแอสเซมบลีออกมา (เช่นgcc -S xyz.c) มันจะเขียนทับ.sโพสต์ก่อนหน้า:
https://news.ycombinator.com/item?id=29031488
https://news.ycombinator.com/item?id=29413656
ตอนแรกผมอ่านเป็น FritzBox 55 GiB/s FritzBox เป็นเราเตอร์ที่ได้รับความนิยมในยุโรปแถบประเทศที่ใช้ภาษาเยอรมัน
ISP ของผมก็เพิ่งทวีตเมื่อสัปดาห์ที่แล้วเกี่ยวกับกล่อง OPNSense ที่รองรับ 60 GiB/s ซึ่งจะเปิดให้ใช้เร็ว ๆ นี้[1]
[1] https://twitter.com/init7/status/1674920410889043973
ผมยังเสียบ USB WiFi adapter ไว้ด้วย ดังนั้นถ้าสายเคเบิลล่ม VLAN บางตัวจะ fail over ได้ และใช้โทรศัพท์มือถือเพื่อรักษาการเชื่อมต่อสำคัญสำหรับงานเอาไว้
มันไม่ถูก แต่ถ้าคุณอยากสนับสนุนโครงการ OPNSense ทางการเงินพร้อมกับได้ฮาร์ดแวร์ดี ๆ อุปกรณ์ของ Deciso ก็นึกข้อโต้แย้งยากมาก มันประหยัดพลังงาน ใช้ชิ้นส่วนที่เน้นความทนทาน และใช้งานได้ดีเฉย ๆ
ดีใจที่ได้เห็นไลน์ผลิตภัณฑ์เชิงพาณิชย์แข็งแกร่งขึ้น