이 Unix 명령을 최적화하는 방법은 무엇입니까?

이 Unix 명령을 최적화하는 방법은 무엇입니까?

다음 명령은 결과를 출력하는 데 약 10분 정도 걸립니다.

find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
    awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head

성능을 어떻게 향상시킬 수 있나요?

답변1

이것은 이미 상당히 최적화되어 있습니다. 자세한 내용을 알지 못하면 병목 현상이 무엇인지 알기가 어렵습니다.

  • 스토리지 유형(HD, SSD, 네트워크, RAID)
  • 일치하는 파일의 수 및 평균 크기
  • 디렉터리 및 기타 일치하지 않는 파일 수
  • 행당 필드 수
  • 평균 줄 길이

어떤 상황에서도 할 수 있는 일:

  • /가 지원하는 경우 or -print | xargs로 바꾸세요 -exec cmd {} +. 이는 잘못된 것일 뿐만 아니라 어떤 문자가 공백인지 알아내기 위해 문자를 디코딩하고 값비싼 인용을 수행해야 하기 때문에 비용이 더 많이 듭니다.-print0 | xargs -r0findxargs-print | xargsxargs
  • 로케일을 C( export LC_ALL=C)로 수정했습니다. 여기에 관련된 모든 문자( |파일 내용의 십진수, 파일 이름의 라틴 문자, 마침표 및 밑줄 포함)는 이식 가능한 문자 세트의 일부이므로 문자 세트가 UTF-8 또는 기타 멀티바이트 문자인 경우 C 언어를 단일 바이트 문자 집합이 있는 언어로 전환하면 find많은 작업이 절약 됩니다 awk.
  • 부품을 awk다음과 같이 단순화합니다 awk -F "|" '$14 == "20160920100643" && $22 == "567094398953"'.
  • 출력을 로 파이프하고 있으므로 출력 버퍼링을 비활성화 하여 가능한 한 빨리 이러한 10개 라인을 출력할 head수 있습니다 . 또는 를 awk통해 사용할 수 있습니다 . 또는 in 을 추가할 수도 있습니다 .gawkmawkfflush()if (++n == 10) exitawk

요약하다:

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -exec zcat {} + |
  awk -F "|" '$14 == "20160920100643" && $22 == "567094398953" {
    print; if (++n == 10) exit}')

CPU가 병목 현상을 일으키는 경우 멀티 코어 GNU 시스템에서 다음을 시도해 볼 수 있습니다.

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -print0 |
  xargs -r0P 4 -n 100 sh -c '
    zcat "$@" | 
      awk -F "|" "\$14 == "20160920100643" && \$22 == "567094398953" {
        print; fflush()}"' sh | head)

zcat | awk100개 파일 배치에서 4개 작업을 병렬로 실행합니다.

타임스탬프 인 경우 20160920100643그 이전에 마지막으로 수정된 파일을 제외할 수 있습니다. GNU 또는 BSD의 find경우 -newermt '2016-09-20 10:06:42'.

행에 필드 수가 많은 경우 awk행을 분할하고 너무 많은 필드를 할당하면 $n불이익을 받게 됩니다. 처음 22개 필드만 고려하는 접근 방식을 사용하면 속도를 높일 수 있습니다.

grep -E '^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)'

awk명령 보다는 . GNU를 사용하여 병렬 방식에서는 초기에 라인 출력에 옵션을 grep추가하거나 , 비병렬 방식에서는 10번 일치 후에 중지합니다.--line-buffered-m 10

요약하자면, CPU에 병목 현상이 있고 시스템에 CPU 코어가 4개 이상 있고 muc* 파일이 400개 이상 있으며 GNU 시스템(보통 grepGNU보다 훨씬 빠름 awk)을 사용하고 있는 경우:

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -newermt '2016-09-20 10:06:42' -print0 |
  xargs -r0P 4 -n 100 sh -c '
    zcat "$@" | 
      grep --line-buffered -E \
        "^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)"
  ' sh | head)

병렬 접근 방식에서는 서로 혼합된 명령 출력을 얻을 수 있습니다 grep(라인 버퍼링을 사용하고 몇 킬로바이트 미만의 라인을 제공하더라도 라인 경계는 유지되어야 함).

답변2

@Stéphane Chazelas의 답변은 명령 파이프라인을 최적화하는 방법에 대한 많은 세부 정보를 제공합니다.

find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
    awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head

저는 여러분이 가장 많은 시간을 보내는 곳을 실제로 측정할 수 있는 또 다른 접근 방법을 제시하겠습니다. 시간을 어디에 쓰고 있는지 찾으면 그 시간으로 무엇을 할지 결정할 수 있습니다. 실행 시간을 10분 향상시키려면 2초가 걸리는 단계를 최적화하는 것은 거의 쓸모가 없습니다.

명령 파이프라인을 살펴보면 다음 세 가지 사항에 주목했습니다.

  1. find .- 디렉토리 구조는 어떻게 생겼나요? 각 디렉토리에는 몇 개의 파일이 있습니까? 디렉토리가 명령을 실행하는 시스템에 로컬입니까? 원격 파일 시스템은 다음과 같습니다.많은더 느리게.
  2. -name "muc*_*_20160920_*.unl*"- 디렉토리 구조에서 모든 파일 이름은 얼마나 가깝습니까? 그것들은 모두 이름에 "가깝고" 일치하기 어렵고/CPU 집약적입니까? 왜냐하면모든디렉터리 트리의 파일 이름은 디스크에서 읽어와 패턴과 비교되어야 합니다.
  3. xargs zcat- 제 생각에는 이는 특히 위의 문제와 그 자체 xargs에 비해 성능 문제가 그리 크지 않은 것 같습니다 . 파일 이름이 10,000개 또는 심지어 10,000,000개라도 이름을 전달하고 구문 분석하는 데 소요되는 시간은 파일 이름에 비해 거의 무시할 수 있습니다.findzcat발견하다이름을 지정한 다음 모든 파일 자체를 열고 추출하십시오. 파일의 크기는 얼마나 됩니까? 전체 압축을 풀기 때문에모든파일 이름 패턴과 일치하는 파일입니다 find.

주요 성능 문제가 무엇인지 어떻게 판단합니까? 파이프라인에서 각 명령의 성능을 측정합니다. (바라보다https://stackoverflow.com/questions/13294554/how-to-use-gnu-time-with-pipeline전체 파이프라인 타이밍에 대한 세부정보입니다. ) 다음 명령을 실행하여 각 단계가 전체 파이프라인 처리 시간에 기여하는 시간을 확인할 수 있습니다.

/usr/bin/time find .- 디렉토리 트리를 실행하는 데 걸리는 시간을 알려줍니다. 속도가 느리다면 더 나은 스토리지 시스템이 필요합니다. 파일 시스템 캐시 플러시최악의 측정을 위해 타이밍을 맞추기 전에 타이밍을 다시 실행하여 find캐시가 성능에 얼마나 많은 영향을 미치는지 확인하십시오. 디렉토리가 로컬이 아닌 경우 파일이 있는 실제 시스템에서 명령을 실행해 보십시오.

/usr/bin/time find . -name "muc*_*_20160920_*.unl*"- 파일 이름을 패턴 일치시키는 데 걸리는 시간을 알려줍니다. 파일 시스템 캐시를 다시 플러시하고 두 번 실행합니다.

/usr/bin/time bash -c "find . -name 'muc*_*_20160920_*.unl*' | xargs zcat > /dev/null"- 나는 이것이 파이프라인의 긴 런타임의 주요 구성 요소라고 생각합니다. 이것이 문제라면 zcatStéphane Chazelas의 답변에 따라 명령을 병렬화하는 것이 아마도 최선의 대답일 것입니다.

가장 많은 시간을 소비하는 위치를 찾을 때까지 원래 명령 파이프라인의 단계를 테스트 중인 파이프라인에 계속 추가하세요. 다시 한 번 이것이 zcat단계 인지 의심됩니다 . 그렇다면 zcat@Stéphane Chazelas가 게시한 병렬화가 도움이 될 것입니다.

병렬화는 zcat도움이 되지 않을 수도 있습니다.피해성능이 저하되고 처리 속도가 느려집니다. zcat한 번에 하나만 실행되므로 IO는 좋은 스트리밍 모드에 있을 가능성이 높으며 디스크 검색을 최소화합니다 . 여러 zcat프로세스가 동시에 실행되는 경우 디스크 헤드가 탐색해야 하고 미리 읽기 작업의 효율성이 떨어지기 때문에 IO 작업이 경쟁하고 실제로 처리 속도가 느려질 수 있습니다.

zcat단계가 주요 성능 병목 현상이고 zcat한 번에 여러 프로세스를 실행해도 도움이 되지 않거나 실제로 속도가 느려지는 경우 파이프라인은 IO 바인딩되어 있으므로 더 빠른 스토리지를 사용하여 해당 문제를 해결해야 합니다.

다시 말하지만, 디렉터리가 명령 파이프라인이 실행 중인 컴퓨터의 로컬이 아닌 경우 파일 시스템이 실제로 있는 컴퓨터에서 실행해 보세요.

답변3

댓글에서 지적했듯이지그레프이러한 작업을 위한 더 나은 선택글로벌 스타다음과 같이 허용되는 **옵션all path inside the directory except hidden

shopt -s globstar
zgrep -m 10 '^\([^|]*|\)\{13\}20160920100643|\([^|]*|\)\{7\}567094398953' ./**muc*_*_20160920_*.unl*
shopt -u globstar

답변4

지적한 대로 추가 세부정보 없이는 정답을 제공하는 것이 불가능합니다.

locate -0 -b -r '^muc.*_.*_20160920_.*.unl.*gz' | 
   xargs -0  zcat |
   awk -F "|" '$14=="20160920100643" && $22=="567094398953"'| head
  • **1: 찾기(사용 가능한 경우)는 or보다 훨씬 빠릅니다 find. 사용된 정규식을 조정해야 합니다...

  • 2 및 3: PO용 필터

@rudmeier가 현명하게 지적했듯이 locate. (예를 들어, 대부분의 Linux 시스템에서는 찾기가 매일 업데이트되므로 오늘 생성된 파일을 찾을 수 없습니다.)

그러나 찾기가 가능하다면 이는 매우 인상적인 속도 향상을 가져올 것입니다.

time ...구매 주문이 다양한 솔루션을 제공할 수 있다면 흥미로울 것입니다.

관련 정보