필수: AWK의 비연관 배열

필수: AWK의 비연관 배열

배열 표현 대신 내부 문자열 표현을 직렬화하여 AWK에서 일부 코드의 속도를 높일 생각입니다. (아래 예제 1과 2에서는 datasep이 단일 문자이고 계산 가능한 데이터의 일부가 아니라고 가정합니다.) 따라서 대신:

# Example 1
split(datastr,data,datasep)
for(i=1; i in data; i++) { 
     # Use data[i]
     }

나는 다음과 같은 것을 시도하고 싶다

 # Example 2 - buggy code assumes datastr terminated by datasep
 l= length(datastr)
 for(j=1; j < l ; ) {
      datumlen = match(substr(datastr,j+1),datasep)
      #Use substr(datastr,j+1,datumlen-1)
      j+=datumlen
      }

연관 배열(데이터)을 사용하는 데 따른 메모리와 조회 시간을 절약하고 싶고, 또한 match 및 substr이 구현되는 방식에 자신이 있기 때문입니다. 나는 길이가 10^6바이트보다 큰 데이터 문자열(대부분의 경우 datumlen < 5)로 시작하여 거기서부터 작업할 계획입니다. 결과를 스트리밍할 수 있으므로 코드의 메모리 요구 사항은 걱정하지 않지만 datastr에 여러 번 전달해야 할 수 있으므로 (더 빠르지 않는 한) datastr 스트리밍을 피하고 싶습니다.

따라서 질문은 다음과 같습니다. 예제 1을 개선하고 예제 2와 비슷하게 보일 수 있는 메모리 및 액세스 효율적인 루틴이 있습니까? 아니면 AWK와 시스템이 입력 파일 처리를 위해 내부적으로 버퍼링하고 있다고 믿고 동일한 입력 파일에 여러 번 패스하는 것이 더 나을까요?

EDIT 2015.09.18: (아직 이 포럼에 등록하지 않았으므로 여기에 의견에 답변하십시오.) 저는 Unix가 아닌 플랫폼에서 gawk 4.1.3을 사용하고 있습니다. 나는 특정 유형의 계산을 수행할 수 있는 작은 휴대용 환경에 관심이 있습니다. 나는 gawk의 내부에 대해 충분히 알지 못하며 이 포럼을 읽는 누군가가 이전에 비슷한 것을 시도했을 수도 있다고 생각했습니다. 다른 제안을 받지 못하면 결국 다른 방식으로 분석하게 됩니다. 편집 종료 2015.09.18

게르하르트 "시스템 튜닝에 대해 물어보세요" Paseman, 2015.09.16

답변1

다음은 질문에 답하기 위해 작성한 gawk 4.1.3용 테스트 코드입니다. PFILE의 원시 데이터는 숫자이며, DFILE에 연속된 항목 간의 차이를 저장하여 데이터를 압축하려고 합니다.

BEGIN{ RLS=bufstr=""; SEP =":" ; PFILE="somenumbers.txt" ; DFILE= "diffile.txt"
if (ATEST=="") ATEST=1
accumulate=lastdatum=0 ; BIGN=5500000 ; DATALENMAX=7 ;TUNELEN=2048
for(i=1; i < BIGN ; i++) {
     getline nextdatum < PFILE
     d = nextdatum -lastdatum
#     RLS = RLS d SEP
     ibuf( d SEP )
     print d > DFILE
     lastdatum=nextdatum  }
# RLS = RLS "0"
ibuf("0")
if (length(bufstr) > 0) { RLS = RLS bufstr ; bufstr="" }
print (RLSlen=length(RLS))
close(PFILE) ; close(DFILE)
timestmp["start"] = systime()
if (ATEST==1){
  split(RLS,data,SEP)
  timestmp["endsplit"] = systime()
  for(i=1; i in data; i++){     accumulate += 1*data[i]     }
  }
if (ATEST==2){
  for(j=1; j<RLSlen ; j+=datalen) {
     datalen=match(substr(RLS,j, DATALENMAX),SEP)
     accumulate  += 1*substr(RLS,j,datalen-1)     }
  }
if (ATEST==3) {
  while((getline diff < DFILE)>0){  accumulate  += 1*diff }
  close(DFILE)
  }
print accumulate 
timestmp["end"] = systime()
for(t in timestmp) print t, (1*timestmp[t] - 1*timestmp["start"])
}

function ibuf(str) {   bufstr=bufstr str
   if (length(bufstr) > TUNELEN) { RLS = RLS bufstr ; bufstr="" }
}

ibuf() 함수와 TUNELEN 매개변수는 별로 중요하지 않습니다. 할당으로 인해 할당된 메모리 값이 튀는 것을 보는 것이 지쳤을 뿐입니다.

RLS = RLS d SEP

그래서 이 부분을 완충하기로 결정했습니다.

두 번째와 세 번째 부분(ATEST=2 및 3)이 첫 번째 부분보다 조금 더 빠르게 실행될 것으로 예상됩니다. 하지만 그런 일은 일어나지 않았습니다. 배열을 사용하는 것은 항상 조금 더 빠른 것 같습니다. 극단적으로 섹션 2보다 약 두 배 빠르고 섹션 3보다 조금 더 빠릅니다. 그러나 배열 버전은 값뿐만 아니라 인덱스도 저장해야 하기 때문에 약 10배(또는 그 이상) 더 많은 메모리를 사용합니다.

처음에는 DATAMAXLEN 값 없이 파트 2를 테스트했는데 반복되는 substr() 호출로 인해 속도가 매우 느려졌습니다. 섹션 2 방법은 입력 데이터에 사용되는 메모리를 절약하기는 하지만 더 빠른 속도를 제공하지는 않습니다.

요약하자면, 소비할 메모리가 있으면 연관 배열을 사용하세요. 디스크가 양호하면 파일에서 읽으십시오. 저장해야 한다면 밧줄 위로 올라가되 조심하고 작은 조각만 보세요. 내 시스템에는 메모리 제약이 있어서 응용 프로그램 파일에서 데이터를 읽을 수 있습니다. 누군가가 인덱스를 사용하거나 문자열에 액세스하기 위해 메모리를 절약하는 다른 방법과 같이 파트 2를 수정하는 방법을 본다면 이에 대해 알고 싶습니다.

게르하르트 "마일리지가 자주 바뀌어요" 패스만, 2015.09.30

관련 정보