17 คะแนน โดย GN⁺ 2025-12-27 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ตัวจัดการแพ็กเกจ Python uv มีความเร็วในการติดตั้ง เร็วกว่า pip มากกว่า 10 เท่า และไม่ได้เร็วเพียงเพราะเขียนด้วย Rust แต่เกิดจาก การเลือกเชิงออกแบบ
  • หัวใจสำคัญที่ทำให้เร็วคือ มาตรฐานเมทาดาทาแบบคงที่ (PEP 518, 517, 621, 658) ซึ่งทำให้สามารถทราบ dependency ได้โดยไม่ต้องรันโค้ด
  • uv ตัด ฟีเจอร์แบบ legacy (.egg, pip.conf, การติดตั้งลงระบบ Python ฯลฯ) ที่ pip ยังรองรับออกอย่างเด็ดขาด เพื่อ กำจัด code path ที่ไม่จำเป็น
  • ส่วนที่ Rust ช่วยได้คือ zero-copy deserialization, lock-free concurrency, โครงสร้างแบบ single binary เป็นต้น ซึ่งเป็นเพียงส่วนหนึ่งของการเพิ่มความเร็วทั้งหมด
  • โดยรวมแล้ว กรณีของ uv แสดงให้เห็นว่า เมทาดาทาที่เป็นมาตรฐานและการตัดความเข้ากันได้ที่ไม่จำเป็นออก คือหัวใจของนวัตกรรมด้านประสิทธิภาพ

มาตรฐานที่ทำให้ uv เร็วได้

  • ความช้าของ pip ไม่ได้เป็นปัญหาจาก implementation แต่เกิดจากโครงสร้างแบบ setup.py-based ในอดีต ที่ทำให้ต้องรันโค้ดเพื่อจะรู้ dependency
    • การรัน setup.py ต้องติดตั้ง build dependency ก่อน ซึ่งก่อให้เกิด “ปัญหาไก่กับไข่”
    • ในกระบวนการติดตั้งอาจเกิด การรันโค้ดตามอำเภอใจและความล้มเหลวซ้ำ ๆ ทำให้การติดตั้งช้าลง
  • PEP 518 (2016) นำ pyproject.toml เข้ามา ทำให้ประกาศ build dependency ได้โดยไม่ต้องรันโค้ด
  • PEP 517 (2017) แยก build frontend กับ backend ออกจากกัน ทำให้ pip ไม่จำเป็นต้องเข้าใจภายในของ setuptools
  • PEP 621 (2020) ทำให้ตาราง [project] เป็นมาตรฐาน จึงตรวจสอบ dependency ได้ด้วยการ parse TOML เท่านั้น
  • PEP 658 (2022) ใส่เมทาดาทาของแพ็กเกจไว้ใน Simple Repository API โดยตรง ทำให้ดึงข้อมูล dependency ได้โดยไม่ต้องดาวน์โหลด wheel
  • PyPI เริ่มใช้ PEP 658 ในเดือนพฤษภาคม 2023 และ uv เปิดตัวในเดือนกุมภาพันธ์ 2024 จึงกลายเป็น เครื่องมือตัวแรกที่ใช้ประโยชน์จากโครงสร้างมาตรฐานใหม่นี้ได้อย่างเต็มที่
  • เช่นเดียวกับ Cargo ของ Rust หรือ npm ระบบนิเวศ Python ก็กำลังเปลี่ยนผ่านสู่ แพ็กเกจจิงที่อิงเมทาดาทาแบบคงที่

ฟีเจอร์ที่ uv ตัดออก

  • ความเร็วของ uv มาจาก การตัดฟีเจอร์ที่ไม่จำเป็นออก
    • ไม่รองรับ .egg: pip ยังจัดการอยู่ แต่ uv ตัดออกทั้งหมด
    • ไม่สนใจ pip.conf: ตัดทั้งไฟล์ตั้งค่า ตัวแปรสภาพแวดล้อม และตรรกะการสืบทอดค่าออกไป
    • ปิดการคอมไพล์ bytecode: ไม่แปลง .py เป็น .pyc จึงลดเวลาในการติดตั้ง
    • บังคับใช้ virtual environment: ไม่ติดตั้งลง system Python โดยตรง จึงไม่ต้องมีโค้ดตรวจสอบสิทธิ์และความปลอดภัยส่วนนี้
    • ปฏิบัติตามสเปกอย่างเข้มงวด: ปฏิเสธแพ็กเกจที่ไม่ถูกต้อง เพื่อลดตรรกะการจัดการข้อยกเว้น
    • เมิน upper bound ของ requires-python: ละเลยข้อจำกัดเชิงป้องกันอย่าง python<4.0 เพื่อลด การแก้ dependency (backtracking)
    • ให้ความสำคัญกับ index แรก: หากพบแพ็กเกจในหลาย index จะหยุดที่ตัวแรกทันที ช่วย ป้องกันการโจมตีแบบ dependency confusion และลดการร้องขอเครือข่าย
  • ทั้งหมดนี้คือตัวอย่างของ code path ที่ pip ต้องมี แต่ uv เลือกตัดทิ้ง

การปรับแต่งที่ทำได้แม้ไม่มี Rust

  • ความเร็วของ uv ส่วนใหญ่เกิดจาก การปรับแต่งด้านการออกแบบที่ไม่ขึ้นกับภาษา
    • ใช้ HTTP Range request เพื่อดาวน์โหลดเฉพาะ central directory ของไฟล์ wheel บางส่วน แทนการดาวน์โหลดทั้งไฟล์
    • ใช้ การดาวน์โหลดแบบขนาน เพื่อดึงหลายแพ็กเกจพร้อมกัน
    • ใช้ global cache และ hardlink ทำให้แม้ติดตั้งแพ็กเกจเดียวกันในหลาย virtual environment ก็ ไม่ใช้พื้นที่ดิสก์เพิ่ม
    • การแก้ dependency โดยไม่พึ่ง Python: parse TOML และเมทาดาทาของ wheel โดยตรง และจะรัน Python เฉพาะกรณีที่มีแค่ setup.py เท่านั้น
    • ใช้อัลกอริทึมแก้ dependency แบบ PubGrub ซึ่งเร็วกว่าแนวทาง backtracking ของ pip และอธิบายข้อผิดพลาดได้ชัดเจนกว่า

ส่วนที่ Rust มีส่วนช่วยจริง

  • Rust มีบทบาทสำคัญใน การปรับแต่งระดับล่าง บางส่วน
    • ใช้ zero-copy deserialization บนพื้นฐานของ rkyv เพื่อใช้งานข้อมูลในแคชได้โดยตรงโดยไม่ต้องคัดลอก
    • ใช้ โครงสร้างข้อมูลแบบ lock-free concurrency เพื่อให้เข้าถึงแบบขนานได้อย่างปลอดภัย
    • ไม่มีค่าใช้จ่ายในการเริ่มต้น interpreter: uv เป็น static binary เดี่ยว จึงตัดต้นทุนการสร้าง Python process ของ pip ออกไป
    • บีบอัดข้อมูลเวอร์ชันเป็นจำนวนเต็ม u64 ทำให้การเปรียบเทียบและแฮชทำได้เร็วขึ้น
  • องค์ประกอบเหล่านี้ช่วยเพิ่มประสิทธิภาพ แต่ ไม่ใช่สาเหตุหลักของความเร็วที่เพิ่มขึ้นทั้งหมด

บทเรียนสำคัญ

  • ความเร็วของ uv ไม่ได้มาจาก Rust แต่มาจากสิ่งที่มันเลือกจะไม่ทำ
  • มาตรฐาน PEP 518·517·621·658 วางรากฐานให้ การจัดการแพ็กเกจที่รวดเร็ว และ uv ก็นำสิ่งนี้ไปทำให้เกิดขึ้นจริงด้วย การตัด legacy ออกและตั้งสมมติฐานแบบสมัยใหม่
  • pip ก็สามารถทำ parallel download, global cache และการแก้ dependency แบบอิงเมทาดาทาได้ แต่ การต้องรักษา backward compatibility มา 15 ปี คืออุปสรรคสำคัญ
  • ผลลัพธ์คือ pip จึงแทบเลี่ยงไม่ได้ที่จะช้า และ มีเพียงเครื่องมือที่เริ่มต้นจากสมมติฐานใหม่เท่านั้นที่สามารถเพิ่มความเร็วได้อย่างถึงราก
  • บทเรียนสำหรับตัวจัดการแพ็กเกจอื่น ๆ คือ เมทาดาทาแบบคงที่, การสำรวจ dependency โดยไม่รันโค้ด, และโครงสร้างที่แก้ dependency ล่วงหน้าได้ เป็นสิ่งจำเป็น
  • Cargo และ npm ใช้แนวทางนี้อยู่แล้ว และ ระบบนิเวศที่ต้องรันโค้ดเพื่อตรวจสอบ dependency นั้นช้าโดยพื้นฐาน

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

 
GN⁺ 2025-12-27
ความคิดเห็นบน Hacker News
  • คิดว่าบทความนี้อธิบาย ประสิทธิภาพของ uv ได้ดีจากหลายมุม
    การที่เขียนด้วย Rust ก็ช่วยอยู่ แต่สิ่งที่มีบทบาทมากกว่ามากคือ ความพยายามในการทำให้เป็นมาตรฐาน ตลอด 10 ปีที่ผ่านมา ที่ทำให้ ecosystem ของ Python หลุดพ้นจากการพึ่งพา setup.py

    • ตอนเคยทำโปรเจกต์ Haskell ข้อดีอย่างหนึ่งไม่ใช่ตัวภาษาเอง แต่คือการคัดกรองหา ชุมชนผู้เชี่ยวชาญ ได้
      Rust เองก็เลือกใช้ได้ด้วยเหตุผลคล้ายกัน คือช่วยยกระดับศักยภาพของชุมชน
    • โปรเจกต์รีไรต์ด้วย Rust หลายตัวได้ อานิสงส์ แบบนี้
      สามารถย้อนดูบทเรียนจากความผิดพลาดเดิม ๆ แล้วออกแบบใหม่ให้ดีขึ้น พร้อมได้ข้อดีของ Rust เพิ่มมา กลายเป็นหมัดชุดแบบ ‘one-two punch’
  • เห็นด้วยกับข้อสรุปที่ว่า “uv ไม่ได้เร็วเพราะเป็น Rust แต่เร็วเพราะมี สิ่งที่ไม่ต้องทำ เยอะกว่า”
    แต่ก็ยังมองว่าเร็วเกินไปที่จะฟันธงปัจจัยเรื่องความเร็วโดยไม่มี benchmark
    PEP 518, 517, 621, 658 มีผลมากแน่ แต่ก็ยังสงสัยว่า การตัดความเข้ากันได้ย้อนหลัง มีส่วนแค่ไหน
    อีกทั้งยังไม่ได้พูดถึงว่าการเลือกภาษามีผลต่อการ optimize อย่างไรบ้าง
    เรื่องที่ TOML parser ของ Cargo เร็วกว่า Python มากก็น่าสนใจ

    • การเอา pip เวอร์ชันเก่ากับปัจจุบันมาเทียบกันก็นับเป็น benchmark แบบหนึ่งได้
      จริง ๆ แล้ว TOML ถูกอ่านเฉพาะตอน build จึงไม่ได้มีสัดส่วนมากนัก แต่การ แพร่หลายของ wheel มีส่วนช่วยให้เร็วขึ้น
      บทความอ้างอิงที่เกี่ยวข้อง: setup.py deprecated, wheels are faster
  • การทำ zero-copy deserialization ด้วย rkyv ไม่ใช่เทคนิคที่มีเฉพาะ Rust
    ภาษาระดับล่างอย่าง C/C++ ก็ทำได้เหมือนกัน

    • ที่บอกว่า “เฉพาะ Rust” ตรงนี้ ผมเข้าใจว่าเขาหมายถึง “ทำใน Python ไม่ได้” มากกว่า
      ประเด็น “ไม่มีการเริ่มต้น interpreter” ก็อยู่ในบริบทเดียวกัน
    • เพราะการทำ zero-copy ด้วย Python ทำได้ยาก จึงยอมรับได้ว่า Rust ทำสิ่งนี้ได้ อย่างปลอดภัยและง่ายกว่า
    • สุดท้ายแล้วประเด็นนี้คือการเปรียบเทียบ Rust กับ Python
  • เนื้อหาของบทความดี แต่ สำนวนที่ถูกขัดเกลาด้วย LLM ให้ความรู้สึกประดิษฐ์เกินไป
    สักวันหนึ่งอาจจะมียุคที่เราต้องคอยกู้บทความที่โดน LLM ทำให้เสียทรงกลับมาให้เป็นมนุษย์อีกครั้ง

    • ผู้เขียนคนนี้เคยขึ้น HN ด้วยบทความเรื่อง SBOM และ Lockfile ด้วย
      ดูเป็นผู้เชี่ยวชาญด้าน supply chain security แต่บทความนั้นก็ยังให้ความรู้สึกว่าโดนสำนวน คลุมเครือแบบ LLM กลบไปเหมือนกัน
    • ในทางกลับกัน บางคนกลับไม่รู้สึกถึงกลิ่น LLM เลย และมองว่าอ่านได้เป็นธรรมชาติ
    • ตอนนี้ถ้าผมอ่านแล้วได้กลิ่น ความเป็น AI ก็จะปิดทันที
      เพราะ prompt แบบตายตัวทำให้ทุกบทความคล้ายกันไปหมด จนน่าเสียดายที่อินเทอร์เน็ตเหมือนพูดด้วยเสียงเดียวกันทั้งหมด
  • ไม่ค่อยเข้าใจบรรยากาศที่ตื่นเต้นกับความเร็วของ uv กันมากขนาดนี้
    ผู้ใช้ Python ส่วนใหญ่น่าจะไม่ได้จัดความเร็วในการติดตั้งแพ็กเกจไว้แม้แต่ใน 10 เรื่องที่กังวลที่สุด
    ผมเองก็ใช้ Python ทุกวัน แต่ไม่ได้รู้สึกมากนัก

    • ที่บริษัทเก่า การอัปเดต dependency ด้วย poetry ใช้เวลา 5–30 นาที
      ถ้าล้มเหลวก็ต้องรออีก 30 นาที และ uv ให้ประสบการณ์ที่ ลื่นไหลน่าใช้มาก จริง ๆ
    • ใช้ Python มาเกิน 20 ปีแล้ว และ pip install กินเวลาส่วนใหญ่ของการ deploy อยู่บ่อยครั้ง
      ใช้เวลาไปมากกับการพยายามทำ caching เพื่อเร่งความเร็ว
    • ในแอป monolithic สำหรับงานของผม poetry install ใช้ 2 นาที แต่ uv sync เสร็จในไม่กี่วินาที
      ประหยัดได้ครั้งละ 2 นาทีในทุก CI พอสะสมแล้วมีผลมาก
    • ตอนรัน uvx sometool ก็ใช้เวลาแค่ไม่กี่วินาทีในการ สร้าง virtual environment และติดตั้ง dependency จน workflow เปลี่ยนไปเลย
    • ในฐานะคนใช้ Python มานาน ความเร็วของ uv คือระดับ “เปลี่ยนคุณภาพชีวิต”
      ตอนนี้กลับไปทำโปรเจกต์ที่ไม่มี uv ได้ยากแล้ว
  • เทคนิคการปรับแต่งความเร็ว บางอย่างของ uv ดูเหมือนจะย้ายไปใส่ใน pip ได้เหมือนกัน
    เช่น การดาวน์โหลดแบบขนาน, การสร้าง .pyc แบบหน่วงเวลา, การไม่สนใจ egg, การเช็กเวอร์ชัน เป็นต้น
    แต่ uv จัดการ venv ได้ดีมากจนดูเหมือนไม่มีเหตุผลนักที่จะไปปรับ pip แล้ว
    สุดท้ายประเด็นก็คือ “ไม่ได้เร็วเพราะ Rust อย่างเดียว” ดังนั้น pip เองก็ยังมีช่องให้เร็วขึ้นได้

  • โปรแกรมเมอร์ที่เลือกใช้ภาษาที่เร็ว มักจะมี mindset เรื่อง performance optimization อยู่แล้ว
    ทัศนคติแบบนั้นต่างหากที่กำหนดประสิทธิภาพได้มากกว่าตัวภาษาเอง

  • น่าสนใจที่ uv เพิกเฉยต่อเพดาน python<4.0
    แพ็กเกจส่วนใหญ่ตั้งค่านี้แบบ ป้องกันไว้ก่อน เพราะกลัวว่าจะพังกับ Python 4 ทั้งที่ในความเป็นจริงอาจไม่มีปัญหา
    การตั้งเพดานแบบนี้จึงเป็นความพยายามแก้ปัญหาสมมุติมากกว่าปัญหาที่เกิดขึ้นจริง

    • uv ไม่ได้เพิกเฉยต่อเพดานทั้งหมด แต่ดูเหมือนจะเพิกเฉพาะ กรณี 4.0
      ข้อจำกัดอย่าง python<3.0 ยังสำคัญอยู่ ดังนั้นกรณีแบบนั้นควรถูกบล็อกไว้
  • การที่ PEP 658 ถูกนำมาใช้ในปี 2023 และ uv โผล่มาในปี 2024 ไม่ใช่เรื่องบังเอิญ
    เพราะ ecosystem พร้อมแล้ว เครื่องมืออย่าง uv จึงเกิดขึ้นได้
    แต่ก็ยังสงสัยว่าทำไมผู้ดูแลแพ็กเกจถึงยอมรับการเปลี่ยนแปลงนี้
    คนที่ใช้ setup.py ได้ดีอยู่แล้วก็น่าจะมี แล้วอะไรคือแรงจูงใจให้ย้ายไป pyproject.toml

    • จริง ๆ แล้ว setup.py สร้างความลำบากให้หลายคน
      ยกตัวอย่างเช่นแม้แต่ Requests ก็ยังไม่เข้ากันกับ PEP 517/518/621 อย่างสมบูรณ์
      minor release ถูกเลื่อนมาแล้วปีครึ่ง และระหว่างนั้นก็เกิด ปัญหาในการ build ด้วย
    • เพราะการประกาศแบบ static ทั้ง ปลอดภัยกว่าและได้เปรียบด้านประสิทธิภาพ
      แต่ก็ยังสงสัยว่าเหตุใด pip ถึงยังใช้ประโยชน์จากสิ่งนี้ได้ไม่เต็มที่
  • ประโยคที่ว่า “เส้นทางโค้ดที่ไม่ต้องทำ ก็ไม่ต้องรอ” นั้นไม่ค่อยแม่นนัก
    มีเพียงโค้ดที่ไม่ถูกรันเท่านั้นที่ช่วยประหยัดเวลาได้
    ตัวอย่างเช่น ต่อให้ไม่มีการรองรับ .egg ถ้ามันเป็น ฟอร์แมตที่เลิกใช้ไปแล้ว ก็อาจไม่กระทบความเร็วอยู่ดี
    ถ้ามี ข้อมูลเชิงปริมาณ ว่าแต่ละอย่างช่วยประหยัดเวลาไปเท่าไรจริงก็คงจะดีกว่านี้
    ถึงอย่างนั้น โดยรวมก็ยังเป็นบทความที่เรียบเรียงมาได้ดี