![파일 설명자는 파일 쓰기에 최적화되어 있습니까?](https://linux55.com/image/133600/%ED%8C%8C%EC%9D%BC%20%EC%84%A4%EB%AA%85%EC%9E%90%EB%8A%94%20%ED%8C%8C%EC%9D%BC%20%EC%93%B0%EA%B8%B0%EC%97%90%20%EC%B5%9C%EC%A0%81%ED%99%94%EB%90%98%EC%96%B4%20%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C%3F.png)
파일 설명자에 쓰는 것이 아니라 파일에 직접 명령을 인쇄하는 것과 동일합니까?
삽화
파일에 직접 쓰기:
for i in {1..1000}; do >>x echo "$i"; done
fd를 사용하십시오:
exec 3>&1 1>x
for i in {1..1000}; do echo "$i"; done
exec 1>&3 3>&-
후자가 더 효율적인가요?
답변1
exec
루프 전에 파일을 여는 명령을 사용하는 것과 루프 내부에 리디렉션을 넣는 것의 주요 차이점은 전자는 파일 설명자를 한 번만 설정하면 되는 반면, 후자는 루프가 반복될 때마다 파일을 열고 닫는다는 것입니다.
한 번 수행하는 것이 더 효율적일 수 있지만 루프 내에서 외부 명령을 실행하는 경우 명령 시작에 따른 비용 차이가 사라질 수 있습니다. ( echo
아마도 여기에 내장되어 있으므로 적용되지 않습니다)
출력이 일반 파일이 아닌 파일로 전송되는 경우(예: x
명명된 파이프인 경우) 파일을 열고 닫는 동작이 다른 프로세스에 표시될 수 있으므로 동작에 차이가 있을 수 있습니다.
command에 의한 리디렉션과 command에 의한 리디렉션 사이에는 실제로 차이가 없습니다. exec
둘 다 파일을 열고 파일 설명자 번호를 처리합니다.
이 두 가지는 open()
파일이자 write()
파일이므로 거의 동일해야 합니다. (단, 명령 실행 중에는 fd 1이 다르게 저장됩니다.)
for i in {1..1000}; do
>>x echo "$i"
done
for i in {1..1000}; do
exec 3>&1 1>>x # assuming fd 3 is available
echo "$i" # here, fd 3 is visible to the command
exec 1>&3 3>&-
done
답변2
응, 그게 더 효율적이야
이를 테스트하는 가장 간단한 방법은 개수를 500000으로 늘리고 시간을 측정하는 것입니다.
> time bash s1.sh; time bash s2.sh
bash s1.sh 16,47s user 10,00s system 99% cpu 26,537 total
bash s2.sh 10,51s user 3,50s system 99% cpu 14,008 total
write
strace(1)은 이유를 밝힙니다( 대신 간단한 open
+5* fcntl
+2* dup
+2* close
+ 가 있습니다 write
):
우리는 다음 for i in {1..1000}; do >>x echo "$i"; done
을 얻습니다:
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "997\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "998\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "999\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "1000\n", 5) = 5
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
그리고 exec 3>&1 1>x
우리는 더 깨끗해질 것입니다
write(1, "995\n", 4) = 4
write(1, "996\n", 4) = 4
write(1, "997\n", 4) = 4
write(1, "998\n", 4) = 4
write(1, "999\n", 4) = 4
write(1, "1000\n", 5) = 5
그러나 차이점은 "FD 사용" 때문이 아니라 리디렉션을 수행하는 위치에 따른 것입니다. 예를 들어 이렇게 하면 for i in {1..1000}; do echo "$i"; done > x
두 번째 예와 거의 동일한 성능을 얻을 수 있습니다.
bash s3.sh 10,35s user 3,70s system 100% cpu 14,042 total
답변3
이 스레드에 몇 가지 새로운 정보를 요약하고 추가하기 위해 효율성에 따라 정렬된 네 가지 방법을 비교했습니다. 두 가지 테스트 시리즈를 기반으로 시간 측정(사용자 + 시스템)으로 100만 반복의 효율성을 추정합니다.
- 이 두 가지는 거의 동일합니다.
- 단순
>
루프 리디렉션(시간:100%) exec
전체 루프는 한 번 사용됩니다(시간:~100%)
- 단순
- 각 반복 에 대해
>>
(시간:200% - 250%) - 각 반복 에 대해
exec
(시간:340% - 480%)
결론은 이렇습니다.
하나 있다작은exec
간단한 리디렉션과 함께 사용합니다 (예 >>
: . (단순한 것이 더 저렴함). 단일 명령 실행 수준에서는 나타나지 않지만 반복 횟수가 증가할수록 차이가 눈에 띕니다. 명령의 실행 가중치가 섀도우로 리디렉션되지만 ikkachu가 다른 답변에서 알 수 있듯이 차이점.