สร้างแพ็กเกจ Ubuntu ใหม่ให้เร็วขึ้น 90%
(gist.github.com/jwbee)- เมื่อนำซอร์ส
jqเดิมมาบิลด์ใหม่และเปลี่ยน allocator เวลาในการประมวลผล GeoJSON ขนาด 500MB ลดจาก 4.606 วินาทีเป็น 2.428 วินาที ทำให้เร็วกว่าไบนารีของ Ubuntu 1.90 เท่า - การ benchmark ทำบน Ryzen 9 9950X โดยใช้ parcel map ของ Alameda County Assessor และดึง
SitusCityที่ตรงเงื่อนไขTotalNetValue < 193000 - แค่บิลด์ใหม่ธรรมดาก็ปรับปรุงได้ 2~4% แล้ว และชุด
clang-18,-O3,-flto,-DNDEBUGให้ประสิทธิภาพ 1.20 เท่า เมื่อเทียบกับแพ็กเกจ Ubuntu - จาก profile พบว่าต้นทุน การจัดสรรหน่วยความจำ สูง จึงเปรียบเทียบ
TCMalloc,jemalloc,mimallocและในการทดลองด้วยLD_PRELOADนั้นmimallocเร็วที่สุด - บิลด์สุดท้ายที่ลิงก์กับ
mimallocยังทำเวลา 0.755 วินาที เทียบกับ 1.424 วินาที ในกรณีประมวลผล JSON ขนาด 2.2GB แยกต่างหาก แสดงว่า build เริ่มต้นของดิสโทรอาจต่างกันมากตาม workload
workload อ้างอิงและวิธีวัดผล
- สิ่งที่ทดสอบคือเครื่องมือประมวลผล JSON
jqโดยข้อมูล input เป็นไฟล์ GeoJSON ขนาด 500MB ที่บรรจุ parcel map ของ Alameda County Assessor - query ที่รันคือการพิมพ์
SitusCityของรายการใน parcel list ที่ตรงเงื่อนไขTotalNetValue < 193000.features[] | select(.properties.TotalNetValue < 193000) | .properties.SitusCity
/usr/bin/jqค่าเริ่มต้นของ Ubuntu ใช้เวลาประมาณ 5 วินาที เมื่อไฟล์ถูก cache แล้ว และ benchmark รายละเอียดวัดซ้ำด้วยhyperfine- เพื่อลดความแปรปรวนระหว่างรัน จึงตรึงไว้ที่ logical CPU 2 ด้วย
taskset -c 2- เป็นการตั้งค่าเพื่อหลีกเลี่ยงผลกระทบจาก system interrupt ที่รันบน CPU 0 และ CPU migration
การบิลด์ซอร์สเดิมใหม่แบบธรรมดา
- ดาวน์โหลด
jqsource code ที่ Ubuntu ใช้ แล้วรัน configure และ build โดยไม่ใส่ flag เพิ่มเติม - แค่บิลด์ใหม่แบบธรรมดานี้ก็เร็วกว่าแพ็กเกจไบนารีของ Ubuntu ประมาณ 2~4%
- ไบนารีที่บิลด์ใหม่: เฉลี่ย 4.517 วินาที
- Ubuntu
/usr/bin/jq: เฉลี่ย 4.641 วินาที - ผลคือประสิทธิภาพประมาณ 1.03 เท่า
การใช้ clang และ flag สำหรับ optimization
- ขั้นถัดมา ใช้
clang-18, optimization level ที่สูงขึ้น, LTO และ flag ที่เกี่ยวกับ debugging/profiling ร่วมกัน - flag หลักที่ส่งผลต่อประสิทธิภาพคือ
-O3,-flto,-DNDEBUG-O3ใช้ระดับ optimization ที่สูงกว่า-O2-fltoเปิดใช้ link-time optimization-DNDEBUGลดต้นทุนของ assertion ที่เห็นเด่นใน profile
- ตัวอย่าง configure ที่ใช้มีดังนี้
CC=clang-18LDFLAGS="-flto -g -Wl,--emit-relocs -Wl,-z,now -Wl,--gc-sections -fuse-ld=lld"CFLAGS="-flto -DNDEBUG -fno-omit-frame-pointer -gmlt -march=native -O3 -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections"
- บิลด์นี้เร็วกว่าไบนารี Ubuntu 1.20 เท่า
- บิลด์ใหม่แบบ optimize: เฉลี่ย 3.853 วินาที
- Ubuntu
/usr/bin/jq: เฉลี่ย 4.631 วินาที
การทดลองเปลี่ยน allocator
jqเป็นโปรแกรม C ที่ซับซ้อน และใน profile พบว่า การจัดสรรหน่วยความจำ เป็นต้นทุนที่ใหญ่ที่สุด- เริ่มจากการลิงก์
TCMallocที่มีเป็นแพ็กเกจใน Ubuntu แล้วบิลด์ใหม่- เพิ่ม
-L/usr/lib/x86_64-linux-gnu -ltcmalloc_minimalลงในLDFLAGS - ไบนารีที่บิลด์ใหม่: เฉลี่ย 3.253 วินาที
- Ubuntu
/usr/bin/jq: เฉลี่ย 4.611 วินาที - ผลคือเร็วกว่าไบนารี Ubuntu 1.42 เท่า
- เพิ่ม
- แม้จะเปลี่ยนเฉพาะ allocator ด้วย
LD_PRELOADบนไบนารี Ubuntu เดิม ก็ยังปรับปรุงได้บางส่วน- ค่าเริ่มต้น: เฉลี่ย 4.601 วินาที
- preload TCMalloc: เฉลี่ย 4.082 วินาที
- เร็วกว่าค่าเริ่มต้น 1.13 เท่า
dynamic preload และการตั้งค่า THP
- เปรียบเทียบ
jemalloc,mimalloc,TCMallocที่ Ubuntu มีให้ด้วยLD_PRELOAD - การเปรียบเทียบนี้ได้ผลหลังตั้ง environment variable ต่อไปนี้
MIMALLOC_LARGE_OS_PAGES=1MALLOC_CONF="thp:always,metadata_thp:always"GLIBC_TUNABLES=glibc.malloc.hugetlb=1
- ผลคือ mimalloc เร็วที่สุด
- glibc ค่าเริ่มต้น: เฉลี่ย 4.123 วินาที
- preload TCMalloc: เฉลี่ย 4.130 วินาที
- preload jemalloc: เฉลี่ย 3.510 วินาที
- preload mimalloc: เฉลี่ย 3.154 วินาที
- การเปิดใช้ THP ให้ประโยชน์กับทั้ง glibc allocator, jemalloc และ mimalloc
THP + mimallocเร็วกว่าTHP + glibc31% และเร็วกว่า glibc ค่าเริ่มต้น 48%
ผลลัพธ์สุดท้ายของบิลด์ที่ลิงก์ mimalloc
- เมื่อเห็นว่า dynamic preload ไม่ใช่วิธีที่เหมาะที่สุดด้านประสิทธิภาพ ขั้นสุดท้ายจึงบิลด์
jqใหม่โดยลิงก์กับmimalloc - บิลด์สุดท้ายเร็วกว่าแพ็กเกจไบนารี Ubuntu 1.90 เท่า
jqที่บิลด์ใหม่ด้วยmimalloc: เฉลี่ย 2.428 วินาที- Ubuntu
/usr/bin/jq: เฉลี่ย 4.606 วินาที - benchmark แต่ละชุดอ้างอิงจากการรัน 10 ครั้ง
- ใช้บิลด์เดียวกันนี้กับแอปพลิเคชันอื่นด้วย
- ประมวลผล JSON ขนาด 2.2GB ในไฟล์ 13,000 ไฟล์
- ใช้
rushสำหรับ parallelization jqที่บิลด์ใหม่ด้วยmimalloc: 0.755 วินาที- แพ็กเกจ Ubuntu
jq: 1.424 วินาที
- ในกรณีแยกนี้ ความเร็วที่เพิ่มขึ้นก็เกือบถึง 2 เท่า
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
คลิกเบตอย่าง “สร้างแพ็กเกจ Ubuntu ขึ้นใหม่หนึ่งตัวแล้วเปลี่ยน ตัวจัดสรรหน่วยความจำ ให้เร็วขึ้น 90%” นี่ชวนให้รู้สึกอยากต่อยข้าม TCP/IP ไปสักทีจริง ๆ มันเป็นแค่ แพ็กเกจเดียว เท่านั้น และการปรับปรุงบางส่วนก็ไม่ได้มาจากการคอมไพล์ใหม่ด้วยซ้ำ
ถึงอย่างนั้น ผมเคยลองแทรก jemalloc เข้าไปในโปรแกรมหนึ่งด้วย
LD_PRELOADเพื่อเปลี่ยน implementation ของmallocและผลลัพธ์ก็ค่อนข้างดี แม้ไม่ได้วัดประสิทธิภาพ แต่การใช้หน่วยความจำของแอปพลิเคชันนั้นเสถียรขึ้น และปัญหาที่ดูเหมือน memory leak ก็หายไปด้วย จริง ๆ แล้วมีความเป็นไปได้สูงว่าไม่ใช่ปัญหาของตัวแอปเอง แต่เป็น memory fragmentation ของmallocมาตรฐานfree()หน่วยความจำก็แทบจะไม่ถูกปล่อยจริงจากมุมมองภายนอก ยกเว้นในกรณีพิเศษยิ่งมีเธรดและคอร์ CPU มาก ปัญหานี้ยิ่งรุนแรงขึ้น วิธีแก้ง่าย ๆ อย่างหนึ่งคือการตั้งค่าตัวแปรสภาพแวดล้อม “วิเศษ”
MALLOC_ARENA_MAX=2เพื่อจำกัดจำนวนแคช อีกวิธีคือให้แอปพลิเคชันเรียกmalloc_trim()เป็นระยะเพื่อเคลียร์แคช แต่วิธีนี้ต้องแก้ซอร์สโค้ดhttps://www.joyfulbikeshedding.com/blog/2019-03-14-what-caus...
กลับกัน ผมดีใจที่ไม่ได้คอมไพล์ด้วย
-O3ซึ่งอาจมีบั๊กแฝงได้ สำหรับบางส่วนที่ประสิทธิภาพสำคัญอาจจะดี แต่ผมไม่อยากให้คอมไพล์ทั้งระบบด้วย-O3นานมาแล้วผมเริ่มบิลด์ Mozilla และเคอร์เนล Linux ของผมเองตามความชอบ และโดยปกติก็ได้ประสิทธิภาพเพิ่มขึ้นพอสมควร จุดประสงค์ทั้งหมดของดิสโทร Gentoo Linux ก็เช่น การคอมไพล์ทุกอย่างจากซอร์สแบบปรับแต่งให้เหมาะสมเพื่อให้ได้ประสิทธิภาพที่ดีขึ้น
jq,grep,ffmpeg,ocrmypdfและยูทิลิตี Unix ทั่วไปแบบนี้มักถูกบิลด์ให้เป็นเป้าหมายใช้งานทั่วไป ไม่ใช่สำหรับแอปพลิเคชันเฉพาะวิศวกรรมคือการประนีประนอม ประโยชน์ส่วนใหญ่ในบทความมาจากการปรับ allocator สำหรับหน่วยความจำให้เฉพาะทาง ต้องจำไว้ว่าบางโปรเจกต์เป็นแบบหลายเธรด อาจจัดสรรในเธรดหนึ่ง เขียนข้อมูลในอีกเธรดหนึ่ง และปล่อยคืนในเธรดที่สาม
allocator ต้องรองรับเรื่องนี้ ดังนั้นการเพิ่มความเร็วในโปรเจกต์หนึ่งอาจกลายเป็นการชนกันในอีกโปรเจกต์หนึ่งได้ กลยุทธ์การจัดสรรซ้ำก็เป็นปัญหาเช่นกัน บางโปรแกรมจัดสรรไว้ล่วงหน้าแล้วไม่แตะ
mallocอีกเลย แต่บางโปรแกรมปล่อยคืนแล้วขอใหม่อยู่ตลอด ความสามารถในการจัดการ fragmentation ได้ดีแค่ไหน และ uptime เป็น 10 วินาทีหรือ 10 ปี ก็สำคัญ บางครั้งการเลือก allocator คือความแตกต่างระหว่างเสถียรภาพระยะยาวกับความเร็วระยะสั้นเคยทดลอง allocator หลายตัวตอนทำโปรแกรมตัดต่อวิดีโอที่ทดสอบวิดีโอ 4K และแคชเฟรม ถ้า 32MB ต่อเฟรม ที่ 60fps ก็เกือบ 2GB ต่อวินาทีต่อหนึ่งแทร็ก ชนขีดจำกัดของ allocator ทันที และตระหนักได้ว่าอย่างน้อย allocator พื้นฐานของ glibc ให้เสถียรภาพระยะยาวดีที่สุด แต่ในการ benchmark สั้นๆ กลับช้าที่สุด
แน่นอนว่าระดับการเพิ่มความเร็วอาจแตกต่างกันได้ แต่ benchmark โดยรวมมักแสดงให้เห็นการปรับปรุงทั่วๆ ไป ส่วนมันมีความหมายกับแอปพลิเคชันเฉพาะหรือไม่นั้นเป็นอีกเรื่องหนึ่งโดยสิ้นเชิง สำหรับเรื่องการชนกัน พวกนี้ล้วนเป็น allocator อเนกประสงค์แบบหลายเธรด จึงไม่ได้ทำงานต่างจาก glibc และบั๊กก็อาจมีใน glibc ได้เหมือนกัน
DEFAULT_MMAP_THRESHOLD_MAXและบนแพลตฟอร์ม 64 บิตมันคือ 32MiB ดังนั้นจึงไม่สามารถโน้มน้าวให้ glibc แคชมันได้ตามที่เอกสารคู่มือmalloptระบุไว้ทุกครั้งจะขอหน่วยความจำจากเคอร์เนลโดยตรงด้วย
mmapและคืนด้วยmunmapsystem call เหล่านี้ช้านิดหน่อย และในกรณีของผม ค่าใช้จ่ายในการเกิด page fault ของแต่ละ memory page ตอนเข้าถึงครั้งแรก ช้าจนไม่ถึงเป้าหมายด้านประสิทธิภาพ วิธีแก้เรียบง่ายมาก คือใช้ free list ของตัวเองเฉพาะสำหรับเฟรมวิดีโอ โดยอยู่บน allocator อเนกประสงค์หรือmmapก็ได้ การจัดสรรขนาดเท่ากันทุกครั้งถูกทำซ้ำอย่างเสถียรมาก จึงทำงานได้ดี[1] ในรูปแบบ UYVY จะเล็กกว่า 64MiB เล็กน้อย และในรูปแบบ I420 จะเล็กกว่า 48MiB เล็กน้อย
แต่พออ่านคอมเมนต์บนนี้แล้วก็เริ่มกังวลว่าผมอาจเข้าใจบทความผิดไปหมดหรือเปล่า จากน้ำเสียงดูเหมือนกำลังบอกว่าสิ่งที่ gist พูดถึงนั้นห้ามทำเด็ดขาด และเป็นข้อเสนอที่แย่มากซึ่งมองข้ามความซับซ้อนเหล่านี้ทั้งหมด ช่วยให้ผมเข้าใจได้ไหมว่า gist ต้นฉบับเป็นบทความที่ดีหรือไม่ มีประเด็นที่สมเหตุสมผลหรือไม่ หรือไม่มีคุณค่าเลย? ก่อนเห็นคอมเมนต์นี้ผมคิดว่ามันมีคุณค่า แต่ตอนนี้รู้ตัวแล้วว่าผมไม่ได้ฉลาดพอจะแยกแยะเอง
ถ้าใช้อัลกอริทึมบีบอัดที่เหมาะกับข้อมูล ผู้คนก็จะบอกว่าทำไมคุณถึงโง่และควรใช้อัลกอริทึมอื่น ช่วงหลังมานี้ต้องบีบอัดสตริง JSON ยาวๆ บางแบบเพื่อใส่ลง Dynamo และเมื่อทดสอบอัลกอริทึมยอดนิยมทั้งหมดอย่างละเอียดแล้ว Brotli นำหน้าแบบขาดลอย ถึงอย่างนั้นก็ไม่ได้หยุดคนที่ผ่านมาทุกคนจากการบอกว่า zlib ดีกว่าได้ บางทีก็เหนื่อยเอาเรื่อง
Gentoo Linux โดยแท้จริงแล้วเป็นดิสโทรที่สร้างมาเพื่อคนกลุ่มนี้ เพื่อให้ปรับแต่งเครื่อง Linux ของตัวเองให้เหมาะกับการใช้งานของตัวเองได้
หลังจากตั้งค่าเริ่มต้นแล้วก็ค่อนข้างเรียบง่ายและใช้งานง่าย จำได้ว่าเคยได้เพื่อนเยอะมากในช่อง Gentoo Linux บน Matrix เป็นช่วงเวลาที่สนุกดี
https://www.gentoo.org/
เกร็ดที่น่าสนใจคือ ChromeOS รุ่นแรก ๆ โดยพื้นฐานแล้วเป็นการติดตั้ง Gentoo Linux แบบคัสตอม ไม่รู้ว่าตอนนี้ภายในยังใช้ Gentoo Linux อยู่หรือเปล่า
ผมใช้ Gentoo มา 20 ปี แต่ไม่เคยใช้เพราะเรื่องประสิทธิภาพเลย Gentoo ยอดเยี่ยมเมื่อคุณรู้ว่าต้องการให้มันทำงานแบบไหน และมันช่วยพาคุณไปถึงจุดนั้น
เป้าหมายของ Gentoo คือการมีระบบปฏิบัติการที่สร้างโปรแกรมทั้งหมดจากซอร์ส แทนที่จะใช้แพ็กเกจไบนารีที่บิลด์ไว้ล่วงหน้า สิ่งนี้ทำให้ปรับแต่งและเพิ่มความเร็วขั้นสูงได้ แต่ก็หมายความว่าแม้แต่องค์ประกอบพื้นฐานที่สุดอย่างเคอร์เนลก็ต้องคอมไพล์จากซอร์ส ในชุมชน Linux มันขึ้นชื่อว่าเป็นระบบปฏิบัติการที่ซับซ้อนมาก เพราะขั้นตอนติดตั้งที่หนักหน่วง การติดตั้ง Gentoo พื้นฐานจะบูตเข้าสู่พรอมป์ต์คำสั่งทันที และผู้ใช้ต้องแบ่งพาร์ทิชันดิสก์เอง ดาวน์โหลดและแตกแพ็กเกจที่เรียกว่า “Stage 3 tarball” ติดตั้งแพ็กเกจด้วยตนเอง และประกอบระบบขึ้นมา ผู้ใช้ใหม่หรือผู้ใช้ที่มีประสบการณ์น้อยมักไม่รู้ว่าต้องทำอะไรเมื่อเข้าโปรแกรมติดตั้งแล้วไม่มีหน้าจอกราฟิก สมาชิก /g/ มักพูดเกินจริงถึงคุณค่าของ Gentoo เพื่อหลอกให้ผู้ใช้ใหม่ลองติดตั้ง
อาจสะดุดหนึ่งสองครั้ง แต่ถ้ามีประสบการณ์ Linux โดยรวมมากพอ ก็มีแนวโน้มว่าจะเข้าไปแก้ build recipe ทำให้มันทำงานตามต้องการ และส่งแพตช์กลับ upstream ได้ นี่เป็นผลจากการโฟกัสกับความมินิมอลอย่างจริงจังและหลีกเลี่ยง overengineering ทุกรูปแบบ ซึ่งเป็นสิ่งที่ผมคิดถึงมาตลอดตอนใช้ Gentoo ใน Gentoo ผมมักลงเอยด้วยการปรับ USE flags และ package masks ในแบบที่ไม่ได้ช่วยผู้ใช้คนอื่นเท่าไร ระบบบิลด์ซับซ้อนเกินไป จนตลอดหลายปีที่ผ่านมายากเกินกว่าจะเรียนรู้ให้ชำนาญ แก้ปัญหาถึงระดับรากสาเหตุ และส่ง upstream ได้ Void อาจเป็นฐานที่เหมาะมากเช่นกันเมื่อไม่อยากบิลด์ทั้งระบบจากซอร์ส แต่อยากผสมใช้ไบนารีที่ดิสโทรให้มากับแพ็กเกจที่บิลด์เองจากซอร์ส
หลังจากนั้นย้ายไป ArchLinux และโดยรวมก็โอเคสำหรับผม ถ้าใช้โปรเซสเซอร์ที่ค่อนข้างมาตรฐาน ผมไม่คิดว่า Gentoo จะให้ข้อได้เปรียบมากนัก
การทำแบบนี้จะทำให้ไม่เพียงพลาดอัปเดตความปลอดภัยของ
jqเท่านั้น แต่ยังพลาดอัปเดตความปลอดภัยของ onigurama ซึ่งเป็น dependency สำหรับการพาร์ส regex ด้วย ก่อนหน้านี้เคยมีอัปเดตความปลอดภัยของ onigurama และถ้าเรื่องแบบนี้เกิดขึ้นอีกก็อาจมีช่องโหว่ได้jqมักถูกใช้พาร์ส JSON ที่ไม่น่าเชื่อถือเนื้อหาคือ “อัปเดตความปลอดภัย: แก้ไขการ dereference พอยน์เตอร์ที่ไม่ถูกต้องหลายรายการ, หน่วยความจำเสียหายจากการเขียนนอกขอบเขต, และ stack buffer overflow” และกรณีนั้นเกี่ยวข้องกับ CVE-2017-9224, CVE-2017-9226, CVE-2017-9227, CVE-2017-9228, CVE-2017-9229
libonig5และจะถูกอัปเดตตามปกติไม่ได้ตั้งใจจะลดคุณค่าของระบบ CVE แต่ก็ปฏิเสธได้ยากว่าผลกระทบจริงระหว่างสิ่งที่พบแต่ละอย่างแตกต่างกันมาก
ไม่ได้ยุ่งกับเรื่องแบบนี้มาสักพักแล้ว แต่เท่าที่จำได้ ทันทีที่ใช้ flags เกินกว่าที่นักพัฒนา upstream ใช้ ก็จะเจอบั๊กแปลก ๆ และความไม่สนใจอย่างมหาศาลเมื่อเกิดบั๊กขึ้น ตรงนี้หมายถึง นักพัฒนา upstream ไม่ใช่แพ็กเกจเจอร์ของดิสโทร
ไม่เคยใช้
mallocที่ไม่ใช่ของ libc แต่คิดว่าหลักการเดียวกันน่าจะใช้ได้แต่ถ้าทุกคนทำแบบนั้น ก็จะกลายเป็น วัฒนธรรมเดี่ยว และวัฒนธรรมเดี่ยวนั้นเปราะบางและไม่ดี โค้ดจะแข็งแรงขึ้นได้บ้างก็เพราะมันถูกบิลด์ในบริบทอื่น ๆ เช่น แพลตฟอร์ม คอมไพเลอร์ ออปชัน ไลบรารี ฯลฯ ที่ต่างกัน บั๊กที่แพลตฟอร์มหรือ build flags ส่วนใหญ่บังเอิญเดินเฉียดกับดักไปโดยไม่แตะ ก็ยังเป็นบั๊กอยู่ดี และการค้นพบกับแก้มันย่อมดีกว่าสำหรับโค้ด ในฐานะปัจเจก พวกเราทุกคนได้ประโยชน์เมื่อโค้ดโดยรวมแข็งแรงขึ้นแทนที่จะเปราะบาง
แน่นอนว่าผมก็เคยคิดว่า
-march=nativeคือการปรับปรุงหลักที่เห็น แต่บทความนี้แสดงให้เห็นว่าไม่จำเป็นต้องเป็นอย่างนั้นเสมอไป แอปพลิเคชันที่ใช้ floating point ก็น่าจะมีจุดหยาบ ๆ มากกว่าค่อนข้างใกล้เคียงกับการนำไปบิลด์ใหม่ด้วย allocator ตัวอื่น ที่ทำเบนช์มาร์กออกมาได้ดีในเวิร์กโฟลว์เฉพาะ
mallocได้ทั้งนั้น การที่ดิสโทรยังใช้ glibcmallocต่อไปแทนที่จะใช้ mimalloc หรือ jemalloc นี่แทบจะเรียกว่าละเลยหน้าที่เลยmallocเป็นที่รู้กันไหมว่าดีกับโหลดงานแบบไหน?อยากรู้ว่าประสิทธิภาพเมื่อเทียบกับโคลน
jqที่เขียนด้วย Rust ตัวนี้เป็นอย่างไรcargo install --locked jaqถ้าต้องการเปิดการปรับแต่งให้เหมาะกับตระกูล CPU เฉพาะ ก็เพิ่ม
RUSTFLAGS="-C target-cpu=native"ได้ด้วยcargo installเป็นฟีเจอร์ที่ถูกประเมินค่าต่ำของ Rust ซึ่งเหมาะพอดีกับการใช้งานแบบที่บทความอธิบายไว้ เพราะมันบิลด์เครื่องมือจากซอร์ส จึงเลือกใช้ฟีเจอร์หรือคำสั่งเฉพาะแพลตฟอร์มที่ปกติไม่ได้ใส่ไว้ในไบนารีเพื่อรักษาความเข้ากันได้กับ CPU รุ่นเก่าได้ ไม่ต้องโคลนรีโพหรือหาวิธีบิลด์ด้วยซ้ำ สิ่งนั้นมาพร้อมกันอยู่แล้วjaq[1] และyq[2] เป็นตัวเลือกที่ผมหยิบมาใช้ทุกครั้งที่ใช้jqแล้วต้องการเพิ่มประสิทธิภาพแบบเร็วและง่าย[1] https://github.com/01mf02/jaq
[2] https://github.com/mikefarah/yq
jq,gojqเป็นครั้งคราว โดยทดสอบด้วยคำตอบjqของผมสำหรับ AoC 2022 day 13https://gist.github.com/oguz-ismail/8d0957dfeecc4f816ffee79d...
ตอนนี้ก็ยังตามหลังทั้งสองตัวอยู่
cargo installยังมี แฟล็ก--gitสำหรับระบุ URL ของรีโพได้ด้วย ใช้ได้ตอนที่ไม่มีแพ็กเกจสาธารณะ หรืออยากได้คอมมิตล่าสุดที่ยังไม่ได้รีลีสผมเคยใช้หลายครั้งมาก่อน โดยเฉพาะเป็นวิธีง่าย ๆ ในการติดตั้งเครื่องมือที่ทำขึ้นลวก ๆ ใช้ส่วนตัวอย่างรวดเร็ว หลังจาก push ขึ้นรีโพแล้ว โดยไม่ต้องทำกระบวนการรีลีส หรือคัดลอกไบนารีไปยังเครื่องส่วนตัวหลายเครื่องเอง และไม่ต้องตามเก็บว่าคอมมิตไหนกันแน่ที่ใช้บิลด์
ถ้าจะทำแบบนี้จริง ๆ ก็ให้ Ubuntu ดาวน์โหลด แพ็กเกจซอร์ส ที่ต้องการตรง ๆ ได้เลย ในกรณีนี้ใช้
apt-get source jqก็พอจากนั้นเข้าไปในแพ็กเกจแล้วคอมไพล์ใหม่ได้ตามใจ จะรีแพ็กเกจเพื่อแจกจ่ายหรือเก็บรักษาไว้ก็ได้ วิธีนี้จะได้ผลลัพธ์ที่ใกล้เคียงกับ Ubuntu upstream มากกว่า แทนที่จะได้ข้อผิดพลาดแปลก ๆ และความไม่สอดคล้องกันเพียบ
ชื่อเรื่องทำให้เข้าใจผิด หมายถึงเวลาที่เร็วขึ้น 90% และจริง ๆ แล้วคือ เร็วขึ้นประมาณ 45%
ถ้าสนใจว่าเราใช้ภาษากันอย่างไร เรื่องนี้ก็น่าสนใจอยู่เล็กน้อย อาจพูดได้ว่าในเวลาเท่ากันทำงานได้มากขึ้น 90% ซึ่งสอดคล้องกับหน่วยวัดความเร็วแบบอื่นที่เราใช้กันทั่วไป เช่น ไมล์ต่อชั่วโมง คำต่อนาที บิตต่อวินาที แต่ในประสิทธิภาพคอมพิวเตอร์มีธรรมเนียมการวัดเวลาที่ใช้กับปริมาณงานคงที่ น่าจะเพราะโดยมากปริมาณงานคงที่ และสิ่งที่เปลี่ยนคือเวลาที่ต้องรอ สำหรับบล็อกโพสต์นี้ก็เป็นแบบนั้นพอดี จึงทำให้เอาเวลาไปไว้เป็นตัวเศษ ตัวบทความเองน่าสนใจและเขียนดีมาก แต่ไม่ได้เร็วขึ้น 90%
อีกอย่าง ถ้าจะใช้หน่วยเวลา ก็คงไม่ใช้คำว่า “faster” อยู่แล้ว “45% less time” กับ “45% faster” เป็นข้ออ้างที่ต่างกันมาก และทั้งสองแบบมีความหมายทั้งในและนอกวงการโปรแกรมมิง
เวลาเราพูดว่า “ลดอะไรบางอย่างลง N%” โดยปกติจะถือว่า N% นั้นเป็น N% ของสิ่งที่ถูกลดลงเอง ไม่ใช่ของค่าอื่น
พออ่านว่าแค่เปลี่ยนง่าย ๆ แบบนี้ก็ได้ความเร็วเพิ่มขึ้นมาก สิ่งแรกที่นึกถึงคือควรบอก ผู้เขียน jq อาจมีหลุมพรางที่ต้องระวัง หรือหลังจากทดสอบแล้วอาจทำให้ทุกคนเร็วขึ้นได้
ไม่ว่าผลจะเป็นอย่างไร การแจ้งสั้น ๆ ก็ดูมีประโยชน์ แต่ในบทความดูเหมือนไม่ได้พิจารณาตัวเลือกนั้นเลย และในคอมเมนต์นี้ก็ยังไม่เห็น ผมพลาดอะไรไปหรือเปล่า?
ไม่รู้เหมือนกันว่าที่นั่นใช้ glibc allocator เป็นมาตรฐานหรือเปล่า
https://en.m.wikipedia.org/wiki/Clear_Linux_OS