ทำไม coreutils ถึงเรียงช้ากว่า Python


20

ฉันเขียนสคริปต์ต่อไปนี้เพื่อทดสอบความเร็วของฟังก์ชันการเรียงลำดับของ Python:

from sys import stdin, stdout
lines = list(stdin)
lines.sort()
stdout.writelines(lines)

ฉันเปรียบเทียบสิ่งนี้กับsortคำสั่งcoreutils บนไฟล์ที่มี 10 ล้านบรรทัด:

$ time python sort.py <numbers.txt >s1.txt
real    0m16.707s
user    0m16.288s
sys     0m0.420s

$ time sort <numbers.txt >s2.txt 
real    0m45.141s
user    2m28.304s
sys     0m0.380s

คำสั่งในตัวใช้ CPU ทั้งสี่ตัว (Python ใช้เพียงอันเดียว) แต่ใช้เวลาในการรันนานกว่า 3 เท่า! สิ่งที่ช่วยให้?

ฉันใช้ Ubuntu 12.04.5 (32 บิต), Python 2.7.3 และsort8.13


@goldilocks ใช่มันคือดูที่ผู้ใช้เทียบกับเวลาจริง
augurar

อืม - คุณพูดถูก เห็นได้ชัดว่ามันขนานใน coreutils 8.6
goldilocks

คุณสามารถใช้--buffer-sizeเพื่อระบุว่าsortใช้หน่วยความจำกายภาพที่มีอยู่ทั้งหมดและดูว่ามีประโยชน์หรือไม่
iruvar

@ 1_CR พยายามใช้บัฟเฟอร์ขนาด 1 GB ไม่มีการเปลี่ยนแปลงเวลาอย่างมีนัยสำคัญ ใช้เพียงประมาณ 0.6 GB เท่านั้นดังนั้นการเพิ่มขนาดบัฟเฟอร์ต่อไปจะไม่ช่วยได้
สิงหาคม

คำตอบ:


22

ความคิดเห็นของ Izkataเปิดเผยคำตอบ: การเปรียบเทียบเฉพาะสถานที่ sortคำสั่งใช้สถานที่ที่ระบุโดยสภาพแวดล้อมในขณะที่ค่าเริ่มต้นของงูใหญ่ที่จะเปรียบเทียบเพื่อ byte การเปรียบเทียบสตริง UTF-8 นั้นยากกว่าการเปรียบเทียบสตริงไบต์

$ time (LC_ALL=C sort <numbers.txt >s2.txt)
real    0m5.485s
user    0m14.028s
sys     0m0.404s

วิธีการเกี่ยวกับที่


และจะเปรียบเทียบกับสตริง UTF-8 ได้อย่างไร
Gilles 'หยุดความชั่วร้าย'

@Gilles การแก้ไขสคริปต์ Python เพื่อใช้locale.strxfrmในการจัดเรียงสคริปต์ใช้เวลา ~ 32 วินาทียังเร็วกว่าsortแต่ก็น้อยกว่ามาก
augurar

3
Python 2.7.3 กำลังทำการเปรียบเทียบแบบไบต์ แต่ Python3 จะทำการเปรียบเทียบคำแบบ Unicode Python3.3 นั้นช้ากว่า Python2.7 สองเท่าสำหรับ "test" นี้ โอเวอร์เฮดของการบรรจุไบต์ดิบลงในการแสดง Unicode นั้นสูงกว่าวัตถุการบรรจุที่สำคัญที่ Python 2.7.3 ต้องทำ
Anthon

2
ฉันพบว่าการชะลอตัวแบบเดียวกันกับcutและอื่น ๆ ด้วย บนเครื่องหลายตอนนี้ผมมีในexport LC_ALL=C .bashrcแต่ระวัง: นี่เป็นการทำลายwc(ยกเว้นwc -l) เพียงเพื่อตั้งชื่อตัวอย่าง "ไบต์ที่ไม่ดี" จะไม่ถูกนับเลย ...
Walter Tross

1
ปัญหาประสิทธิภาพการทำงานนี้ก็เกิดขึ้นด้วยgrep: คุณสามารถปรับปรุงประสิทธิภาพได้อย่างมากเมื่อทำการ grepping ไฟล์ขนาดใหญ่โดยการปิดการใช้งาน UTF-8 โดยเฉพาะอย่างยิ่งเมื่อทำgrep -i
Adrian Pronk

7

นี่เป็นการวิเคราะห์พิเศษมากกว่าคำตอบจริง แต่ดูเหมือนจะแตกต่างกันไปขึ้นอยู่กับข้อมูลที่เรียงลำดับ ก่อนอ่านฐาน:

$ printf "%s\n" {1..1000000} > numbers.txt

$ time python sort.py <numbers.txt >s1.txt
real    0m0.521s
user    0m0.216s
sys     0m0.100s

$ time sort <numbers.txt >s2.txt
real    0m3.708s
user    0m4.908s
sys     0m0.156s

ตกลงหลามเป็นมากได้เร็วขึ้น อย่างไรก็ตามคุณสามารถทำให้ coreutils sortเร็วขึ้นโดยบอกให้เรียงลำดับตัวเลข:

$ time sort <numbers.txt >s2.txt 
real    0m3.743s
user    0m4.964s
sys     0m0.148s

$ time sort -n <numbers.txt >s2.txt 
real    0m0.733s
user    0m0.836s
sys     0m0.100s

นั่นเป็นมากเร็วขึ้น แต่ยังคงหลามชนะโดยอัตรากำไรกว้าง ตอนนี้ลองอีกครั้ง แต่ด้วยรายการที่ไม่มีการเรียงลำดับของตัวเลข 1M:

$ sort -R numbers.txt > randomized.txt

$ time sort -n <randomized.txt >s2.txt 
real    0m1.493s
user    0m1.920s
sys     0m0.116s

$ time python sort.py <randomized.txt >s1.txt
real    0m2.652s
user    0m1.988s
sys     0m0.064s

coreutils sort -nเร็วกว่าสำหรับข้อมูลตัวเลขที่ไม่เรียงลำดับ (แม้ว่าคุณอาจสามารถปรับแต่งcmpพารามิเตอร์ของ python sort เพื่อทำให้เร็วขึ้น) Coreutils sortยังคงช้าลงอย่างมีนัยสำคัญโดยไม่มี-nธง แล้วตัวละครแบบสุ่มไม่ใช่เลขบริสุทธิ์ล่ะ?

$ tr -dc 'A-Za-z0-9' </dev/urandom | head -c1000000 | 
    sed 's/./&\n/g' > random.txt

$ time sort  <random.txt >s2.txt 
real    0m2.487s
user    0m3.480s
sys     0m0.128s

$ time python sort.py  <random.txt >s2.txt 
real    0m1.314s
user    0m0.744s
sys     0m0.068s

Python ยังคงเต้น coreutils แต่มีขนาดเล็กกว่าสิ่งที่คุณแสดงในคำถามของคุณ น่าแปลกที่มันยังเร็วขึ้นเมื่อดูข้อมูลตัวอักษรบริสุทธิ์:

$ tr -dc 'A-Za-z' </dev/urandom | head -c1000000 |
    sed 's/./&\n/g' > letters.txt

$ time sort   <letters.txt >s2.txt 
real    0m2.561s
user    0m3.684s
sys     0m0.100s

$ time python sort.py <letters.txt >s1.txt
real    0m1.297s
user    0m0.744s
sys     0m0.064s

เป็นสิ่งสำคัญที่จะต้องทราบว่าทั้งสองไม่ได้ผลิตผลลัพธ์เรียงเดียวกัน:

$ echo -e "A\nB\na\nb\n-" | sort -n
-
a
A
b
B

$ echo -e "A\nB\na\nb\n-" | python sort.py 
-
A
B
a
b

ผิดปกติพอ--buffer-sizeตัวเลือกไม่ได้สร้างความแตกต่างมาก (หรือใด ๆ ) ในการทดสอบของฉัน สรุปได้ว่าน่าจะเพราะขั้นตอนวิธีการที่แตกต่างกันดังกล่าวในคำตอบของ goldilock หลามsortดูเหมือนว่าจะได้เร็วขึ้นในกรณีส่วนใหญ่ แต่ตัวเลข GNU sortเต้นบนตัวเลขไม่ได้เรียงลำดับ1


OP อาจพบสาเหตุที่แท้จริงแต่เพื่อความสมบูรณ์นี่เป็นการเปรียบเทียบขั้นสุดท้าย:

$ time LC_ALL=C sort   <letters.txt >s2.txt 
real    0m0.280s
user    0m0.512s
sys     0m0.084s


$ time LC_ALL=C python sort.py   <letters.txt >s2.txt 
real    0m0.493s
user    0m0.448s
sys     0m0.044s

1 คนที่มี python-fu ยิ่งกว่าที่ฉันควรลองทดสอบการปรับแต่งlist.sort()เพื่อดูความเร็วเดียวกันสามารถทำได้โดยการระบุวิธีการเรียงลำดับ


5
การจัดเรียง Python มีข้อได้เปรียบด้านความเร็วเพิ่มเติมโดยอิงจากตัวอย่างล่าสุดของคุณ: ลำดับตัวเลข ASCII sortดูเหมือนว่าจะทำงานเพิ่มเล็กน้อยสำหรับการเปรียบเทียบตัวพิมพ์ใหญ่ / ตัวพิมพ์เล็ก
Izkata

@ อิซกะตะแค่นั้นแหละ! ดูคำตอบของฉันด้านล่าง
สิงหาคม

1
จริง ๆ แล้วหลามมีค่าใช้จ่ายค่อนข้างน้อยในการสร้างสตริงภายในจากstdinอินพุตดิบ การแปลงตัวเลขเหล่านี้เป็นตัวเลข ( lines = map(int, list(stdin))) และย้อนกลับ ( stdout.writelines(map(str,lines))) ทำให้การเรียงลำดับทั้งหมดช้าลงมากขึ้นจาก 0.234 จริงไปเป็น 0.720s บนเครื่องของฉัน
Anthon

6

การใช้งานทั้งสองอยู่ใน C ดังนั้นสนามเด็กเล่นในระดับนั้น coreutils sort เห็นได้ชัดว่าใช้mergesortอัลกอริทึม Mergesort ทำการเปรียบเทียบจำนวนคงที่ซึ่งเพิ่มลอการิทึมตามขนาดอินพุตเช่นใหญ่ O (n log n)

การเรียงลำดับของไพ ธ อนใช้การเรียงลำดับการผสาน / การแทรกแบบไฮบริดซึ่งเป็นเอกลักษณ์timsortซึ่งจะทำการเปรียบเทียบจำนวนตัวแปรกับสถานการณ์กรณีที่ดีที่สุดของ O (n) - สันนิษฐานว่าอยู่ในรายการเรียงลำดับแล้ว - แต่โดยทั่วไปแล้วคือลอการิทึม ไม่สามารถรับได้ดีกว่าลอการิทึมสำหรับกรณีทั่วไปเมื่อเรียงลำดับ)

เมื่อได้รับลอการิทึมที่แตกต่างกันสองประเภทเราสามารถได้เปรียบกว่าอีกชุดหนึ่งในชุดข้อมูลบางชุด การเรียงแบบผสานแบบดั้งเดิมนั้นไม่แตกต่างกันดังนั้นมันจะทำงานแบบเดียวกันโดยไม่คำนึงถึงข้อมูล แต่เช่น quicksort (เช่นลอการิทึม) ซึ่งแตกต่างกันจะทำงานได้ดีกว่ากับข้อมูลบางอย่าง แต่จะแย่กว่ากัน

ปัจจัยที่สาม (หรือมากกว่า 3 เนื่องจากsortเป็น parallelized) ไม่น้อยแม้ว่าซึ่งทำให้ผมสงสัยว่ามีไม่ฉุกเฉินบางอย่างที่นี่เช่นsortการสลับไปยังดิสก์ (คน-Tตัวเลือกที่จะดูเหมือนจะบ่งบอกว่ามันไม่) อย่างไรก็ตามเวลาของคุณต่ำเมื่อเทียบกับผู้ใช้แสดงว่านี่ไม่ใช่ปัญหา


จุดที่ดีว่าการใช้งานทั้งสองนั้นเขียนขึ้นใน C. ฉันแน่ใจว่าถ้าฉันใช้อัลกอริทึมการเรียงลำดับใน Python มันจะช้ากว่ามาก
สิงหาคม

อย่างไรก็ตามไฟล์นี้ประกอบด้วยค่าทศนิยมที่สร้างขึ้นแบบสุ่มระหว่าง 0 ถึง 1 ดังนั้นจึงไม่ควรใช้โครงสร้างที่มีประโยชน์มากเกินไป
สิงหาคม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.