Coreutils neden Python'dan daha yavaş sıralanıyor?


20

Python'un sıralama işlevinin hızını test etmek için aşağıdaki komut dosyasını yazdım:

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

Daha sonra bunu sort10 milyon satır içeren bir dosyadaki coreutils komutuyla karşılaştırdım :

$ 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

Yerleşik komut dört CPU'nun hepsini kullandı (Python yalnızca bir tane kullandı) ancak çalışması yaklaşık 3 kat daha uzun sürdü! Ne oluyor?

Ubuntu 12.04.5 (32 bit), Python 2.7.3 ve sort8.13 kullanıyorum


@goldilocks Evet, kullanıcıya gerçek zamanlıya bakın.
augurar

Ha - haklısın. Görünüşe göre, çekirdekler 8.6'da paralelleştirildi.
goldilocks

Eğer kullanabilir miyim --buffer-sizebelirtmek için sortkullanılması mevcut tüm fiziksel bellek ve görmek eğer yardımcı olur?
iruvar

@ 1_CR 1 GB arabellek ile denendi, zamanlamada önemli bir değişiklik yok. Sadece yaklaşık 6 GB kullanıyordu, bu nedenle arabellek boyutunu daha da arttırmak yardımcı olmaz.
augurar

Yanıtlar:


22

Izkata'nın yorumu şu cevabı ortaya çıkardı: yerele özgü karşılaştırmalar. sortKomut, bir bayt sırası karşılaştırmak için Python varsayılan ise çevre ile gösterilen yerel kullanır. UTF-8 dizelerini karşılaştırmak bayt dizelerini karşılaştırmaktan daha zordur.

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

Peki ya bu.


Ve UTF-8 dizelerini nasıl karşılaştırıyorlar?
Gilles 'SO- kötü olmayı kes'

@Gilles locale.strxfrmSıralamak için kullanılacak Python betiğini değiştirerek, betiğin ~ 32 saniye sürdü, yine de daha hızlı sortama çok daha az.
augurar

3
Python 2.7.3, bayt karşılaştırma yapıyor, ancak Python3 unicode kelime karşılaştırma yapıyor. Python3.3, bu "test" için yaklaşık iki kat daha yavaş Python2.7'dir. Ham baytların Unicode temsiline paketlenmesi yükü, Python 2.7.3'ün yapması gereken önemli paketleme nesnelerinden bile daha yüksektir.
Anthon

2
Aynı yavaşlamayı cutdiğerlerinde de buldum . Birçok makineye Şimdi sahip export LC_ALL=Cbölgesi .bashrc. Ancak dikkat: bu sadece bir örnek vermek gerekirse wc(hariç wc -l) kırılır . "Kötü bayt" hiç sayılmaz ...
Walter Tross

1
Bu performans sorunu ayrıca şu durumlarda da ortaya çıkar grep: UTF-8'i devre dışı bırakarak büyük dosyaları selamlarken, özellikle de yaparken önemli bir performans artışı elde edebilirsinizgrep -i
Adrian Pronk

7

Bu, gerçek bir cevaptan daha fazla bir analizdir, ancak sıralanan verilere bağlı olarak değişiyor gibi görünmektedir. İlk olarak, bir temel okuma:

$ 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

Tamam, python çok daha hızlı. Ancak, coreutils'i sortsayısal olarak sıralamasını söyleyerek daha hızlı yapabilirsiniz :

$ 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

Bu çok daha hızlı ama python hala geniş bir farkla kazanıyor. Şimdi tekrar deneyelim, ancak 1M numaralarının sıralanmamış bir listesiyle:

$ 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, sıralanmamış sayısal veriler için daha hızlıdır (ancak cmpdaha hızlı hale getirmek için python sort parametresini değiştirebilirsiniz). Coreutils bayrak sortolmadan hala önemli ölçüde yavaştır -n. Peki ya saf rakamlar değil rastgele karakterler?

$ 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 hala coreutils'i yeniyor, ancak sorunuzda gösterdiğinizden çok daha küçük bir marjla. Şaşırtıcı bir şekilde, saf alfabetik verilere bakıldığında hala daha hızlı:

$ 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

Bu ikisinin aynı sıralı çıktıyı üretmediğine dikkat etmek de önemlidir:

$ 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

İşin garibi, --buffer-sizeseçenek testlerimde fazla (veya herhangi bir) fark yaratmadı. Sonuç olarak, muhtemelen goldilock'un cevabında belirtilen farklı algoritmalar nedeniyle, python sortçoğu durumda daha hızlı görünüyor, ancak sayısal GNU sortbunu sıralanmamış sayılar 1'e atıyor .


OP muhtemelen temel nedeni buldu, ancak tamlık uğruna, son bir karşılaştırma:

$ 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 Ben daha hızlı python-fu olan list.sort()biri aynı hızı görmek için tweaking denemek gerekir sıralama yöntemi belirleyerek elde edilebilir.


5
Python sıralama, son örneğinize bağlı olarak ek bir hız avantajına sahiptir: ASCII sayısal sırası. sortbüyük / küçük harf karşılaştırmaları için biraz fazladan iş yapıyor gibi görünüyor.
Izkata

@Izkata İşte bu! Cevabımı aşağıda görebilirsiniz.
augurar

1
Aslında python, ham dizgiden dahili dizelerini yaratan biraz ek yüke sahiptir stdin. Bunları sayılara ( lines = map(int, list(stdin))) ve geri ( stdout.writelines(map(str,lines))) dönüştürmek , makinemdeki 0.234s'den 0.720s'e kadar tüm sıralamayı yavaşlatır .
Anthon

6

Her iki uygulama da C cinsindendir, bu nedenle orada eşit bir oyun alanı vardır. Coreutils sort görünüşe göre mergesort algoritmasını kullanıyor . Mergesort, giriş boyutuna logaritmik olarak artan sabit sayıda karşılaştırma yapar, yani büyük O (n log n).

Python'un sıralama, sıralama benzersiz hibrit birleştirme / ekleme özelliğini kullanan timsort muhtemelen, bir zaten sıralanmış listesinde - - O (n) bir iyi senaryoya sahip karşılaştırmalar değişken sayıda yapacak, ama mantıksal olarak (genellikle logaritmik, teşekkür sıralama için genel durum için logaritmik daha iyi olamaz).

İki farklı logaritmik tür göz önüne alındığında, belirli bir veri setinde biri diğerine göre avantaj sağlayabilir. Geleneksel birleştirme sıralaması değişmez, bu nedenle verilerden bağımsız olarak aynı performansı gösterecektir, ancak örneğin, değişken olan quicksort (ayrıca logaritmik) bazı verilerde daha iyi, diğerlerinde daha kötü performans gösterecektir.

Üç faktör (veya sortparalel olduğundan beri 3'ten fazla ) oldukça azdır, bu da burada sortdiske takas gibi bazı beklenmedik durumlar olup olmadığını merak etmemizi sağlar ( -Tseçenek bunu ima eder gibi görünüyor). Ancak, düşük sys'e karşı kullanıcı süreniz bu sorunun olmadığını gösterir.


Her iki uygulamanın da C dilinde yazıldığı iyi bir nokta. Python'da bir sıralama algoritması uygularsam, çok, çok daha yavaş olacağından eminim.
augurar

Bu arada, dosya 0 ile 1 arasında rastgele oluşturulmuş şamandıra değerlerinden oluşur, bu nedenle kullanmak için çok fazla yapı olmamalıdır.
augurar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.