파일 1..64은 각각 160MB이며 RAM 디스크에 저장됩니다.
에 의해 생성 된:
seq 120 | parallel -k 'seq {}0000000 {}9999999 | fmt -30' | head -c 10G > 10G
parallel --pipepart --block -1 -a 10G -j 64 'cat > {#}'
nocat
:
#!/bin/bash
export LC_ALL=C
sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 1; sort ) < 1) \
<((rm 2; sort ) < 2) ) \
<(sort -m \
<((rm 3; sort ) < 3) \
<((rm 4; sort ) < 4) ) ) \
<(sort -m \
<(sort -m \
<((rm 5; sort ) < 5) \
<((rm 6; sort ) < 6) ) \
<(sort -m \
<((rm 7; sort ) < 7) \
<((rm 8; sort ) < 8) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 9; sort ) < 9) \
<((rm 10; sort ) < 10) ) \
<(sort -m \
<((rm 11; sort ) < 11) \
<((rm 12; sort ) < 12) ) ) \
<(sort -m \
<(sort -m \
<((rm 13; sort ) < 13) \
<((rm 14; sort ) < 14) ) \
<(sort -m \
<((rm 15; sort ) < 15) \
<((rm 16; sort ) < 16) ) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 17; sort ) < 17) \
<((rm 18; sort ) < 18) ) \
<(sort -m \
<((rm 19; sort ) < 19) \
<((rm 20; sort ) < 20) ) ) \
<(sort -m \
<(sort -m \
<((rm 21; sort ) < 21) \
<((rm 22; sort ) < 22) ) \
<(sort -m \
<((rm 23; sort ) < 23) \
<((rm 24; sort ) < 24) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 25; sort ) < 25) \
<((rm 26; sort ) < 26) ) \
<(sort -m \
<((rm 27; sort ) < 27) \
<((rm 28; sort ) < 28) ) ) \
<(sort -m \
<(sort -m \
<((rm 29; sort ) < 29) \
<((rm 30; sort ) < 30) ) \
<(sort -m \
<((rm 31; sort ) < 31) \
<((rm 32; sort ) < 32) ) ) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 33; sort ) < 33) \
<((rm 34; sort ) < 34) ) \
<(sort -m \
<((rm 35; sort ) < 35) \
<((rm 36; sort ) < 36) ) ) \
<(sort -m \
<(sort -m \
<((rm 37; sort ) < 37) \
<((rm 38; sort ) < 38) ) \
<(sort -m \
<((rm 39; sort ) < 39) \
<((rm 40; sort ) < 40) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 41; sort ) < 41) \
<((rm 42; sort ) < 42) ) \
<(sort -m \
<((rm 43; sort ) < 43) \
<((rm 44; sort ) < 44) ) ) \
<(sort -m \
<(sort -m \
<((rm 45; sort ) < 45) \
<((rm 46; sort ) < 46) ) \
<(sort -m \
<((rm 47; sort ) < 47) \
<((rm 48; sort ) < 48) ) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 49; sort ) < 49) \
<((rm 50; sort ) < 50) ) \
<(sort -m \
<((rm 51; sort ) < 51) \
<((rm 52; sort ) < 52) ) ) \
<(sort -m \
<(sort -m \
<((rm 53; sort ) < 53) \
<((rm 54; sort ) < 54) ) \
<(sort -m \
<((rm 55; sort ) < 55) \
<((rm 56; sort ) < 56) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 57; sort ) < 57) \
<((rm 58; sort ) < 58) ) \
<(sort -m \
<((rm 59; sort ) < 59) \
<((rm 60; sort ) < 60) ) ) \
<(sort -m \
<(sort -m \
<((rm 61; sort ) < 61) \
<((rm 62; sort ) < 62) ) \
<(sort -m \
<((rm 63; sort ) < 63) \
<((rm 64; sort ) < 64) ) ) ) ) ) |
md5sum
withcat
:
#!/bin/bash
export LC_ALL=C
sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 1; sort ) < 1) \
<((rm 2; sort ) < 2) | cat) \
<(sort -m \
<((rm 3; sort ) < 3) \
<((rm 4; sort ) < 4) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 5; sort ) < 5) \
<((rm 6; sort ) < 6) | cat) \
<(sort -m \
<((rm 7; sort ) < 7) \
<((rm 8; sort ) < 8) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 9; sort ) < 9) \
<((rm 10; sort ) < 10) | cat) \
<(sort -m \
<((rm 11; sort ) < 11) \
<((rm 12; sort ) < 12) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 13; sort ) < 13) \
<((rm 14; sort ) < 14) | cat) \
<(sort -m \
<((rm 15; sort ) < 15) \
<((rm 16; sort ) < 16) | cat) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 17; sort ) < 17) \
<((rm 18; sort ) < 18) | cat) \
<(sort -m \
<((rm 19; sort ) < 19) \
<((rm 20; sort ) < 20) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 21; sort ) < 21) \
<((rm 22; sort ) < 22) | cat) \
<(sort -m \
<((rm 23; sort ) < 23) \
<((rm 24; sort ) < 24) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 25; sort ) < 25) \
<((rm 26; sort ) < 26) | cat) \
<(sort -m \
<((rm 27; sort ) < 27) \
<((rm 28; sort ) < 28) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 29; sort ) < 29) \
<((rm 30; sort ) < 30) | cat) \
<(sort -m \
<((rm 31; sort ) < 31) \
<((rm 32; sort ) < 32) | cat) | cat) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 33; sort ) < 33) \
<((rm 34; sort ) < 34) | cat) \
<(sort -m \
<((rm 35; sort ) < 35) \
<((rm 36; sort ) < 36) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 37; sort ) < 37) \
<((rm 38; sort ) < 38) | cat) \
<(sort -m \
<((rm 39; sort ) < 39) \
<((rm 40; sort ) < 40) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 41; sort ) < 41) \
<((rm 42; sort ) < 42) | cat) \
<(sort -m \
<((rm 43; sort ) < 43) \
<((rm 44; sort ) < 44) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 45; sort ) < 45) \
<((rm 46; sort ) < 46) | cat) \
<(sort -m \
<((rm 47; sort ) < 47) \
<((rm 48; sort ) < 48) | cat) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 49; sort ) < 49) \
<((rm 50; sort ) < 50) | cat) \
<(sort -m \
<((rm 51; sort ) < 51) \
<((rm 52; sort ) < 52) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 53; sort ) < 53) \
<((rm 54; sort ) < 54) | cat) \
<(sort -m \
<((rm 55; sort ) < 55) \
<((rm 56; sort ) < 56) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 57; sort ) < 57) \
<((rm 58; sort ) < 58) | cat) \
<(sort -m \
<((rm 59; sort ) < 59) \
<((rm 60; sort ) < 60) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 61; sort ) < 61) \
<((rm 62; sort ) < 62) | cat) \
<(sort -m \
<((rm 63; sort ) < 63) \
<((rm 64; sort ) < 64) | cat) | cat) | cat) | cat) | cat) | cat |
md5sum
유일한 차이점은 withcat
모든 것이 sort -m
로 파이프된다는 것입니다 cat
.
"고양이의 쓸모없는 이용" - 사람들은 당신 withcat
이 보다 느리게 믿게 만들 것입니다 nocat
. 그러나 그 반대가 사실입니다.
$ time bash nocat
c933d81faea7b8dec8eb64ca0b044d74 -
real 3m40.854s
user 2m48.687s
sys 0m49.135s
$ time bash withcat
c933d81faea7b8dec8eb64ca0b044d74 -
real 2m21.812s
user 2m16.651s
sys 1m36.135s
이 테스트는 64코어 시스템에서 실행되며 다른 작업은 수행하지 않습니다. 모든 것이 RAM에 있습니다(따라서 디스크 속도가 느려서가 아닙니다). 각 테스트는 3회 실행되었으며 가장 좋은 시간은 위에 나와 있습니다. 세 가지 테스트 모두 가장 좋은 시간으로부터 5초 이내에 완료되었습니다(그래서 이는 우연이 아니었습니다).
출력을 파이프하는 것이 더 빠른 이유는 무엇입니까 cat
?
편집하다
그룹 입력이 cat
더 커질까요? 그리고/또는 sort
각 라인의 출력을 플러시하시겠습니까?
이것을 테스트하기 위해 다음을 시도했습니다.
$ strace -ff sort -m <(sort 1) <(sort 2) 2>fromsort | cat >/dev/null
$ strace -ff sort -m <(sort 1 | cat ) <(sort 2 | cat) 2>fromcat | cat >/dev/null
cat
더 큰 청크로 분할되면 더 read
큰 청크가 반환될 것으로 예상됩니다. 하지만 그렇지 않습니다:
$ grep -E 'read|write' fromsort |field 1,5|sort | uniq -c
1 openat(AT_FDCWD, 3
8 pread64(3, =
1 read(3, 3771
40989 read(3, 4096
2 read(3, 832
1 read(3, unknown
1 read(4, 0
1 read(4, 2241
40959 read(4, 4096
1 write(1, 1916
81949 write(1, 4096
$ grep -E 'read|write' fromcat |field 1,5|sort | uniq -c
1 openat(AT_FDCWD, 3
8 pread64(3, =
1 read(3, 3771
40989 read(3, 4096
2 read(3, 832
1 read(3, unknown
1 read(4, 2241
40959 read(4, 4096
1 read(4, unknown
1 write(1, 1916
81949 write(1, 4096
두 경우 모두 read
합은 write
4K입니다.
(그런데,sort
하다파이프가 아닌 파일에서 읽는 경우 (더 많은) 더 큰 청크를 읽지만 여기서는 그렇지 않습니다.
편집 2
위의 목적은 추가가 cat
항상 쓸모없는 것은 아니라는 것을 보여주고 이것이 사실인 이유를 알아내는 것입니다.
목표는 데이터를 정렬하는 것이 아닙니다.
하지만 목표라면예전에는sort
데이터를 정렬하려면 내장된 데이터를 사용하면 어떨까요 --parallel
?
64코어 머신에서 기본적으로 사용되는 sort
것 같습니다 . CPU의 최대 800%를 사용하고 있음을 보여줍니다. 64개의 코어를 사용하도록 강제할 수 있습니다 .--parallel 8
top
--parallel 64
$ time sort {1..64} | md5sum
real 9m4.005s
user 29m56.454s
sys 5m49.560s
$ time sort --parallel 64 {1..64} | md5sum
real 6m50.332s
user 35m55.040s
sys 11m37.609s
따라서 GNU 정렬은 --parallel
위의 정렬보다 훨씬 느립니다. 이제 위 콘텐츠를 사용할 수 있습니다 parsort
.http://git.savannah.gnu.org/cgit/parallel.git/tree/src/parsort
답변1
이는 결코 '고양이의 쓸데없는 행동'이 아니다.
some_command | cat | some_command
이는 전통적인 의미에서 "고양이의 막다른 골목"이 아니며 일반적으로 인클로저에 대한 무지에서 발생합니다. 대신에 고양이의 역동성을 가지고 뭔가를 하려는 고의적인 시도처럼 보입니다. 이 경우에는 캐시라고 생각합니다.
내 두 번째 생각
읽기와 쓰기의 크기에 차이가 없더라도 감지할 수 없는 무언가가 작용할 수 있습니다.
첫째(매우 중요함):정렬되지 않은 배열보다 정렬된 배열을 처리하는 것이 더 빠른 이유는 무엇입니까?. CPU가 이 작업을 처리하는 순서를 변경하면 타이밍이 변경될 수 있습니다. 중단(및 다른 프로세스로 전환)하지 않고 cat
각 실행 시간을 더 길게 만드는 데 성공 하면 sort
이는 CPU의 분기 예측에 큰 영향을 미치고 시간이 더 길어지거나 짧아질 수 있습니다.
둘째, 읽기 수와 크기가 영향을 받지 않더라도 작업을 일시 중지(차단)해야 하는 횟수는 다를 수 있습니다. 이는 그 자체로 오버헤드를 늘리거나 줄일 수 있습니다. 따라서 읽기와 쓰기의 크기가 동일하더라도 (캐싱) 계층은 cat
시간당 읽기 및 쓰기 횟수를 줄일 수 있습니다.read()
write()
Cat은 정렬을 더 오래 기다리게 하여 중단 없이 더 많은 작업을 수행하고 각 프로세스가 차단되는 횟수를 줄일 수 있습니다. 이는 감지하기 어려울 것입니다.
나의 첫 생각
내 기대는 두 버전을 자체 스크립트에 넣고 strace -f
각각에서 실행하면 cat을 사용한 예제에서 읽기 또는/쓰기 호출이 더 적어질 것이라는 것입니다. 최소한 cat
. sort
사실, 나는 그것이 read()
충분히 큰 블록에 있지만 write()
단일 행에만 있기를 원합니다. 이는 자체적으로 배관되도록 설계되지 않았음을 의미합니다.
락탁이 지적했듯이그의 대답, cat은 128KB 청크로 읽습니다(이봐) 그러나 파이프는 일반적으로 64KB만 버퍼링합니다. 내 생각이 맞다면 cat이 read()
완료되기를 기다리는 동안 sort
정지하지 않고 쓸 수 있는 큰(128 + 64KB) 버퍼를 쓰기 작업에 제공하게 됩니다. 복구될 때쯤에는 다음 쓰기로 전달할 cat
데이터가 많아집니다( sort
단일 쓰기로 전송된 것보다 훨씬 더 많음) sort
. 그래서 다음 사람은 sort
멈추지 않고 그 내용을 많이 읽을 수 있습니다.
나도 의심한다다음에 추가가장 가까운 파일 계층은 cat
성능에 거의 또는 전혀 영향을 미치지 않습니다. 이러한 파일은 이미 RAM 디스크에 캐시되어 있습니다. 그러나 호출 사이의 레이어는 sort
버퍼 역할을 하므로 그 수를 줄여야 합니다. 이것은 실제로 "cat의 쓸모없는 사용", 즉 cat을 사용하여 파일을 읽는 상황입니다. 이는 다음과 같은 형태입니다.
cat some_file | some_command
흥미로운 실험
파이프의 버퍼 크기를 늘려서 동일한 효과를 얻을 수 있는지 알고 싶습니다. 올바른 프로그래밍 언어(셸이 아님)를 사용하여 동일한 파이프를 설정하는 경우. 예를 들어 C에서는 , pipe()
, dup2()
를 사용하여 파이프를 생성하고 각 파이프를 fork()
먼저 호출하여 버퍼 크기를 늘릴 수 있습니다(참조:exec()
ioctl()
파이프 용량)
답변2
내 생각엔 cat을 사용하면 각 명령의 처리량이 제한되어 명령이 병렬로 더 빠르게 실행되는 것 같습니다.
cat
데이터 읽기128KB 청크. 귀하의 테스트를 재현할 수 없으므로 귀하의 사용법을 바꾸어 제가 옳고 그른지 증명할 cat
수 있습니까?dd
dd status=none bs=128K
동일한 효과가 있어야 합니다 cat
. 청크 크기를 늘리거나 줄이고 결과를 비교해 보세요.