“wc -l”보다 빠른 것이 필요합니다


12

1GB와 같은 큰 파일의 wc -l경우 속도가 느려집니다. 특정 파일의 줄 바꿈 수를 계산하는 더 빠른 방법이 있습니까?


25
더 빠른 디스크를 구입 하시겠습니까? 입력의 각 바이트에 대한 검사가 필요하다는 점을 감안할 때 0x0AI / O는 병목 현상이 아닙니다.
thrig

2
wc오버 헤드가 너무 의심되는 경우 직접 구현하려고 할 수 있습니다 foreach byte in file: if byte == '\n': linecount++. C 또는 어셈블러에서 구현하면 우선 순위가 가장 높은 RTOS의 커널 공간을 제외하고는 더 빠를 것이라고 생각하지 않습니다 (또는 인터럽트를 사용하더라도 시스템에서 다른 작업을 수행 할 수는 없습니다). .. 좋아, 난 탈선 ;-))
Murphy

3
그리고 스케일에 대한 느낌을 얻으려면 time wc -l some_movie.avi캐시되지 않은 파일을 빠르게 처리 하여 결과를 얻었습니다 5172672 some_movie.avi -- real 0m57.768s -- user 0m0.255s -- sys 0m0.863s. 기본적으로 @ thrig을 입증하는 I / O는이 경우 성능을 산산조각냅니다.
Murphy

10
디스크 IO 병목 현상을 보여주는 가장 좋은 방법은 time wc -l some_large_file_smaller_than_cache연속으로 두 번 빠르게 수행하고 두 번째 작업이 얼마나 빠른지 time wc -l some_large_file_larger_than_cache확인한 다음 실행간에 시간이 어떻게 변하지 않는지 확인하십시오. 여기 ~ 280MB 파일의 경우 시간은 1.7 초에서 0.2 초이지만 2GB 파일의 경우 두 번 모두 14 초입니다.
EightBitTony

1
너무 느리게 당신을 위해? 무엇을 /usr/bin/time wc -l <file>말을? 하드웨어는 무엇입니까? 명령을 반복해서 실행하면 더 빠릅니까? 우리는 정말로 더 많은 정보가 필요합니다.)
marcelm

답변:


21

C쓰려고 시도 할 수 있습니다 .

#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(){
  char buf[BUFSIZ];
  int nread;
  size_t nfound=0;
  while((nread=read(0, buf, BUFSIZ))>0){
    char const* p;
    for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}
  }
  if(nread<0) { perror("Error"); return 1; }
  printf("%lu\n", nfound);
  return 0;
}

에 저장, 예를 들어 wcl.c컴파일 gcc wcl.c -O2 -o wcl및 실행

<yourFile ./wcl

이것은 약 370ms (반복 실행)로 시스템의 1GB 파일에 줄 바꿈을 발견 합니다. (버퍼 크기를 늘리면 시간이 약간 증가합니다. BUFSIZ가 최적에 가까워 야합니다). 이것은 내가 얻는 ~ 380ms 와 매우 비슷합니다 wc -l.

Mmaping은 약 280ms 의 더 나은 시간을 제공 하지만 물론 실제 파일 (FIFOS 없음, 터미널 입력 없음)로 제한되는 한계가 있습니다.

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
  struct stat sbuf;
  if(fstat(0, &sbuf)<0){ perror("Can't stat stdin"); return 1; }

  char* buf = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, 0/*stdin*/, 0/*offset*/);
  if(buf == MAP_FAILED){ perror("Mmap error"); return 1; } 

  size_t nread = sbuf.st_size, nfound=0;
  char const* p;
  for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}

  printf("%lu\n", nfound);
  return 0;
}

테스트 파일을 다음과 같이 만들었습니다.

 $ dd if=/dev/zero of=file bs=1M count=1042 

다음과 같이 테스트 줄 바꿈을 추가했습니다.

 $ echo >> 1GB 

그리고 16 진 편집기.


나는 mmap 결과 TBH에 놀랐다. 필자는 mmaping이 읽기 / 쓰기보다 빠르다고 생각했지만 그 반대의 Linux 벤치 마크를 보았습니다. 이 경우에는 매우 사실 인 것 같습니다.
PSkocik

4
mmap은 요즘 거대한 페이지에 매핑되기 때문에 Linux에서 훨씬 더 나은 결과를 얻을 수 있으며 TLB 누락은 sloooowwwwwww입니다.
jthill

파일의 다른 부분을 별도의 스레드 (예 : OpenMP for루프) 로 읽는 것이 도움이 될 수 있으므로 하나의 스레드가 입력을 기다리는 동안 중단되는 동안 일부 진행이 이루어질 수 있습니다. 그러나 다른 한편으로는, I / O 스케줄링을 방해 할 수 있으므로, 시도하고 측정하는 것만으로도 추천 할 수 있습니다!
Toby Speight

read()버전은 미리 읽기의 이점 이 있을 수 있습니다.
Barmar

1
@TobySpeight 예, 멀티 스레딩이 속도를 높일 수 있습니다. 또한 2 ^ 16 룩업 테이블을 통해 한 번에 두 바이트를 스캔하는 것은 지난 번에 플레이했을 때 꽤 좋은 속도를 제공했습니다.
PSkocik

18

에 대한 호출 수를 줄임으로써 @pskocik에서 제안한 솔루션을 개선 할 수 있습니다 read. 있습니다 많이 읽을 수있는 통화의 BUFSIZ1GB의 파일에서 청크. 이를 수행하는 일반적인 방법은 버퍼 크기를 늘리는 것입니다.

  • 재미를 위해서 버퍼 크기를 10 배로 늘리십시오. 또는 100. 내 데비안 7에서는 BUFSIZ8192입니다. 원래 프로그램에서는 12 만 건의 읽기 작업이 있습니다. 아마도 1Mb 입력 버퍼를 감당하여 100 배로 줄일 수 있습니다.
  • 보다 최적의 접근 방식을 위해 응용 프로그램은 파일만큼 큰 버퍼를 할당하여 단일 읽기 작업이 필요할 수 있습니다. "작은"파일에는 충분합니다 (일부 독자는 컴퓨터에 1Gb 이상을 가지고 있지만).
  • 마지막으로, 할당을 처리하는 메모리 매핑 I / O를 실험 할 수 있습니다.

다양한 접근 방식을 벤치마킹 할 때 Linux와 같은 일부 시스템은 대부분의 시스템에서 사용하지 않는 메모리를 디스크 캐시로 사용한다는 것을 명심하십시오. 얼마 전 (거의 20 년 전, 사악한 FAQ 에서 언급 ), 텍스트 편집기에서 메모리 부족 조건을 처리하기 위해 개발 한 (아주 좋지 않은) 페이징 알고리즘의 예기치 않은 좋은 결과에 의아해했습니다. 프로그램이 파일을 읽는 데 사용 된 메모리 버퍼에서 작동하기 때문에 빠르게 실행되었다고 설명했으며 파일을 다시 읽거나 쓰는 경우에만 속도 차이가 있다고 설명했습니다.

동일하게 적용됩니다 mmap(또 다른 경우에도 FAQ에 포함시키기 위해 할 일 목록에 있음). 개발자는 디스크 캐시가 실제 개선의 이유 인 시나리오에서 매우 좋은 결과를보고했습니다. 벤치 마크를 개발하려면 성능이 우수한 이유를 분석하는 데 시간과주의가 필요합니다.

더 읽을 거리 :


2
특정 임계 값을 초과하는 버퍼 크기의 영향을 과대 평가하고 있습니다. 일반적으로 버퍼 크기를 4KB 이상으로 늘리면 큰 도움이되지 않으며 실제로 L1 캐시에서 버퍼를 밀어 낼 수 있기 때문에 해로울 수 있습니다. 내 컴퓨터에서 dd1MB 버퍼를 사용한 테스트 는 8KB보다 느립니다 . wc의 8KB 기본값은 실제로 꽤 잘 선택되어 있으며 광범위한 시스템에 최적에 가깝습니다.
marcelm
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.