gzip 압축 파일에서 여러 레코드 (줄)를 가져 오는 가장 빠르고 효율적인 방법


16

7.6GB gzip 파일에서 레코드 수를 계산하려고합니다. zcat명령을 사용하여 접근 방식을 거의 찾지 못했습니다 .

$ zcat T.csv.gz | wc -l
423668947

작동하지만 시간이 너무 오래 걸립니다 (수를 계산하는 데 10 분 이상 소요). 나는 같은 몇 가지 접근법을 더 시도했다.

$ sed -n '$=' T.csv.gz
28173811
$ perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811

이 세 명령 모두 매우 빠르게 실행되지만 28173811의 잘못된 수를 제공합니다.

최소 시간 내에 레코드 수를 어떻게 수행 할 수 있습니까?


5
왜 레코드 수를 세어야합니까? 처리하기 전에 개수를 세려고하면 파일을 두 번 압축 해제해야합니다.
Andrew Henle

3
왜 이런 일을하는지에 대한 자세한 정보가 도움이 될 것입니다. 그것이 진행중인 것이라면, 즉 정기적으로 많은 파일을 압축하고 나중에 레코드 수를 알아야합니다-압축 된 것으로 계산하고 파일 이름에 숫자를 포함시키는 이유는 무엇입니까?
jamesqf

3
기계 디스크에서 9.7GB 파일을 읽는 것은 본질적으로 느립니다. 파일을 SSD에 저장하고 gunzip / zcat의 실행 속도를 확인하십시오. 그러나 @ jamesqf가 말했듯이 줄 수를 파일 이름 또는 tgz의 파일에 저장하면 해당 파일을 추출하는 것이 훨씬 빠릅니다.
ChuckCottrill

2
이 작업을 피할 수없는 좋은 이론적 이유가 있습니다. "압축 해제하지 않고"데이터의 유용한 속성을 결정할 수있는 압축 형식은 압축 형식이 좋지 않은 정의에 의해 정의 된 것입니다. :)
hobbs

답변:


28

sed, perl그리고 awk당신이 언급 명령이 올바르지 수 있지만, 그들은 모두 읽기 압축 점에서 데이터와 카운트 개행 문자를. 이러한 개행 문자는 압축되지 않은 데이터의 개행 문자와 관련이 없습니다.

압축되지 않은 데이터의 줄 수를 계산하기 위해 압축 해제하는 방법은 없습니다. 귀하의 접근 방식 zcat은 올바른 접근 방식이며 데이터가 너무 크기 때문에 압축 해제하는 데 시간 걸립니다.

gzip압축 및 압축 해제 를 처리하는 대부분의 유틸리티 는 동일한 공유 라이브러리 루틴을 사용합니다. 속도를 높이는 유일한 방법 zlib은 기본 루틴보다 빠른 루틴 의 구현을 찾고 zcat이를 사용하도록 재구성 하는 것입니다.


11
사소한 프로그래밍 연습이지만 가능합니다. 요점은 재건 하지 않는 것zcat 입니다. 작업의 중요한 부분은 zcat실제 출력을 생성하는 것입니다. 그러나 \n문자 만 세는 경우 에는 필요하지 않습니다. gzip압축은 본질적으로 일반적인 긴 문자열을 더 짧은 문자열로 대체하여 작동합니다. 따라서을 포함하는 사전의 긴 문자열 만 신경 \n쓰고 (가중치) 발생 횟수를 세어야합니다. 예를 들어 영어 규칙으로 인해 .\n일반적인 16 비트 문자열이 있습니다.
MSalters

19

unpigz를 사용하십시오.

Kusalananda의 대답이 올바른지, 당신은 전체 파일의 내용을 검색 할 수 있음을 압축 해제해야합니다. /bin/gunzip단일 코어에서 최대한 빨리 수행합니다. Pigzgzip여러 코어를 사용할 수 있는 병렬 구현입니다 .

슬프게도, 보통은 gzip 파일의 압축 해제 자체가 병렬하지만 할 수없는 pigz제안을의 개선 된 버전을 수행 gunzip, unpigz등, 읽기, 쓰기 및 별도의 스레드에서 체크섬 등 관련 작업을 수행하는. 일부 빠른 벤치 마크에서는 코어 i5 시스템 unpigz보다 거의 두 배 빠릅니다 gunzip.

pigz선호하는 패키지 관리자로 설치 하고 unpigz대신 gunzip또는 unpigz -c대신에 사용하십시오 zcat. 따라서 귀하의 명령은 다음과 같습니다.

$ unpigz -c T.csv.gz | wc -l

이 모든 것은 병목이 디스크가 아니라 CPU라고 가정합니다.


4
필자의 pigz맨 페이지에는 감압을 병렬화 할 수 없으며 적어도 그 목적을 위해 특별히 준비된 수축 스트림이 없으면 압축을 병렬화 할 수 없다고 나와 있습니다. 결과적으로, pigz는 압축 해제를 위해 단일 스레드 (주 스레드)를 사용하지만, 읽기, 쓰기 및 점검 계산을 위해 다른 스레드 3 개를 작성하여 일부 상황에서 압축 해제 속도를 높일 수 있습니다 . 그래도, 당신처럼 나는 그것이 gzip병렬성 때문에 아니더라도 적어도 두 배 더 빠르다는 것을 알았다
Stéphane Chazelas

@ StéphaneChazelas 좋은 지적입니다! 압축 해제 속도가 약간 실망 스럽습니다. 이 정보를 더 잘 반영하도록 게시물을 수정했습니다.
marcelm

5

모든 파이프 라인의 문제점은 본질적으로 작업을 두 배로 늘리는 것입니다. 압축 해제 속도에 관계없이 데이터는 여전히 다른 프로세스로 셔틀되어야합니다.

Perl에는 PerlIO :: gzip 이있어 gzipped 스트림을 직접 읽을 수 있습니다. 따라서 압축 해제 속도가 다음 속도와 일치하지 않더라도 이점을 제공 할 수 있습니다 unpigz.

#!/usr/bin/env perl

use strict;
use warnings;

use autouse Carp => 'croak';
use PerlIO::gzip;

@ARGV or croak "Need filename\n";

open my $in, '<:gzip', $ARGV[0]
    or croak "Failed to open '$ARGV[0]': $!";

1 while <$in>;

print "$.\n";

close $in or croak "Failed to close '$ARGV[0]': $!";

16GB RAM이 있는 오래된 2010 MacBook Pro와 이미 캐시에 파일이있는 8GB RAM 이 있는 오래된 ThinkPad T400에서 13MB gzip 압축 파일 (1.4GB로 압축 해제)로 시도했습니다 . Mac에서 Perl 스크립트는 파이프 라인 (5 초 대 22 초)을 사용하는 것보다 훨씬 빠르지 만 ArchLinux에서는 unpigz를 잃었습니다.

$ 시간 -p ./gzlc.pl spy.gz 
1154737
진짜 4.49
사용자 4.47
시스 0.01

$ time -p unpigz -c spy.gz | 화장실 -l
1154737
진짜 3.68
사용자 4.10
시스템 1.46

$ time -p zcat spy.gz | 화장실 -l
1154737
진짜 6.41
사용자 6.08
시스템 0.86

분명히, unpigz -c file.gz | wc -l속도 측면에서 사용하는 것이 승자입니다. 그리고이 간단한 명령 줄은 프로그램을 작성하는 것보다 짧습니다.


1
감압 계산과 비교하여 두 프로세스간에 데이터를 이동하는 데 필요한 리소스를 과대 평가하고 있다고 생각합니다. 다양한 접근법을 벤치마킹 해보십시오;)
marcelm

2
@ SinanÜnür 내 x86_64 Linux 시스템 (또는 오래된 하드웨어)에서 gzip | wcperl 스크립트와 동일한 속도를 갖습니다. 그리고 pigz | wc두 배 빠릅니다. gzip/ dev / null에 출력을 쓰거나 wcperl에 의해 사용되는 "gzip 라이브러리"가 gzip 명령 줄 도구보다 빠르다는 것에 관계없이 동일한 속도로 실행됩니다 . 파이프에 다른 Mac / Darwin 관련 문제가있을 수 있습니다. 이 펄 버전이 전혀 경쟁적이라는 것은 여전히 ​​놀라운 일입니다.
rudimeier

1
내 x86_64 Linux 설치에서보다 나아 zcat지고 나쁜 것 같습니다 unpigz. Mac과 비교하여 Linux 시스템에서 파이프 라인이 얼마나 빠른지에 놀랐습니다. 나는 같은 프로그램이 베어 메탈보다 동일한 Mac의 CPU 제한 Linux VM에서 더 빨리 실행되는 것을 관찰 했음에도 불구하고 기대하지 않았습니다.
Sinan Ünür

1
그 흥미 롭군요; 내 시스템 (Debian 8.8 amd64, quad core i5)에서 perl 스크립트는 약간 느립니다 ... 109M .gz 파일은 1.1G의 텍스트로 압축을 풀고 꾸준히 5.4 초가 걸리고 zcat | wc -lperl 스크립트는 5.5 초가 걸립니다 . 솔직히 말해서, 사람들이 여기에보고 한 변형, 특히 Linux와 MacOS X 간의 차이에 놀랐습니다!
marcelm

Mac에서보고있는 내용을 일반화 할 수 있는지 모르겠습니다. 이상한 일이 일어나고 있습니다. 1.4GB 파일의 압축을 풀면 wc -l2.5 초가 걸립니다. gzcat compressed.gz > /dev/null2.7 초가 걸립니다. 그러나 파이프 라인은 22 초가 걸립니다. GNU wc를 사용하면 압축 해제 된 파일에서 0.5 초 밖에 걸리지 않지만 파이프 라인에서는 22 초가 걸립니다. GNU zcat는 실행하는 데 두 배가 걸립니다 zcat compressed.gz > /dev/null. Mavericks, 구형 Core 2 Duo CPU, 16GB RAM, Crucial MX100 SSD에 있습니다.
Sinan Ünür

4

Kusalananda의 대답은 대부분 맞습니다. 줄을 세려면 줄 바꾸기를 검색해야합니다. 그러나 파일을 완전히 압축 해제하지 않고도 개행을 검색하는 것이 이론적으로 가능합니다.

gzip은 DEFLATE 압축을 사용합니다. DEFLATE는 LZ77과 Huffman 인코딩의 조합입니다. 줄 바꿈에 대한 허프만 심볼 노드 만 파악하고 나머지는 무시하는 방법이있을 수 있습니다. L277을 사용하여 인코딩 된 줄 바꿈을 찾고 바이트 수를 유지하고 다른 모든 것을 무시하는 방법은 거의 확실합니다.

따라서 IMHO는 이론적으로 unpigz 또는 zgrep보다 효율적인 솔루션을 제시 할 수 있습니다. 그것은 누군가가 이미 그것을하지 않았다면 확실히 실용적이지 않다는 말입니다.


7
이 아이디어의 주요 문제점은 DEFLATE가 사용하는 Huffman 심볼이 LZ77 압축 후 비트 시퀀스에 해당 하므로 압축되지 않은 파일에서 U + 000A 문자와 간단한 관계가 없을 수 있다는 것입니다. 예를 들어, 허프만 기호 하나는 "."의 마지막 5 비트를 의미합니다. "\ n"의 처음 3 비트가 뒤에오고 다른 기호는 "\ n"의 마지막 5 비트와 "T"의 8 비트가 모두 뒤에 있음을 의미합니다.
zwol

@zwol 아니오, Deflate 알고리즘의 LZ77 부분은 비트 시퀀스가 ​​아닌 바이트 시퀀스를 압축합니다. en.wikipedia.org/wiki/DEFLATE#Duplicate_string_elimination
Ross Ridge

1
@RossRidge Huh, 나는 그것을 몰랐지만, 그것이 내가 말한 것을 무효화한다고 생각하지 않습니다. 허프만 수, 그것은 그 기준의 다음 단락에 따라 나에게 표시 기호, 각 비트의 가변 수를 확장, 그들은 바이트의 전체 수를 생성 할 필요가 없습니다.
zwol

1
@zwol 물론 비트 스트림에서 일치하는 Huffman 코드 비트 시퀀스를 검색해야하지만이 대답은 그렇지 않습니다. 이 답변의 문제점은 어떤 허프만 코드가 궁극적으로 개행 문자를 생성하거나 더 많은지를 결정하는 것이 간단하지 않다는 것입니다. 개행을 생성하는 LZ77 코드는 슬라이딩 윈도우가 이동함에 따라 지속적으로 변경되므로 허프만 코드도 변경됩니다. 줄 바꿈에만 관심이 있기 때문에 출력 부분을 제외하고 전체 압축 해제 알고리즘과 슬라이딩 창의 일부를 구현해야합니다.
로스 릿지

1

사용하여 수행 할 수 있습니다 zgrep-c플래그 및 $매개 변수입니다.

이 경우 -c 명령은 일치하는 줄 수를 출력하도록 지시하고 regex $는 줄 끝과 일치하므로 모든 줄이나 파일과 일치합니다.

zgrep -c $ T.csv.gz 

@ StéphaneChazelas의 의견에 따르면 - zgrep스크립트 일뿐 zcat이며 grep원래 제안과 비슷한 성능을 제공해야합니다.zcat | wc -l


2
안녕하세요 Yaron은 zgrep조차도 zcat만큼 많은 시간이 걸리고 있다고 생각합니다. 다른 접근법을 찾아야합니다.
Rahul

8
zgrep는 일반적으로 데이터를 압축 해제하고에 피드하기 위해 zcat(와 동일 gzip -dcq) 호출하는 스크립트 grep이므로 도움이되지 않습니다.
Stéphane Chazelas

1
@ StéphaneChazelas-의견을 보내 주셔서 감사합니다. 내 답변을 업데이트하여 반영하십시오.
Yaron

0

보시다시피 대부분의 답변은 컨텍스트 스위치 수와 프로세스 간 IO의 수를 최적화하려고 시도합니다. 그 이유는 여기에서 쉽게 최적화 할 수있는 유일한 것입니다.

이제 문제는 리소스 요구가 압축 해제의 리소스 요구와 거의 무시할 수 있다는 것입니다. 그렇기 때문에 최적화로 인해 더 빠른 결과를 얻지 못할 수 있습니다.

실제로 가속화 될 수있는 경우 압축 해제 된 데이터 스트림의 실제 생성을 생략하는 수정 된 압축 해제 (예 : 압축 해제) 알고리즘이됩니다. 오히려 만의 개행의 수가 산출 압축 해제 된 스트림 로부터 압축 된 하나. 어려울 것이므로 gzip 알고리즘 ( LZWHuffman 압축 알고리즘 의 일부 조합)에 대한 깊은 지식이 필요합니다 . 알고리즘으로 인해 라이트닝으로 압축 해제 시간을 크게 최적화 할 수 없으며 개행 수만 알면됩니다. 가능할지라도 본질적으로 새로운 gzip 압축 해제 라이브러리가 개발되어 있어야합니다 (알기 전까지는 존재하지 않음).

귀하의 질문에 대한 현실적인 대답은 아닙니다.

어쩌면 병렬화 된 gzip 압축 풀기가있을 수도 있습니다. 압축 해제를 위해 여러 개의 CPU 코어를 사용할 수 있습니다. 존재하지 않으면 비교적 쉽게 개발할 수 있습니다.

들어 XZ 병렬 압축기 (pxz)에 존재한다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.