2 คะแนน โดย GN⁺ 2024-01-05 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • One Billion Row Challenge (1BRC) ที่จัดขึ้นตลอดเดือนมกราคม 2024 คือชาเลนจ์ด้านประสิทธิภาพที่แข่งขันกันว่า Java จะทำงานได้เร็วแค่ไหนในการประมวลผลไฟล์ข้อความ 1 พันล้านแถว
  • อินพุตเป็นข้อความธรรมดาในรูปแบบ station;temperature แต่ต้องคำนวณ อุณหภูมิต่ำสุด·เฉลี่ย·สูงสุด ของแต่ละสถานี และแสดงผลอย่างถูกต้องตามลำดับชื่อ
  • อนุญาตให้ใช้ได้เฉพาะ Java โดยสามารถใช้ดิสทริบิวชันจาก SDKMan และ Early Access build จาก openjdk.net ได้ แต่ห้ามใช้ external dependency
  • ผู้เข้าร่วมส่งผลงานผ่าน pull request ไปยัง รีโพซิทอรี 1brc บน GitHub และสามารถใช้ implementation พื้นฐานที่มีให้เพื่อเทียบรูปแบบคำตอบและประสิทธิภาพได้
  • การประเมินใช้สภาพแวดล้อม Hetzner Cloud CCX33 เดียวกัน โดยรัน 5 ครั้งแล้วตัดสถิติที่ช้าที่สุดและเร็วที่สุดออก ก่อนนำค่าเฉลี่ยของ 3 ครั้งที่เหลือมาจัดอันดับบนลีดเดอร์บอร์ด

โจทย์ Java สำหรับสรุปผล 1 พันล้านแถวให้เร็วที่สุด

  • One Billion Row Challenge เป็น ชาเลนจ์ด้านประสิทธิภาพของ Java ที่จัดขึ้นตั้งแต่วันที่ 1 มกราคมถึง 31 มกราคม 2024
  • ผู้เข้าร่วมต้องเขียนโปรแกรม Java ที่อ่านค่าการวัดอุณหภูมิจากไฟล์ข้อความ แล้วคำนวณ อุณหภูมิต่ำสุด·เฉลี่ย·สูงสุด ของแต่ละสถานีตรวจอากาศ
  • แก่นของความยากคือไฟล์อินพุตมีขนาด 1,000,000,000 แถว
  • อินพุตมีโครงสร้างเรียบง่าย โดยหนึ่งแถวมีค่าการวัดหนึ่งรายการ
    • ตัวอย่าง: Hamburg;12.0
    • ตัวอย่าง: Bulawayo;8.9
    • ตัวอย่าง: Palembang;38.8
  • เอาต์พุตต้องเรียงชื่อตามลำดับตัวอักษร และแสดงค่า min/mean/max ของแต่ละสถานี
    • ตัวอย่าง: {Abha=5.0/18.0/27.4, Abidjan=15.7/26.0/34.1, ...}

กติกาการส่งและสภาพแวดล้อมการรัน

  • เป้าหมายคือการสร้าง implementation Java ที่เร็วที่สุด สำหรับงานเดียวกัน
  • การปรับแต่งสามารถใช้ virtual thread, Vector API และ SIMD, การปรับแต่ง GC, AOT compilation เป็นต้น
  • กติกาพื้นฐานมีดังนี้
    • ผลงานที่ส่งต้องเขียนด้วย Java
    • สามารถใช้ดิสทริบิวชัน Java จาก SDKMan และ Early Access build จาก openjdk.net ได้
    • อนุญาตให้ใช้ EA build ของโปรเจกต์ OpenJDK อย่าง Valhalla ได้ด้วย
    • ห้ามใช้ external dependency
  • ผู้เข้าร่วมต้องโคลน รีโพซิทอรี 1brc และส่ง implementation ตามคำแนะนำใน README
  • มี implementation พื้นฐาน ให้ใช้เป็นเกณฑ์เปรียบเทียบและตรวจสอบรูปแบบคำตอบ
  • การส่งผลงานทำโดยเปิด pull request ไปยัง upstream repository

วิธีคำนวณลีดเดอร์บอร์ดและการแบ่งปันในชุมชน

  • การประเมินดำเนินการบนอินสแตนซ์ Hetzner Cloud CCX33
    • สเปกคือ 8 dedicated vCPU, 32 GB RAM
    • ใช้โปรแกรม time วัดเวลาแบบ end-to-end
    • ผลงานแต่ละชิ้นจะถูกรันต่อเนื่อง 5 ครั้ง
    • ตัดผลการรันที่ช้าที่สุดและเร็วที่สุดออก
    • นำค่าเฉลี่ยเวลาของ 3 ครั้งที่เหลือมาเป็นผลลัพธ์ของผลงานนั้น
    • ผลลัพธ์จะถูกเพิ่มเข้า leaderboard
  • การพูดคุยเรื่องเทคนิคการปรับแต่งดำเนินต่อใน discussion ของรีโพซิทอรีบน GitHub
  • ยังมี Show & Tell สำหรับแชร์ implementation ที่เขียนด้วยภาษาอื่นนอกเหนือจาก Java ด้วย โดยมีการแชร์ 1BRC เวอร์ชัน Rust, Go, C++ เป็นต้น

2 ความคิดเห็น

 
GN⁺ 2024-01-05
ความคิดเห็นบน Hacker News
  • โซลูชันที่ดูเหมือนจะมีประสิทธิภาพดีที่สุดในตอนนี้ [0] ไม่ได้คำนึงถึง การชนกันของแฮช ดังนั้นถ้ามีเมืองต่างกันมากพอในชุดข้อมูล ก็น่าจะให้ผลลัพธ์ผิดได้
    สงสัยว่าผมพลาดอะไรไปหรือเปล่า
    [0] https://github.com/gunnarmorling/1brc/blob/main/src/main/jav...

    • ถูกต้อง เมื่อวานมีการพบปัญหานี้ และจริง ๆ แล้วมีสองโซลูชันที่พึ่งพาฟังก์ชันแฮชที่ปรับให้เข้ากับชุดข้อมูลเฉพาะ ซึ่งละเมิดกฎที่ว่า ต้องทำงานได้กับชื่อสถานีตรวจวัดทุกชื่อ แต่หลุดรอดไประหว่างการประเมิน
      ตอนนี้ได้ลบรายการเหล่านั้นออกจากตารางอันดับแล้ว และผู้เขียนทั้งสองกำลังแก้ไข submission ของตน จึงจะถูกเพิ่มกลับเข้ามาในภายหลัง
      [0] https://twitter.com/mtopolnik/status/1742652716919251052
  • ผมคิดว่าแนวทางต่อไปนี้น่าจะประมวลผลทั้งหมดได้ภายใน 0.3 วินาที
    อุณหภูมิมีทศนิยมหนึ่งตำแหน่ง ดังนั้นโดยทั่วไปค่าประมาณ 400 ค่าก็น่าจะพอ และชื่อสถานที่ก็มีจำนวนจำกัดประมาณ 400 ชื่อ จึงสามารถสร้างตาราง lookup สำหรับชุดค่าผสม อุณหภูมิ×ชื่อสถานที่ ได้ประมาณ 160,000 รายการ
    สร้าง state machine อัตโนมัติที่แม้ 160,000 รายการนี้จะอยู่ในตำแหน่งหมุนใด ๆ ภายในรีจิสเตอร์ 4 ไบต์ ก็จะ map ไปยัง bucket เฉพาะใน hash table ได้ และในรีจิสเตอร์สถานะ 32 บิต ให้ทำการ lookup ตารางเปลี่ยนสถานะและ XOR กับ 4 ไบต์ถัดไปในทุก cycle
    จากนั้นก็ไล่กวาดข้อมูลทั้งหมดด้วยความเร็วของหน่วยความจำและเพิ่ม counter ตามสถานะได้เลย และเพราะมีสถานะแค่ 65K counter จึงอยู่ใน cache ได้
    ถ้าเป็น AVX512 จะสามารถรัน state machine แบบ 32 บิตแบบนี้ได้ขนานกัน 512 ตัวต่อคอร์ ดังนั้นการคำนวณไม่น่าจะเป็นคอขวด
    อุณหภูมิที่สูง/ต่ำหรือชื่อสถานที่ที่ไม่รู้จักซึ่งไม่ map ไปยัง bucket ที่ถูกต้อง ก็ส่งต่อไปยังโค้ดช้า และการจัดการค่าต่ำสุด/สูงสุดก็ทำผ่าน escape แบบนี้ได้ ซึ่งจะเกิดขึ้นแค่ไม่กี่พันครั้ง
    วิธีนี้สามารถทำงานด้วยความเร็วของหน่วยความจำได้ด้วยคอร์เดียวที่ใช้ AVX512 ผมจึงคิดว่าไม่มีประโยชน์ที่จะกระจายไปหลายคอร์

    • ไม่จำเป็นต้องมีตาราง lookup สิ่งที่ต้องการมีแค่ค่าต่ำสุด/ค่าเฉลี่ย/ค่าสูงสุด ดังนั้นคำนวณทั้งหมดได้ด้วย การวนอ่านครั้งเดียว โดยไม่ต้องเก็บข้อมูล
      สิ่งที่ต้องมีคือ hash table ขนาด 400 รายการ, ค่าทศนิยม 3 ค่า ได้แก่ ต่ำสุด·เฉลี่ย·สูงสุดที่อัปเดตระหว่างรัน และจำนวนเต็มหนึ่งตัวสำหรับนับเพื่ออัปเดตค่าเฉลี่ย
      แม้ใช้ 16 ไบต์สำหรับชื่อ ทั้งหมดก็ยังอยู่ภายใน 16KB
      เวลารันจะถูกครอบงำด้วย I/O และถัดมาก็น่าจะเป็นการ parse JSON
    • คอร์เดียวไม่สามารถทำให้ แบนด์วิดท์หน่วยความจำ เต็มได้ คอร์ถูกจำกัดด้วย memory parallelism และ latency
      ชิปเซิร์ฟเวอร์ x86 รุ่นใหม่ส่วนใหญ่สามารถ retire SIMD load ได้ 2 ครั้งต่อ clock ดังนั้นตาม AVX2 ที่ 1GHz จะได้ราว 32GB/s จึงไม่จำเป็นต้องมี AVX-512 เพื่อรีดแบนด์วิดท์ต่อคอร์ให้สูงสุดเสมอไป
      แต่ถ้าอ่านจาก DRAM ก็น่าจะตันเร็วกว่านั้นมาก โดยในเซิร์ฟเวอร์ทั่วไปมักจะอยู่แถว 10–16GB/s
      ตราบใดที่ข้อมูลส่วนใหญ่ล้นไปอยู่ใน RAM throughput ของคอร์เดียวจะตกลงมาก และในงาน streaming ขนาดใหญ่ parallelism แบบหลายคอร์แทบจะให้ประโยชน์เสมอ
      ตรวจสอบได้ง่าย ๆ ด้วยการจัดสรรบล็อกหน่วยความจำที่ใหญ่กว่า L3 cache มาก ๆ, ทำ page fault ไว้ล่วงหน้า แล้วรัน vector load ที่ unroll แล้ว (AVX2/AVX-512) ใน loop ที่แน่น
    • ผมไม่เข้าใจว่าจะรัน state machine แบบขนานได้อย่างไร ในเมื่อสถานะถัดไปขึ้นกับสถานะก่อนหน้าเสมอ
      อีกอย่างก็สงสัยว่าจะตีความรีจิสเตอร์สถานะอย่างไร ถ้า XOR กับอินพุต 4 ไบต์ สำหรับชื่อสถานที่ที่ไม่คาดไว้ มันอาจกลายเป็นค่าใดก็ได้จากค่าที่เป็นไปได้ราว 4.7 พันล้านค่า
      แม้เป็นชื่อสถานที่ที่คาดไว้ ถ้ายาวกว่า 4 ไบต์ ก็ต้องมีหลายสถานะสำหรับแต่ละชื่อเพื่อแยกจากชื่ออื่นที่มี prefix ร่วมกันไม่ใช่หรือ
    • คงต้องตรวจสอบการตีความกฎก่อน ยังไม่ชัดเจนว่าโค้ดที่ปรับเฉพาะกับชื่อสถานที่ 400 ชื่อที่รู้จัก แต่รองรับชื่อเพิ่มเติมผ่าน slow path นั้นถือว่าใช้ได้หรือไม่
      กฎระบุไว้ว่าแม้ตัวสร้างข้อมูลจะใช้ชุดชื่อสถานีตรวจวัดแบบคงที่ แต่โซลูชันใด ๆ ก็ต้องทำงานได้กับชื่อสถานีตรวจวัด UTF-8 ใด ๆ
    • ถ้าจะหาชื่อสถานที่ สุดท้ายก็ต้อง อ่านและ parse ทั้งไฟล์ อยู่ดี
  • แทนที่จะทิ้งการรันที่ช้าที่สุดและเร็วที่สุดแล้วใช้ค่าเฉลี่ยของอีกสามครั้ง ผมคิดว่าทิ้งสองครั้งที่ช้า หรือยอมรับค่าที่เร็วที่สุดไปเลยน่าจะดีกว่า
    ผมไม่เห็นเหตุผลที่สมเหตุสมผลในการทิ้งผลการรันที่ดี

    • นี่เป็นวิธีวัดที่ค่อนข้างเป็นมาตรฐาน เรียกว่า ค่าเฉลี่ยแบบตัดปลาย (Trimmed Mean): https://statisticsbyjim.com/basics/trimmed-mean/
    • มีเหตุผลที่จะทิ้งการรันที่ดีที่สุด ถ้ามองว่าระบบทำงานได้อย่างคาดเดาได้ และมีแค่ช้าลงเพราะงานเบื้องหลัง การใช้รันที่ดีที่สุดก็อาจสมเหตุสมผล
      แต่ถ้าในตัวโปรแกรมมีสาเหตุของความไม่กำหนดแน่นอนแม้เพียงเล็กน้อย ซึ่งพบได้บ่อยกว่าที่คิด เวลาเร็วที่สุดก็มีโอกาสจะไม่เป็นตัวแทนที่ดี
      บทความที่เกี่ยวข้อง https://tratt.net/laurie/blog/2019/minimum_times_tend_to_mis... เขียนไว้ดี
    • ถ้าการทิ้งการรันที่เร็วที่สุดเป็นเรื่องรับไม่ได้ ก็สงสัยว่าทำไมถึงเห็นด้วยกับการทิ้งการรันที่ช้าที่สุด
  • จากมุมมองที่เคร่งกับกติกา ตอนรันครั้งแรกอาจอยากให้เปิด ดีมอนเบื้องหลัง โหลดไฟล์ทั้งหมดขึ้นหน่วยความจำแล้วตรึงไว้ จากนั้นดึงแคชมาเตรียมไว้ล่วงหน้า เพื่อให้การรันครั้งถัด ๆ ไปแทบเหลือแค่การสแกนแบบเชิงเส้น
    การคำนวณผลลัพธ์ล่วงหน้าตั้งแต่การรันครั้งแรกก็ดูเหมือนจะทำได้ ขึ้นกับว่าจะตีความกติกาให้ยืดได้แค่ไหน และอาจพาร์สตัวเลขไว้ล่วงหน้าเป็นรูปแบบที่หนาแน่นกว่า แล้วให้การรันครั้งต่อไปอ่านเป็นผลรวมสะสมได้ทันที
    แม้จะไม่ตรงกับเจตนาของการแข่งขันเลย แต่ดูจากกติกาที่เห็นก็เหมือนไม่ได้ห้ามไว้
    ถ้าไม่ชอบการคำนวณล่วงหน้า ก็ยังมีลูกเล่นอย่างการเรียงอินพุตล่วงหน้า พาร์สล่วงหน้า หรือบีบอัด·เรียงลำดับ·จัดวางหน่วยความจำแบบเรียงแล้วได้
    สุดโต่งกว่านั้นคือแพตช์สคริปต์ calculate_time ให้คืนค่า 0 วินาที และคืนค่า 9999 ให้คู่แข่งก็ยังได้

    • ถ้าให้ไฟล์ที่ถูกต้องเป๊ะ ๆ ที่จะใช้จริงในการแข่งขันแก่ผู้เข้าแข่งขัน ปัญหาจริงจะเกิดขึ้น
      ตั้งแต่การไม่อ่านอินพุตเลยแล้วฮาร์ดโค้ดคำตอบเป็นบรรทัดเดียว ไปจนถึงการประมวลผลโดยสมมติว่าไม่รู้เนื้อหาไฟล์ มี พื้นที่สีเทาของการคำนวณล่วงหน้า อยู่สักพันล้านระดับ
      มันอาจกลายเป็นการแข่งขันตัดสินว่าอะไรคือการคำนวณล่วงหน้าที่เป็นธรรมและอะไรไม่ใช่
      นั่นจึงเป็นเหตุผลที่การแข่งขันแมชชีนเลิร์นนิงไม่เปิดเผยข้อมูลชุดสุดท้ายให้ผู้เข้าแข่งขันเห็น
    • น่าจะละเมิดกติกาข้อนี้
      มีระบุว่าการคำนวณต้องเกิดขึ้นตอนรันแอปพลิเคชัน และห้ามประมวลผลไฟล์ที่ใช้วัดผลในช่วง build แล้วฝังผลลัพธ์ลงในไบนารี
    • ตามกติกา ผมคิดว่าควรระบุให้แต่ละการรันทำงานบน tmpfs แยกกัน และลบทุกโปรเซสกับ page cache ระหว่างการรัน
  • ผมสงสัยว่านี่เป็นปัญหาที่ถูกจำกัดด้วย ความเร็วดิสก์ อย่างเดียวหรือเปล่า ไม่แน่ใจว่าการปรับแต่งอย่าง SIMD หรือมัลติเธรดจะมีความหมายไหม
    แม้จะขึ้นกับจำนวนสถานีตรวจวัดที่ต่างกันและวิธี lookup แฮช แต่เมื่อเทียบกับ I/O แล้วก็ยังสงสัยว่าจะวัดผลได้ชัดเจนแค่ไหน

    • การเข้าถึงดิสก์ทำแบบขนานได้ และ NVMe ก็เร็วมาก ดังนั้นคอขวดอาจอยู่ฝั่ง CPU มากกว่าดิสก์
      ระบบที่ออกแบบโดยสมมติฮาร์ดแวร์สมัยใหม่ใช้ประโยชน์จากจุดนี้ และ redpanda.com ที่ผมทำงานอยู่ก็เป็นตัวอย่างหนึ่ง
      การพาร์สเป็นส่วนใหญ่ของเวลาในการคำนวณ และเทคนิค SIMD อย่าง SWAR สำหรับค้นหาตัวคั่นก็ช่วยได้
      ถ้าอยากดูการทำอัลกอริทึมแบบนี้ที่สะอาดตา Stringzilla น่าสนใจ: https://github.com/ashvardanian/StringZilla
      ส่วนประเด็นที่ไฟล์จะถูกแคชไว้ในหน่วยความจำทั้งหมดหลังรันครั้งแรก ผมตอบไว้ที่นี่: https://news.ycombinator.com/item?id=38864034
    • ขึ้นกับ workload และฮาร์ดแวร์ล้วน ๆ SSD สำหรับผู้บริโภคทั่วไปก็รักษา 7GB/s (56Gbps) ได้สบาย ถ้าใช้แค่ 700GB จาก 2TB
      โดยทั่วไปเซิร์ฟเวอร์มีเลน PCIe พอสำหรับเสียบ SSD แบบนี้ 15 ตัว ดังนั้นแบนด์วิดท์ I/O ของเซิร์ฟเวอร์จึงอยู่ในระดับใกล้เคียงกับแบนด์วิดท์หน่วยความจำ
      เซิร์ฟเวอร์ที่แพงกว่าจะมีเลนที่เร็วกว่าและมากกว่า เช่น PCIe 5.0
      ไฟล์นี้มี 1 พันล้านแถว เมื่อบีบอัดแล้วประมาณ 1GB และหลังจากการรันแรกที่ทิ้งไปก็จะอยู่ในหน่วยความจำ ดังนั้นในสถานการณ์นี้แบนด์วิดท์ I/O จึงไม่สำคัญ
      ใน GitHub repository ระบุว่าแบบไม่บีบอัดมีขนาด 12GB ซึ่งก็ยังยืนยันได้ว่าแบนด์วิดท์ I/O ไม่ใช่เรื่องสำคัญ
    • พรีเซนเทชันนี้ของ Daniel Lemire น่าสนใจ: https://www.youtube.com/watch?v=wlvKAT7SZIQ
      ใจความคือดิสก์แทบไม่ค่อยเป็นคอขวด
    • ขึ้นกับระบบปฏิบัติการและไฟล์ซิสเต็ม ไฟล์อินพุตมีขนาดประมาณ 12GB และรัน 5 ครั้งบนเครื่องหน่วยความจำ 32GB ดังนั้นหลังรันครั้งแรก ไฟล์ทั้งหมดอาจถูกแคชในหน่วยความจำได้
      ตัวอย่างเช่น บน Linux ถ้าใช้ ext2 ก็มีโอกาสสูงที่จะ cache ไฟล์ทั้งหมดหลังรันครั้งแรก แต่บน ZFS อาจไม่เป็นเช่นนั้น
    • ถ้าจะพาร์สให้เร็วที่สุด ดูชัดเจนว่าควรโหลดทั้งหมดขึ้น RAM แล้วประมวลผล ย้อนจากท้ายไฟล์
      แบบนั้นตัวเลขจะออกมาเรียงจากหลักต่ำไปหลักสูง แล้วตามด้วยตัวคั่นและสตริง จากนั้นทำต่อไปจนเจอ EOF หรืออักขระขึ้นบรรทัดใหม่
  • ตามกติกา submission ต้องทำงานถูกต้องกับทุกอินพุต แต่ดูเหมือนจะหมายความว่าสามารถ และคงควร ปรับจูนให้เข้ากับอินพุตเฉพาะที่สร้างด้วย create_measurements.sh ได้
    ตัวอย่างเช่น submission ที่ใช้ perfect hash function ให้เหมาะกับชุดสถานีตรวจวัดที่กำหนดมาก็จินตนาการได้

    • ถ้ามีข้อกำหนดนี้ การทำข้อมูลทดสอบให้ต่างจากข้อมูลตัวอย่างน่าจะเป็นทางเลือกที่ฉลาด
      เพื่อป้องกัน การปรับแต่งแบบ overfit
    • เพราะ UTF-8 เลยยากขึ้นมาก แต่ถ้าทำตามตัวอักษรของกติกา ไม่ใช่เจตนารมณ์ของกติกา ก็แค่พอเจอไบต์ที่มากกว่า 127 ให้ส่งต่อไปยัง implementation แบบช้าได้ทันที
      ไบต์ที่มากกว่า 127 หมายถึงอักขระ UTF-8 แบบหลายไบต์
  • ลองเทียบความเร็ว awk กับ Java เล่น ๆ
    เป็นสคริปต์ที่ใช้ awk -F';' สะสมผลรวม จำนวน ค่าต่ำสุด ค่าสูงสุดแยกตามสถานีตรวจวัด แล้วคำนวณค่าเฉลี่ยใน END ก่อนพิมพ์ออกมา

    • อยากเห็นการเทียบความเร็วกับ file foreign data wrapper ของ PostgreSQL: https://www.postgresql.org/docs/current/file-fdw.html
      วิธีคือใช้ file_fdw ทำไฟล์ CSV เป็นตารางภายนอก แล้วคำนวณ MIN, AVG, MAX ด้วย GROUP BY station_name
    • ถ้ารันด้วย ClickHouse local จะได้ประมาณ 15.2 วินาที
      ใน clickhouse local อ่าน file('measurements.txt', 'CSV', 'station String, t Float32') แล้วจัดกลุ่มตามสถานีตรวจวัดเพื่อหา min, max, avg โดยรันด้วย max_threads = 8
      เวลาส่วนใหญ่ใช้ไปกับการพาร์สไฟล์
    • ตัวแปร sum อาจมีค่าค่อนข้างใหญ่ จึงควรใช้ ค่าเฉลี่ยแบบสตรีมมิง
      เช่นวิธี new_mean = ((n*old_mean)+temp)/(n+1)
  • เป็นชาเลนจ์ที่น่าสนใจ แต่น่าเสียดายที่จำกัดเฉพาะ Java รอคอยวันที่ผู้คนเริ่มสร้าง JVM bytecode ด้วยมือกันเอง

    • ดูจากการอภิปราย เหมือนจะมีงานส่งจากหลายภาษา เช่น Go, Rust, Python, C++ เป็นต้น
      [0] https://github.com/gunnarmorling/1brc/discussions
    • หรืออาจตีความว่า “ต้องเขียนด้วย Java” เป็น “ตอนเริ่มรันต้องใช้ JVM” ก็ได้ และแน่นอนว่า Java สามารถเปิดโปรเซสอื่นได้
  • สนุกดี ให้ความรู้สึกเหมือนงานต่อหลัง Advent of Code
    ถ้าจะเปรียบเทียบระหว่างภาษาอย่างเป็นธรรม ก็ควรรวม make และเวลา build ด้วย ผมไม่ได้ใช้ Java/Maven มาหลายปีแล้ว แต่พอเห็น ./mvnw clean verify ดาวน์โหลดต่อเนื่องมาถึง 2 นาที ก็ทำให้นึกเหตุผลออกอีกครั้ง

    • เวลา build ของ Java เร็วมาก สิ่งที่วัดอยู่ตอนนี้คือ ความเร็วอินเทอร์เน็ต
      และถ้าเป็นเครื่องมือ build แบบ incremental compilation แล้ว Gradle เร็วกว่า
    • ถ้ารวมเวลา build ก็ควรรวมเวลาเขียนโปรแกรมด้วย แล้วหารทั้งสองอย่างด้วยจำนวนครั้งที่โค้ดจะถูกรันตลอดอายุการใช้งาน
      ควรบวกสัดส่วนที่เหมาะสมของเวลาที่ใช้เรียนเขียนโปรแกรมเข้าไปด้วย
      ในชาเลนจ์แบบนี้ เวอร์ชันที่ไร้เดียงสามาก ๆ มีโอกาสชนะสูง ซึ่งผมคิดว่าไม่เพียงไม่สมจริง แต่ยังผิดจากเจตนาของชาเลนจ์ด้วย
    • ไม่เข้าใจว่าทำไมต้อง clean
      เหมือนทิ้ง cache แล้วมาบอกว่าช้า
    • ไม่จำเป็นต้องใช้ Maven
      เขียนไว้ว่าห้ามใช้ dependency ภายนอก
  • ในวิชา C ของมหาวิทยาลัยเทคนิคเช็กเคยมีงานที่คล้ายกันมาก
    งานส่งของนักศึกษาทุกคนถูกประเมินบน ตารางอันดับ อย่างต่อเนื่อง และนักศึกษาจำนวนมากใช้เวลาหลายสิบชั่วโมงในการ optimize เพื่อให้ได้คะแนนพิเศษสำหรับผลการเรียนที่ดีกว่า หรือเอาจริง ๆ ก็คือคะแนนสถานะ

 
dlehals2 2024-01-10

อันดับ 1 ใช้เวลา 6 วินาทีเอง.. น่าทึ่งมาก