왜 "에코"가 "터치"보다 훨씬 빠릅니까?


116

디렉토리의 모든 xml 파일에서 현재 시간으로 타임 스탬프를 업데이트하려고합니다 (재귀 적으로). Mac OSX 10.8.5를 사용하고 있습니다.

약 300,000 개의 파일에서 다음 echo명령은 10 초가 걸립니다 .

for file in `find . -name "*.xml"`; do echo >> $file; done

그러나 다음 touch명령은 10 분이 걸립니다 ! :

for file in `find . -name "*.xml"`; do touch $file; done

여기에 터치하는 것보다 에코가 너무 빠른 이유는 무엇입니까?


20
그냥 측면 발언 : 당신은 않는 두 명령은 당신이 동일하지 않는 아니라는 것을 알고? 적어도 유닉스 / 리눅스의 echo >> $file경우 개행을 추가 $file하여 수정합니다. OS / X에서도 동일하다고 가정합니다. 원하지 않으면을 사용하십시오 echo -n >> $file.
Dubu

2
또한 touch `find . -name "*.xml"` 위의 두 가지보다 빠르지 않을까요?
elmo

4
아니면 그냥 생각해보십시오>>$file
gerrit

8
명백한 질문에 대한 대답은 아니지만 왜 touch그렇게 여러 번 호출 합니까? 훨씬 적은 시간을 find . -name '*.xml' -print0 | xargs -0 touch호출합니다 touch(아마도 한 번만 가능). Linux에서 작동, OS X에서 작동해야 함
Mike Renfro

3
@elmo 인수 목록이 너무 깁니다 (쉽게, 300.000 파일 포함)
Rmano

답변:


161

배쉬에서, touch외부 바이너리이지만, echoA는 쉘 내장 :

$ type echo
echo is a shell builtin
$ type touch
touch is /usr/bin/touch

touch외부 바이너리 이므로 touch파일 당 한 번만 호출 하므로 셸은의 30 만 개의 인스턴스를 생성해야 touch하는데 시간이 오래 걸립니다.

echo그러나 셸 내장이며 셸 내장을 실행하는 데 전혀 포크가 필요하지 않습니다. 대신, 현재 쉘은 모든 조작을 수행하며 외부 프로세스는 작성되지 않습니다. 이것이 너무 빠른 이유입니다.

다음은 셸 작업의 두 가지 프로필입니다. 를 사용할 때 새 프로세스를 복제하는 데 많은 시간이 소요된다는 것을 알 수 있습니다 touch. /bin/echo쉘 내장 대신에 사용하면 훨씬 더 비슷한 결과가 나타납니다.


터치 사용

$ strace -c -- bash -c 'for file in a{1..10000}; do touch "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 56.20    0.030925           2     20000     10000 wait4
 38.12    0.020972           2     10000           clone
  4.67    0.002569           0     80006           rt_sigprocmask
  0.71    0.000388           0     20008           rt_sigaction
  0.27    0.000150           0     10000           rt_sigreturn
[...]

에코 사용

$ strace -c -- bash -c 'for file in b{1..10000}; do echo >> "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 34.32    0.000685           0     50000           fcntl
 22.14    0.000442           0     10000           write
 19.59    0.000391           0     10011           open
 14.58    0.000291           0     20000           dup2
  8.37    0.000167           0     20013           close
[...]

1
OS X에서 strace를 컴파일하거나 다른 OS에서 테스트를 실행 했습니까?
bmike

1
@bmike 내 테스트는 Linux에서 수행되지만 원칙은 동일합니다.
Chris Down

나는 완전히 동의한다-/ bin / echo가 / bin / touch만큼 느리게 추론이 건전하다는 주요 질문에 대한 나의 의견을 참조하십시오. 방금 strace의 타이밍을 재현하고 싶었고 dtruss / dtrace를 사용하여 실패했으며 bash -c 구문이 OS X에서도 예상대로 작동하지 않습니다.
bmike

71

다른 사람이 대답 것처럼, 사용 echo보다 더 빨리 될 것 touch같은 것은 echo(할 필요가 없습니다 불구하고) 일반적으로 내장 된 쉘에있는 명령입니다. 이를 사용하면 얻는 각 파일에 대해 새 프로세스를 시작하는 것과 관련된 커널 오버 헤드가 없어집니다 touch.

그러나이 효과를 얻는 가장 빠른 방법은 여전히 ​​사용하는 touch것이지만 각 파일에 대해 한 번만 프로그램을 실행하는 대신 -exec옵션 을 사용하여 find몇 번만 실행되도록 할 수도 있습니다. 이 방법은 일반적으로 쉘 루프와 관련된 오버 헤드를 피하기 때문에 더 빠릅니다.

find . -name "*.xml" -exec touch {} +

은 Using +(에 반대 \;하여) find ... -exec인수로 각 파일에 한 번만 가능하면 실행 명령을. 인수 목록이 매우 길면 (300,000 파일의 경우와 같이) ARG_MAX대부분의 시스템 에서 한계에 가까운 길이를 갖는 인수 목록으로 여러 번 실행됩니다 .

이 방법의 또 다른 장점은 원래 루프의 경우가 아닌 모든 공백 문자를 포함하는 파일 이름으로 강력하게 작동한다는 것입니다.


17
+1find +인수 를 지적합니다 . 나는 많은 사람들이 이것을 알지 못한다고 생각합니다 (그렇지 않았습니다).
gerrit

7
모든 버전의 주장 find이있는 것은 아닙니다 +. 로 배관하면 비슷한 효과를 얻을 수 있습니다 xargs.
Barmar

5
@Barmar +는 POSIX에 필요한 부분이므로 휴대용이어야합니다. -print0그렇지 않습니다.
Graeme

1
나는 때때로 그것을 가지고 있지 않은 구현을 겪습니다. YMMV.
Barmar

1
@ChrisDown, 내가 발견 한 것은 Busybox에 find사용 가능한 옵션이 있지만 ;표면 아래 처럼 취급한다는 것 입니다.
Graeme

29

echo쉘 내장입니다. 반면에 touch외부 바이너리입니다.

$ type echo
echo is a shell builtin
$ type touch
touch is hashed (/usr/bin/touch)

쉘 내장 명령은 전혀 없다 프로그램을로드에 관련된 오버 헤드, 즉 없기 때문에 훨씬 빠릅니다 fork/ exec있었습니다. 따라서 내장 명령과 외부 명령을 여러 번 실행할 때 상당한 시간 차이가 발생합니다.

이것이 같은 유틸리티 time를 쉘 내장으로 사용할 수 있는 이유입니다 .

다음과 같이 말하면 쉘 내장의 전체 목록을 얻을 수 있습니다.

enable -p

위에서 언급했듯이 내장 기능 과 반대로 유틸리티 를 사용하면 성능이 크게 저하됩니다. 다음은 내장유틸리티를 사용하여 ~ 9000 파일을 작성하는 데 걸린 시간의 통계입니다 . echo echo

# Using builtin
$ time bash -c 'for i in {1000..9999}; do echo > $i; done'

real    0m0.283s
user    0m0.100s
sys 0m0.184s

# Using utility /bin/echo
$ time bash -c 'for i in {1000..9999}; do /bin/echo > $i; done'

real    0m8.683s
user    0m0.360s
sys 0m1.428s

그리고 나는 echo대부분의 시스템에 바이너리 가 있다고 생각한다. (나를 위해 /bin/echo) 내장 된 대신 그것을 사용하여 타이밍 테스트를 다시 시도 할 수있다
Michael Mrozek

@MichaelMrozek 내장 및 이진에 대한 타이밍 테스트를 추가했습니다.
devnull
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.