왜 coreutils가 Python보다 느리게 정렬됩니까?


20

파이썬의 정렬 기능 속도를 테스트하기 위해 다음 스크립트를 작성했습니다.

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

그런 다음 이것을 sort1000 만 줄이 포함 된 파일 의 coreutils 명령 과 비교했습니다 .

$ 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

내장 명령은 4 개의 CPU를 모두 사용했지만 (Python은 하나만 사용했지만) 실행하는 데 약 3 배가 걸렸습니다! 무엇을 제공합니까?

우분투 12.04.5 (32 비트), Python 2.7.3 및 sort8.13을 사용하고 있습니다


@goldilocks 그렇습니다. 사용자 대 실시간을보십시오.
augurar

허-네 말이 맞아. 분명히 coreutils 8.6에서 병렬화되었습니다.
goldilocks

사용 가능한 모든 실제 메모리 를 사용 --buffer-size하도록 지정하고 sort이것이 도움이되는지 확인할 수 있습니까?
iruvar

@ 1_CR 1GB 버퍼로 시도했지만 타이밍에 큰 변화가 없었습니다. 약 .6GB 만 사용했기 때문에 버퍼 크기를 늘리면 도움이되지 않습니다.
augurar

답변:


22

Izkata의 의견 은 로케일 별 비교라는 답을 보여주었습니다. 이 sort명령은 환경에 의해 표시된 로케일을 사용하는 반면 Python은 기본적으로 바이트 순서 비교입니다. UTF-8 문자열을 비교하는 것은 바이트 문자열을 비교하는 것보다 어렵습니다.

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

어떻게에 대한.


그리고 UTF-8 문자열을 어떻게 비교합니까?
Gilles 'SO- 악마 그만

@Gilles locale.strxfrm정렬하는 데 사용 하도록 Python 스크립트를 수정하면 스크립트는 ~ 32 초가 걸리지 만 여전히 빠릅니다 sort.
augurar

3
Python 2.7.3은 바이트 비교를 수행하지만 Python3은 유니 코드 단어 비교를 수행합니다. Python3.3은이 "테스트"에서 Python2.7보다 두 배 느립니다. 원시 바이트를 유니 코드 표현으로 패킹하는 오버 헤드는 Python 2.7.3이 수행해야하는 이미 중요한 패킹 오브젝트보다 훨씬 높습니다.
Anthon

2
나는 cut다른 사람들 과 같은 둔화를 발견했습니다 . 내가 가진 여러 컴퓨터 export LC_ALL=C에서 .bashrc. 그러나주의하십시오 : 이것은 단지 예를 들기 위해 본질적으로 깨집니다 wc(제외 wc -l). "나쁜 바이트"는 전혀 계산되지 않습니다 ...
Walter Tross

1
이러한 성능 문제가 발생합니다 grep: 당신이 얻을 수있는 실질적인 특히 수행 할 때, UTF-8을 사용하지 않도록 설정하여 대용량 파일을 grepping 때 성능 향상grep -i
애드리안 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

자, 파이썬이 훨씬 빠릅니다. 그러나 sort숫자로 정렬하도록 지시 하여 coreutils를 더 빠르게 만들 수 있습니다 .

$ 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매개 변수를 조정하여 더 빠르게 만들 수는 있지만). 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

파이썬은 여전히 ​​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 분류 방법을 지정하면 동일한 속도를 확인하기 위해 조정 list.sort()을 테스트하려고 시도하는 것보다 더 많은 파이썬 퓨를 가진 사람이 있습니다.


5
파이썬 정렬은 마지막 샘플 인 ASCII 숫자 순서에 따라 추가 속도 이점이 있습니다. sort대문자 / 소문자 비교를 위해 약간의 추가 작업을 수행하는 것 같습니다.
이즈 카타

@ 이즈 카타! 아래 답변을 참조하십시오.
augurar

1
실제로 파이썬은 원시 stdin입력 에서 내부 문자열을 작성하는 데 약간의 오버 헤드가 있습니다. 숫자 그 변환 ( lines = map(int, list(stdin))) 다시 ( stdout.writelines(map(str,lines))) 전체 정렬이 느리게 이동하게 최대 0.234s에서 실제 내 컴퓨터에 0.720s에.
Anthon

6

두 가지 구현은 모두 C에 있으므로 레벨링 경기장이 있습니다. Coreutils는 sort 분명히 mergesort 알고리즘을 사용합니다 . Mergesort는 고정 된 수의 비교를 수행하여 입력 크기, 즉 큰 O (n log n)에 대해 로그 적으로 증가 합니다.

파이썬의 정렬은 고유 한 하이브리드 병합 / 삽입 정렬 인 timsort를 사용합니다.이 정렬은 O (n)의 최상의 시나리오 (아마도 이미 정렬 된 목록)와 비교할 수 있지만 일반적으로 로그입니다 (논리적으로는 정렬 할 때 일반적인 경우에 대수보다 나을 수 없습니다).

두 개의 다른 로그 정렬이 주어지면, 하나는 특정 데이터 세트에서 다른 것보다 이점을 가질 수 있습니다. 기존의 병합 정렬은 다양하지 않으므로 데이터에 관계없이 동일하게 수행되지만 예를 들어, 퀵 정렬 (대수)은 일부 데이터에서는 더 잘 수행되지만 다른 데이터에서는 더 잘 수행됩니다.

3의 요인 (또는 sort병렬화 된 이후 3 이상 )은 꽤 조금 큽니다. sort디스크 로 스왑하는 것과 같은 우발적 인 상황이 없는지 궁금합니다 ( -T옵션이 의미하는 것처럼 보입니다). 그러나 시스템 시간 대 사용자 시간이 적다는 것은 이것이 문제가 아니라는 것을 의미합니다.


두 구현이 모두 C로 작성되었다는 것이 좋은 지적입니다. 파이썬에서 정렬 알고리즘을 구현하면 훨씬 느려질 것입니다.
augurar

그건 그렇고, 파일은 0과 1 사이에서 무작위로 생성 된 float 값으로 구성되므로 악용하기에는 너무 많은 구조가 없어야합니다.
augurar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.