GNU 병렬 처리의 기본 출력 모드는 다음과 같습니다 --group
.
각 작업의 출력은 임시 파일에 기록되고 parallel
작업이 완료된 후에만 출력으로 전달됩니다.
/tmp
이 기본 출력 모드는 공간 보다 큰 데이터와 함께 사용할 때
parallel lz4 -dc ::: /var/lib/apt/lists/*lz4 | wc
속도가 느리고 충돌이 발생합니다.
parallel: Error: Output is incomplete.
Cannot append to buffer file in /tmp.
이 모드를 사용하면 --ungroup
라인이 중간에서 분할되어
parallel --ungroup lz4 -dc ::: /var/lib/apt/lists/*lz4 | wc
unparallelized와 다른 출력이 생성됩니다 lz4 -dmc /var/lib/apt/lists/*lz4 | wc
.
parallel
매뉴얼 페이지 에 따르면 이 문제는 내가 이해하는 옵션으로 해결되어야 합니다 --line-buffer
. 모든 작업에는 병렬로 읽혀지는 출력 파이프가 있으며, 어떤 작업의 출력도 사용 가능하면 병렬 프로세스의 출력 파이프에 한 줄씩 전달됩니다. 그 자체. (편집: 라인당 하나의 시스템 호출이 아닌 병렬 프로세스에 걸쳐 많은 입력을 분산시키는 것과 같이 블록 단위의 라인을 의미합니다. 이는 너무 느립니다.)
그러나 이것은 작동하지 않습니다. 위에서 암시한
parallel --line-buffer lz4 -dc ::: /var/lib/apt/lists/*lz4 | wc -c
것과 --group
동일한 디스크 가득 참 오류가 발생합니다.
parallel --line-buffer
임시 파일 없이 어떻게 사용하나요?
시스템은 LUbuntu 20 LTS입니다. parallel -V
반품 20161222
. 하이퍼스레딩(4개 스레드)을 사용하는 듀얼 코어 i3-4130의 원시 직렬 및 병렬 압축 해제 성능 비교:
time ls -S /var/lib/apt/lists/*lz4 | parallel --ungroup lz4 -dc > /dev/null
1.461s
time lz4 -dmc /var/lib/apt/lists/*lz4 > /dev/null
3.069s
실제 사용 사례는 다음과 같습니다(솔루션 없음 --line-buffer
).
time lz4 -dmc /var/lib/apt/lists/*Contents* | grep -F $'/parallel\t' | sort -u
usr/bin/parallel universe/utils/moreutils,universe/utils/parallel
usr/bin/parallel universe/utils/parallel
usr/lib/R/library/parallel/R/parallel universe/math/r-base-core
usr/lib/cups/backend/parallel net/cups-filters
usr/share/doc-base/parallel universe/utils/parallel
real 0m5.349s
user 0m3.970s
sys 0m5.839s
time ls -S /var/lib/apt/lists/*Contents* | parallel lz4 -dc '{}' \| grep -F "\$'/parallel\t'" | sort -u
(same output as above)
real 0m3.669s
user 0m5.888s
sys 0m7.676s
이는 압축 해제뿐만 아니라 사후 처리도 병렬화하며 파이프라인의 첫 번째 부분이 99% 작동하지 않기 때문에 더 나은 솔루션입니다.
그러나 전체 파이프라인을 병렬화하는 이 방법은 항상 실현 가능한 것은 아니므로 첫 번째 단계의 출력이 그다지 작지 않고 스트리밍되어야 하는 경우에는 일반적인 문제가 남아 있습니다.
답변1
제안한 대로 수행하려면 lz4
각 파이프의 출력을 별도의 파이프로 보내야 하며 모든 파이프에서 읽고 출력을 여러 줄로 분할하는 선택/폴링 루프가 필요하거나 프로세스/스레드가 매 파이프를 처리하도록 해야 합니다. 파이프.
이것은 엄청난 비용처럼 들립니다. 이러한 오버헤드가 없더라도 빠른 SSD가 장착된 12년 된 4코어 8스레드 노트북 printf '%s\0' /var/lib/apt/lists/*lz4 | xargs -r0 -n 1 -P8 lz4 -dc
(GNU 병렬 오버헤드가 없더라도)에서는 lz4 -dmc /var/lib/apt/lists/*lz4
.
이상적으로는 병렬 명령 출력 라인이 먼저 버퍼링되기를 원합니다. 이 작업을 수행하는 데 사용할 수 있는 방법이 많이 있습니다 stdbuf -oL
.
그렇지 않은 것 같지만 다음과 같이 두 번째 접근 방식(하나의 출력에 대해 하나의 프로세스) 을 lz4
수동으로 구현할 수 있습니다.lz4
printf '%s\0' /var/lib/apt/lists/*lz4 |
stdbuf -oL xargs -r0 -n 1 -P4 sh -c 'lz4 -dc "$1" | paste' sh |
wc -c
( paste
이것은 한 번에 한 줄씩 입력을 처리하는 명령이며 출력을 확실히 라인 버퍼링합니다 . GNU stdbuf
도 참조하세요 . 한 번에 한 줄씩 출력하는 것을 피하고 한 번에 한 바이트씩 입력을 읽습니다.)grep
grep --line-buffered '^'
sed -u
출력이 폐기되더라도 /dev/null
내 시스템에서는 비병렬 시스템보다 13배 느립니다 lz4 -dmc /var/lib/apt/lists/*lz4
(6.5초 대 0.5초).
이는 paste
C로 작성 되었습니다. GNU 병렬 처리는 에서 작성되었으며 perl
내부적으로 이와 같은 기능을 지원한다면 훨씬 덜 효율적일 가능성이 높습니다.
병렬화(적어도 이런 방식)는 lz4
쉬운 압축 해제와 달리 상대적으로 적은 텍스트 출력을 생성하는 CPU 집약적 작업에만 적합합니다.
답변2
버전 20170822의 릴리스 노트는 다음과 같습니다.
- --line-buffer는 더 이상 임시 파일을 사용하지 않습니다. 이는 속도가 더 빠르며 단일 프로세스가 사용 가능한 디스크 공간보다 더 많은 데이터를 출력할 수 있게 해줍니다.
따라서 해결책은 20170822로 업그레이드하는 것입니다.
GNU Parallel은 여전히 다른 목적으로 임시 파일을 사용하지만 라인 버퍼링에는 사용하지 않습니다.
대기열이 긴 경우에도 올바른 작업을 수행합니다.
#!/bin/bash
5gfile() {
# Create file with 5GB long line
perl -e '$a=(shift)x1000000;for(1..5000){print $a};print "\n"' $1 | lz4 > $1.lz4;
}
export -f 5gfile
parallel 5gfile ::: a b c d
echo The correct output: One line with a b c d
lz4 -dc {a..d}.lz4 | tr -s abcd
echo Output from parallel: One line with a b c d might be reordered
parallel --line-buffer lz4 -dc ::: {a..d}.lz4 |
tr -s abcd
echo Output from xargs with stdbuf -oL
echo This does not handle long lines because stdbuf -oL does not guarantee only full lines will be written
printf '%s\0' /tmp/*lz4 |
stdbuf -oL xargs -r0 -n 1 -P4 sh -c 'lz4 -dc "$1" | paste' sh |
tr -s abcd