Perl 명령 성능 문제

Perl 명령 성능 문제

다음 명령을 사용하여 숫자 열(.CSV 파일)의 특수 문자를 제거하고 있는데 제대로 작동하고 있지만 여기서 문제는 성능입니다. 내 CSV 파일 번호 열 데이터에는 다음이 포함됩니다. 데이터에서 1000개의 구분 기호 쉼표를 제거하기 위해 다음 Perl 명령을 사용했습니다.

payment        
"4,326.34"        
590.20          
"12,499.40" 

참고: 내 파일 구분 기호는 "," 쉼표입니다.

input file :               
    Organization,Amount,Revenue,Balance,Desc
    Congos,"4,233.78","3,233.78","1,233.78",Payment
    Toyoto,590.2,390.2,190.2,Payment
    lenives,"5,234.89","2,234.89","1,234.89",Payment
            
            
Excepted OutPut:                    
    Organization,Amount,Revenue,Balance,Desc
    Congos,4233.78,3233.78,1233.78,Payment
    Toyoto,590.2,390.2,190.2,Payment
    lenives,5234.89,2234.89,1234.89,Payment

주문하다: cat | perl -p -e 's/,(?=[\d,.]\d")//g and s/"(\d[\d,.])"/\1/g' 'test.csv' >> newfile.csv

파일 데이터 수:1,100만 개의 데이터

질문:데이터에서 1,000개의 개별 "쉼표"를 제거하는 데 거의 10분이 걸렸습니다.

성능을 향상시킬 수 있는 더 나은 솔루션이 있습니까?

답변1

샘플 입력을 파일에 저장한 다음 데이터 행을 수백만 번 복사하여 약 1,100만 개의 레코드를 포함했습니다.

$ wc -l input2.csv 
11100445 input2.csv

$ ls -l input2.csv 
-rw-r--r-- 1 cas cas 505070243 Apr 10 22:32 input2.csv

빠르고 더러운 한 줄짜리:

$ time perl -pe 's/"([0-9-.]+),?([0-9-.]+)"/$1$2/g' input2.csv >output.csv

real 0m37.922s  user 0m36.371s  sys 0m1.347s

"이렇게 하면 숫자 필드에서 쉼표와 따옴표( ) 문자가 제거됩니다. 안타깝게도 이 기능은 쉼표가 하나만 있는 숫자 필드(예: 아래 숫자)에서만 작동합니다 1,000,000. 숫자 필드의 여러 쉼표와 일치하는 정규식을 찾는 것이 가치가 있다고 생각하지 않고 대신 csv 구문 분석 라이브러리를 사용합니다.

Text::CSV 모듈을 사용하는 것은 느리지만 더 "정확"합니다.

이 버전은 각 필드를 검사하여 숫자인지 확인하고 그렇다면 모든 쉼표를 제거하는 CSV 구문 분석 Perl 모듈을 사용합니다. 이는 모든 값의 숫자 필드에 적용됩니다.

숫자 필드 주위의 따옴표 문자를 제거할 필요가 없습니다. Text::CSV는 필드 따옴표를 자동으로 처리합니다.

#!/usr/bin/perl

use Text::CSV;

my $filename = shift;

my $csv = Text::CSV->new;
open my $fh, $filename or die "$filename: $!";
while (my $row = $csv->getline ($fh)) {
    my @row = map { tr/,//d if ( m/^[0-9,.-]+$/); $_ } @{ $row };
    $csv->print(*STDOUT, \@row);
    print "\n";
}
close($fh);

예를 들어 다른 이름으로 저장 strip.pl하고 실행 가능하게 만듭니다 chmod +x strip.pl.

$ time ./strip.pl input2.csv > output2.csv

real 1m32.379s  user 1m30.422s  sys 0m1.609s

이것은 아마도 많이 최적화될 수 있을 것입니다. 더 빠르게 만들기 위해 어떤 노력도 하지 않았습니다. 1,100만 개의 레코드를 처리하고 각 레코드의 5개 필드를 확인/수정하는 데 1.5분도 나쁘지 않은 것 같습니다.

$ ls -l input2.csv output.csv output2.csv 
-rw-r--r-- 1 cas cas 505070243 Apr 10 22:32 input2.csv
-rw-r--r-- 1 cas cas 430142246 Apr 10 22:40 output2.csv
-rw-r--r-- 1 cas cas 430142246 Apr 10 22:37 output.csv

$ cmp output.csv output2.csv 
$ echo $?
0

두 출력 파일 모두 동일하므로 CSV 파일에 ​​>= 100만 값의 숫자 필드가 없는 것 같습니다. 빠르고 더러운 버전이 작동합니다.

성능 문제

귀하의 Perl 코드 한 줄은 내 시스템(threadripper 1950x, 64GB RAM, zfs 미러 쌍의 2개의 NVME SSD*)에서 약 15초 동안 실행되지만 아무 작업도 수행하지 않습니다. 출력은 입력과 동일합니다.

귀하의 성능 문제는 다음 중 하나 또는 조합으로 인해 발생하는 것 같습니다.

  • 느린 CPU
  • 하드 드라이브가 느립니다.
  • 가장 중요한 것은 메모리 부족입니다.

즉, 적어도 시스템을 크게 업그레이드하기 전까지는 알고리즘을 최적화하여 성능을 향상시킬 수 없을 것입니다. 작동하지 않는다는 사실을 무시한다면 코드 한 줄이 내 코드보다 거의 3배 빠르고 Text::CSV 버전보다 약 7배 빠릅니다.

한 가지는가능한성능을 크게 향상시키는 방법(느린 디스크 I/O가 문제의 주요 원인인 경우)은 CSV 파일을 압축한 다음 gzip사용 zcat가능한 단일 코드 줄로 변환하는 것입니다. 10MB 이하로 압축될 수 있습니다 -읽다10MB 미만의 디스크에 있는 데이터는 디스크보다 약 50배 빠릅니다.읽다500MB 이상. xz그리고 xzcat비슷한 압축 프로그램도 더 좋진 않더라도 마찬가지로 잘 작동할 것입니다.

이는 전체 프로세스가 50배 더 ​​빠르게 실행된다는 의미가 아니라 파일을 훨씬 더 빠르게 읽을 수 있다는 의미입니다. Perl은 여전히 ​​각 라인을 개별적으로 처리해야 합니다.


* 그런데 NVME는 빠르지만 zfs는 약간 느립니다(ext4 또는 xfs와 같은 단순한 파일 시스템에 비해). OTOH lz4 압축을 활성화하면 파일 읽기 속도가 다시 빨라집니다.

답변2

사용csvkit:

$ in2csv file.csv
Organization,Amount,Revenue,Balance,Desc
Congos,4233.78,3233.78,1233.78,Payment
Toyoto,590.2,390.2,190.2,Payment
lenives,5234.89,2234.89,1234.89,Payment

in2csv유틸리티는 다양한 표 형식의 데이터 형식을 CSV로 변환합니다. 완전히 유효한 CSV 파일을 변환하면 in2csv해당 로캘의 숫자가 en_US기본적으로 재해석되어 해당 숫자가 삭제됩니다 ,.

이러한 csvkit도구는 Python으로 구현된 CSV 파서를 기반으로 구축되었습니다.

관련 정보