저는 sed
and와 관련된 몇 가지 성능 문제를 이해하려고 노력하고 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 ''
눈에 띄는 변화가 없습니다. 비어 있는 것은 지원되지 않습니다.gawk
mawk
original-awk
FS
편집 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_p
2*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)로 설정해야 합니다.