programing

왜 이렇게 인쇄가 느리죠?더 빨리 할 수 있나요?

nicescript 2022. 12. 29. 21:52
반응형

왜 이렇게 인쇄가 느리죠?더 빨리 할 수 있나요?

인쇄문만으로 단말기에 출력하는 데 시간이 걸리는 것에 항상 놀라거나 좌절했습니다.최근 매우 느린 로그 기록 후 알아보기로 결정했는데, 거의 모든 시간이 단말기가 결과를 처리하기를 기다리고 있다는 것을 알고는 매우 놀랐습니다.

stdout에 쓰는 것을 어떻게 해서든 빨리 할 수 있을까요?

썼습니다.print_timer.py의 ' ')는 stdout, 'stdout', 'stdout', 'stdout' /dev/null타이밍 결과는 다음과 같습니다.

$ python print_timer.py
this is a test
this is a test
<snipped 99997 lines>
this is a test
-----
timing summary (100k lines each)
-----
print                         :11.950 s
write to file (+ fsync)       : 0.122 s
print with stdout = /dev/null : 0.050 s

와, python이 stdout을 /dev/null로 재할당했다는 것을 인지하는 등 뒤에서 뭔가를 하고 있지 않은지 확인하기 위해 스크립트 외부에서 리다이렉트를 했습니다.

$ python print_timer.py > /dev/null
-----
timing summary (100k lines each)
-----
print                         : 0.053 s
write to file (+fsync)        : 0.108 s
print with stdout = /dev/null : 0.045 s

그래서 이건 비단뱀의 속임수가 아니라 단지 단말기의 속임수입니다.출력을 /dev/null로 덤핑하는 것은 항상 알고 있었지만, 그렇게 중요한지 몰랐어요!

나는 TTY가 얼마나 느린지 놀랍다.물리적 디스크에 쓰는 것이 "화면"에 쓰는 것보다 훨씬 빠르고(아마도 전체 RAM 운영) 효과적으로 /dev/null을 사용하여 가비지에 버리는 것만큼 빠를 수 있습니까?

링크는 단말기가 I/O를 차단하여 "[입력] 해석, 프레임 버퍼 업데이트, 윈도우 스크롤을 위한 X 서버와의 통신 "을 할 수 있는 방법에 대해 설명하지만 완전히 이해하지 못합니다.뭐가 그렇게 오래 걸려?

(빠른 tty 실장 이외에는) 방법이 없다고 생각합니다만, 어쨌든 묻고 싶습니다.


업데이트: 코멘트를 읽고 나서, 제 화면 사이즈가 실제로 인쇄 시간에 미치는 영향이 어느 정도인지 궁금했습니다.그것은 확실히 의미가 있습니다.위의 매우 느린 수치는 1920x1200까지 확대된 Gnome 단말기의 수치입니다.아주 작게 줄이면...

-----
timing summary (100k lines each)
-----
print                         : 2.920 s
write to file (+fsync)        : 0.121 s
print with stdout = /dev/null : 0.048 s

확실히 (~4배)는 낫지만, 제 질문은 바뀌지 않습니다.터미널 화면 렌더링이 왜 stdout에 응용 프로그램을 쓰는 데 시간이 걸리는지 이해할 수 없기 때문에 질문만 더합니다.프로그램이 화면 렌더링을 계속할 때까지 기다려야 하는 이유는 무엇입니까?

모든 단말/TTY 앱이 동일하지 않은가?나는 아직 실험을 하지 않았다.단말기는 모든 착신 데이터를 버퍼링하여 보이지 않게 해석/렌더하고 현재 화면 구성에서 볼 수 있는 최신 청크만 적절한 프레임 속도로 렌더링할 수 있어야 합니다.따라서 0.1초 이내에 디스크에 +fsync를 쓸 수 있다면 단말기는 동일한 작업을 동일한 순서로 완료할 수 있습니다(화면 업데이트를 몇 번 하면 가능).

이 동작을 프로그래머에게 더 좋게 하기 위해 어플리케이션 측에서 변경할 수 있는 tty 설정이 있으면 좋겠다고 생각하고 있습니다.이것이 터미널 어플리케이션의 문제일 경우 StackOverflow에 속하지 않을 수도 있습니다.

제가 무엇을 빠뜨리고 있나요?


다음은 타이밍 생성에 사용되는 python 프로그램입니다.

import time, sys, tty
import os

lineCount = 100000
line = "this is a test"
summary = ""

cmd = "print"
startTime_s = time.time()
for x in range(lineCount):
    print line
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)

#Add a newline to match line outputs above...
line += "\n"

cmd = "write to file (+fsync)"
fp = file("out.txt", "w")
startTime_s = time.time()
for x in range(lineCount):
    fp.write(line)
os.fsync(fp.fileno())
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)

cmd = "print with stdout = /dev/null"
sys.stdout = file(os.devnull, "w")
startTime_s = time.time()
for x in range(lineCount):
    fp.write(line)
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)

print >> sys.stderr, "-----"
print >> sys.stderr, "timing summary (100k lines each)"
print >> sys.stderr, "-----"
print >> sys.stderr, summary

물리적 디스크에 쓰는 것이 "화면"에 쓰는 것보다 훨씬 빠르고(아마도 전체 RAM 운영) 효과적으로 /dev/null을 사용하여 가비지에 버리는 것만큼 빠를 수 있습니까?

축하합니다. I/O 버퍼링의 중요성을 알게 되었습니다. :- )

디스크는 버퍼가 높기 때문에 고속으로 보입니다: 모든 Python의write()실제 물리 디스크에 쓰기 전에 콜이 반환됩니다.(OS는 나중에 수천 개의 개별 쓰기를 하나의 크고 효율적인 청크로 결합하여 이 작업을 수행합니다.)

는 버퍼링을 거의 의 개인은 버퍼링을 실시하지 않습니다.각각의 개개의 버퍼링printwrite(line)전체 쓰기(즉, 디스플레이에서 출력 장치로)가 완료될 때까지 기다립니다.

비교를 공평하게 하기 위해서는 파일테스트에서 단말기와 동일한 출력 버퍼링을 사용해야 합니다.이러한 출력 버퍼링은 예를 다음과 같이 수정함으로써 실행할 수 있습니다.

fp = file("out.txt", "w", 1)   # line-buffered, like stdout
[...]
for x in range(lineCount):
    fp.write(line)
    os.fsync(fp.fileno())      # wait for the write to actually complete

제 기계에서 파일 쓰기 테스트를 실행했는데 버퍼링으로 10만 줄 동안 0.05초도 나왔습니다.

단, 위의 변경으로 버퍼 없이 쓸 수 있게 되어 디스크에 1,000줄만 쓸 수 있게 되어 40초밖에 걸리지 않습니다.10만 줄을 기다리는 것은 포기했지만, 조금 전의 추정으로는 1시간 이상 걸릴 것 같습니다.

그럼 터미널의 11초가 시야에 들어오게 되는 거네요?

그래서 당신의 첫 번째 질문에 대답하자면, 단말기에 쓰는 것은 사실 매우 빠르고, 모든 것을 고려해 볼 때 훨씬 더 빨리 쓸 수 있는 공간이 많지 않다(단, 단말기마다 작업량이 다릅니다. 이 답변에 대한 Russ의 코멘트를 참조하십시오).

(디스크 I/O와 같이 쓰기 버퍼링을 추가할 수 있지만 버퍼가 플러시될 때까지 단말기에 기록된 내용을 볼 수 없습니다.이는 인터랙티브와 벌크 효율의 트레이드오프입니다.

댓글 달아주셔서 감사합니다.당신의 도움으로 제가 직접 답변하게 되었습니다.그래도 네 질문에 대답하는 건 지저분한 기분이야.

질문 1: 인쇄가 느린 이유는 무엇입니까?

답변: stdout으로의 인쇄가 본질적으로 느린 것은 아닙니다.작업하는 단말기가 느립니다.애플리케이션 측의 I/O 버퍼링(예: python 파일 버퍼링)과는 거의 관계가 없습니다.이하를 참조해 주세요.

질문 2: 속도를 높일 수 있습니까?

답변: 가능하지만 프로그램 측('인쇄'를 하는 측)에서는 그렇지 않은 것 같습니다.속도를 높이려면 보다 빠른 터미널 에뮬레이터를 사용합니다.

설명...

''경량'.wterm훨씬 더 좋은 결과를 얻었습니다.실행 시의 테스트스크립트 출력은 다음과 같습니다(질문 하단에 있습니다).wtermgnome-terminal을 사용하여 가 걸린 합니다.

-----타이밍 요약(각각 10만 회선)-----인쇄: 0.261초파일에 쓰기(+fsync): 0.110초stdout = /dev/slots: 0.050초로 인쇄

00보다 .26s가 12s보다 훨씬 낫다!나는 잘 모르겠다wterm('를 적절한 프레임 방법)이나 '덜 ' 인지('알겠습니다')보다 더 .gnome-terminal제 질문의 목적상, 저는 답을 알고 있습니다. gnome-terminal

따라서 - 장시간 실행 중인 스크립트가 느리고 대량의 텍스트가 stdout으로 전송되는 경우...다른 터미널에서 더 나은지 확인해 보세요!

는 거의 로 뽑힌 을 주의해 주세요.wtermubuntu/subinian 저장소에서 가져옵니다. 링크는 같은 단말기일 수 있지만 확실하지 않습니다.다른 터미널 에뮬레이터는 테스트하지 않았습니다.


업데이트: 가려운 곳을 긁어야 했기 때문에 동일한 스크립트와 풀스크린(1920x1200)으로 다른 터미널 에뮬레이터를 모두 테스트했습니다.수동으로 수집한 통계는 다음과 같습니다.

wterm 0.3초a항 0.3초rxvt 0.3초mrxvt 0.4skonsole 0.6s야쿠아케 0.7slxterminal 7sxterm 9sgnome-terminal 12sxfce4 단자 12s밸라 말단 18sxvt 48s

기록된 시간은 수동으로 수집되지만 꽤 일관성이 있습니다.나는 최고의 값을 기록했다.YMMV, 당연히.

보너스로, 그것은 다양한 터미널 에뮬레이터를 이용할 수 있는 흥미로운 투어였다!나는 나의 첫 번째 '대체' 시험이 최고인 것으로 판명되어 놀랐다.

프로그램이 출력 FD가 tty를 가리키는지 여부를 판별할 수 있기 때문에 리다이렉션은 아무것도 하지 않을 수 있습니다.

(C의 스트림 동작과 같은) 단말기를 가리킬 때 stdout이 회선 버퍼링될 수 있습니다.

재미있는 실험으로 출력 파이프 접속을 시도합니다.cat.


저만의 재미있는 실험을 해봤는데, 결과는 이렇습니다.

$ python test.py 2>foo
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 6.040 s
write to file                 : 0.122 s
print with stdout = /dev/null : 0.121 s

$ python test.py 2>foo |cat
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 1.024 s
write to file                 : 0.131 s
print with stdout = /dev/null : 0.122 s

기술적인 세부 사항은 잘 모르기 때문에 말씀드릴 수 없지만, 놀랍지도 않습니다. 단말기는 이렇게 많은 데이터를 인쇄하도록 설계되지 않았습니다.실제로 인쇄를 원할 때마다 수행해야 하는 수많은 GUI에 대한 링크도 제공합니다.스크립트를 호출하면pythonw대신 15초가 걸리지 않습니다.이것은 전적으로 GUI의 문제입니다.리다이렉트stdout파일에 저장하여 이 문제를 방지합니다.

import contextlib, io
@contextlib.contextmanager
def redirect_stdout(stream):
    import sys
    sys.stdout = stream
    yield
    sys.stdout = sys.__stdout__

output = io.StringIO
with redirect_stdout(output):
    ...

터미널로의 인쇄가 느립니다.아쉽게도 새로운 터미널 구현을 작성하지 않은 상태에서는 이 속도를 크게 높일 수 있을지 모르겠습니다.

출력은 아마 라인 버퍼 모드로 디폴트 되어 있을 뿐만 아니라 단말기로의 출력은 최대 스루풋을 가진 단말기와 시리얼 회선에 데이터를 흘리거나 의사 단말기와 디스플레이 이벤트 루프를 처리하는 별도의 프로세스에 데이터를 흘려보내고 일부 글꼴에서 문자를 렌더링하고 디스플레이 비트를 이동하여 스크롤을 구현합니다.를 표시합니다.후자의 시나리오는 아마도 여러 프로세스(예: telnet 서버/클라이언트, 터미널 애플리케이션, X11 디스플레이 서버)에 분산되어 있기 때문에 컨텍스트 스위칭 및 지연 문제도 발생합니다.

언급URL : https://stackoverflow.com/questions/3857052/why-is-printing-to-stdout-so-slow-can-it-be-sped-up

반응형