앗

저는 sedand와 관련된 몇 가지 성능 문제를 이해하려고 노력하고 awk있으며 다음 실험을 수행했습니다.

$ seq 100000 > test
$ yes 'NR==100001{print}' | head -n 5000 > test.awk
$ yes '100001{p;b}' | head -n 5000 > test.sed
$ time sed -nf test.sed test
real    0m3.436s
user    0m3.428s
sys     0m0.004s
$ time awk -F@ -f test.awk test
real    0m11.615s
user    0m11.582s
sys     0m0.007s
$ sed --version
sed (GNU sed) 4.5
$ awk --version
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 3.1.6-p2, GNU MP 6.1.2)

test.sed여기서 테스트 파일에는 100,000줄만 포함되어 있으므로 및의 모든 명령은 test.awk작동하지 않습니다. 두 프로그램 모두 단순히 행 번호를 주소 (in sed) 또는 NR(in awk)에 일치시켜 명령을 실행할 필요가 없다고 결정하지만 여전히 시간 비용에는 큰 차이가 있습니다. 왜 그럴까요? 다른 버전을 설치 sed하고 awk이 테스트에서 다른 결과를 얻은 사람이 있습니까?

편집하다:결과 mawk(@mosvy가 제안한 대로) original-awk(@GregA.Woods가 제안한 데비안 기반 시스템의 "one true awk" 이름) perl는 다음과 같습니다.

$ time mawk -F@ -f test.awk test
real    0m5.934s
user    0m5.919s
sys     0m0.004s
$ time original-awk -F@ -f test.awk test
real    0m8.132s
user    0m8.128s
sys     0m0.004s
$ yes 'print if $.==100001;' | head -n 5000 > test.pl
$ time perl -n test.pl test
real    0m33.245s
user    0m33.110s
sys     0m0.019s
$ mawk -W version
mawk 1.3.4 20171017
$ perl --version
This is perl 5, version 28, subversion 1 (v5.28.1) built for x86_64-linux-thread-multi

및 의 경우에는 -F@을 대체해도 -F ''눈에 띄는 변화가 없습니다. 비어 있는 것은 지원되지 않습니다.gawkmawkoriginal-awkFS

편집 2 @mosvy의 테스트에서는 21초 sed와 11초라 는 다른 결과가 나왔습니다 mawk. 자세한 내용은 아래 설명을 참조하세요.

답변1

awk보다 광범위한 기능 세트 sed와 보다 유연한 구문을 가지고 있습니다. 그러니 스크립트를 파싱하고 실행하는데 시간이 더 오래 걸리는 것은 무리가 아닙니다.

예제 명령(중괄호 안의 부분)은 실행되지 않으므로 시간에 민감한 부분은 테스트 표현식이어야 합니다.

awk먼저 예제의 테스트를 살펴보세요 .

NR==100001

gprof그리고 (GNU awk 4.0.1)의 효과를 확인하세요:

  누적 자체 합계 %
 시간 초 초 통화 s/통화 s/통화 이름
 55.89 19.73 19.73 1 19.73 35.04 설명
  8.90 22.87 3.14 500000000 0.00 0.00 cmp_스칼라
  8.64 25.92 3.05 1000305023 0.00 0.00 free_wstr
  8.61 28.96 3.04 500105014 0.00 0.00 mk_number
  6.09 31.11 2.15 500000001 0.00 0.00 cmp_nodes
  4.18 32.59 1.48 500200013 0.00 0.00 참조되지 않음
  3.68 33.89 1.30 500000000 0.00 0.00 평가조건
  2.21 34.67 0.78 500000000 0.00 0.00 업데이트_NR

구문 분석 스크립트에서 생성된 opcode를 실행하는 최상위 루프인 "해석"에 약 50%의 시간이 소요됩니다.

테스트를 실행할 때마다(예: 5000 스크립트 줄 * 100000 입력 줄) 다음을 수행 awk해야 합니다.

  • 내장 변수 "NR"( update_NR)을 가져옵니다.
  • 문자열 "100001"( mk_number)을 변환합니다.
  • ( cmp_nodes, cmp_scalar, eval_condition)를 비교해보세요.
  • 비교에 필요한 임시 객체를 모두 삭제합니다( free_wstr, unref).

다른 awk구현에서는 호출 흐름이 완전히 동일하지는 않지만 여전히 변수를 검색하고 자동으로 변환한 다음 비교해야 합니다.

sed

대조적으로, 에서는 sed"테스트"가 훨씬 더 제한됩니다. 단일 주소, 주소 범위 또는 아무것도 될 수 없으며(명령이 행의 첫 번째 항목인 경우) sed첫 번째 주소부터 시작할 수 있습니다 .특징주소든 명령이든 말이죠. 예에서는

100001

...단일 숫자 주소. 구성 파일(GNU sed 4.2.2)은 다음과 같습니다.

  누적 자체 합계 %
 시간 초 초 통화 s/통화 s/통화 이름
 52.01 2.98 2.98 100000 0.00 0.00 프로그램 실행
 44.16 5.51 2.53 1000000000 0.00 0.00 일치하는 address_p
  3.84 5.73 0.22 일치하는 주소
[...]
  0.00 5.73 0.00 5000 0.00 0.00 정수

다시 말하지만, 최상위 수준에서 50% 정도의 시간이 소요됩니다 execute_program. 이 경우 각 입력 라인에 대해 한 번씩 호출된 다음 구문 분석된 명령을 반복합니다. 루프는 주소 확인으로 시작하지만 예제에서는 이것이 전부가 아닙니다(아래 참조).

입력 스크립트의 줄 번호는 컴파일 타임에 구문 분석됩니다( in_integer). 이는 입력의 각 주소 번호에 대해 한 번만 수행하면 됩니다. 5000번으로 전체 실행 시간에 크게 기여하지 않습니다.

즉, 주소 확인은 match_address_p이미 사용 가능한 정수(구조 및 포인터를 통해)만 비교한다는 의미입니다.

추가 sed개선

구성 파일은 match_address_p2*5000*100000번 호출되는 것으로 표시됩니다.두 배각 스크립트 줄 * 입력 줄. 이는 GNU가 배후에서 sed"블록 시작" 명령을 처리하기 때문입니다.

100001{...}

블록 끝까지의 음수 분기로

100001!b end; ... :end

주소가 일치합니다성공각 입력 라인에서 블록 끝( })으로 분기를 발생시킵니다. 블록 끝에 연관된 주소가 없으므로 이는 또 다른 성공적인 일치입니다. 그것이 왜 그렇게 오래 걸리는지 설명해줍니다 execute_program.

따라서 sed사용하지 않는 내용을 생략 ;b하고 결과적으로 불필요한 내용이 발생 하는 경우 {...}.100001p

  누적 자체 합계 %           
 시간 초 초 통화 s/통화 s/통화 이름    
 71.43 1.40 1.40 500000000 0.00 0.00 일치하는 주소
 24.49 1.88 0.48 100000 0.00 0.00 프로그램 실행
  4.08 1.96 0.08 일치하는 주소

이렇게 하면 호출 횟수가 절반으로 줄어들고 match_address_p소요 시간도 크게 줄어듭니다 execute_program(주소 일치가 절대 성공하지 못하기 때문).

답변2

실제로 위 스크립트는 awk에 적합하지 않습니다.

필드의 내용을 사용하지 않더라도 상황에 따라GAWK 매뉴얼각 레코드를 읽을 때마다 다음 단계가 필연적으로 수행됩니다.

  • 모든 FS 발생을 검사합니다.
  • 필드 분할
  • NF 변수 업데이트

이 정보를 사용하지 않으면 폐기됩니다.

레코드에 필드 구분 기호가 나타나지 않으면 awk는 여전히 텍스트를 $0(귀하의 경우 $1)에 할당하고 NF를 가져온 필드의 실제 수(위 예에서는 1)로 설정해야 합니다.

관련 정보