glibc의 sscanf가 리눅스의 fscanf보다 매우 느린 이유는 무엇입니까?
저는 x86_64 리눅스에서 GCC 4.8과 glibc 2.19를 사용하고 있습니다.
다른 질문에 대해 다른 입력 방법으로 게임을 하면서 비교해 보았습니다.fscanf그리고.sscanf. 구체적으로 말하자면, 나는 그들을fscanf표준 입력에 직접적으로 다음을 입력합니다.
char s[128]; int n;
while (fscanf(stdin, "%127s %d", s, &n) == 2) { }
아니면 먼저 전체 입력을 버퍼로 읽은 다음 버퍼를 통과합니다.sscanf. (모든 것을 버퍼로 읽는 것은 아주 적은 시간이 걸립니다.)
char s[128]; int n;
char const * p = my_data;
for (int b; sscanf(p, "%127s %d%n", s, &n, &b) == 2; p += b) { }
놀랍게도.fscanf버전이 엄청나게 빠릅니다.예를 들어, 다음과 같이 수만 개의 라인을 처리하는 경우fscanf시간이 오래 걸립니다.
10000 0.003927487 seconds time elapsed
20000 0.006860206 seconds time elapsed
30000 0.007933329 seconds time elapsed
40000 0.012881912 seconds time elapsed
50000 0.013516816 seconds time elapsed
60000 0.015670432 seconds time elapsed
70000 0.017393129 seconds time elapsed
80000 0.019837480 seconds time elapsed
90000 0.023925753 seconds time elapsed
지금도 마찬가지입니다.sscanf:
10000 0.035864643 seconds time elapsed
20000 0.127150772 seconds time elapsed
30000 0.319828373 seconds time elapsed
40000 0.611551668 seconds time elapsed
50000 0.919187459 seconds time elapsed
60000 1.327831544 seconds time elapsed
70000 1.809843039 seconds time elapsed
80000 2.354809588 seconds time elapsed
90000 2.970678416 seconds time elapsed
구글 퍼프 도구를 사용해서 측정하고 있었습니다.예를 들어, 50000 라인의 경우,fscanf코드는 약 50M 사이클을 필요로 하며,sscanf약 3300M 사이클을 코드화합니다.그래서 내가 최고의 통화 사이트들을 해체했어요.perf record/perf report.와 함께fscanf:
35.26% xf libc-2.19.so [.] _IO_vfscanf
23.91% xf [kernel.kallsyms] [k] 0xffffffff8104f45a
8.93% xf libc-2.19.so [.] _int_malloc
그리고.sscanf:
98.22% xs libc-2.19.so [.] rawmemchr
0.68% xs libc-2.19.so [.] _IO_vfscanf
0.38% xs [kernel.kallsyms] [k] 0xffffffff8104f45a
그래서 거의 모든 시간에sscanf에 소비됩니다.rawmemchr! 이게 왜죠?어쩜 그래요?fscanf코드는 이 비용을 피합니까?
검색을 해봤지만, 이 잠금에 대한 논의가 최선의 방법이었습니다.realloc여기에는 해당되지 않는 전화입니다.나는 또한 생각하고 있었습니다.fscanf메모리 로컬리티(동일한 버퍼를 반복해서 사용)가 더 좋지만, 그렇게 큰 차이를 만들 수는 없습니다.
이 이상한 불일치에 대한 통찰력을 가진 사람이 있습니까?
sscanf()는 당신이 전달하는 문자열을 변환합니다._IO_FILE*"파일"처럼 보이게 하기 위해서입니다.이는 문자열과 FILE*에 모두 동일한 내부 _IO_vfscanf()를 사용할 수 있습니다.
그러나 _IO_str_init_static_internal() 함수에서 수행되는 변환의 일부로 다음을 호출합니다.__rawmemchr (ptr, '\0');기본적으로 입력 문자열에 있는 strlen(스트렌) 호출입니다.이 변환은 sscanf()로 호출할 때마다 수행되며 입력 버퍼가 다소 크기 때문에 입력 문자열의 길이를 계산하는 데 상당한 시간이 소요됩니다.
fmemopen() 및 fscanf()를 사용하여 입력 문자열에서 FILE*을 생성하는 것도 방법이 될 수 있습니다.
글리비씨의 것처럼 보입니다.sscanf()는 소스 문자열의 길이를 검사한 후 다른 작업을 수행합니다.
sscanf()(에서)stdio-common/sscanf.c)는 기본적으로 에 대한 호출 주위의 포장지입니다._IO_vsscanf()(에서)libio/iovsscanf.c). 그리고 가장 먼저 한 것은_IO_vsscanf()does는 자신의 것을 초기화합니다._IO_strfile호출에 의한 구조_IO_str_init_static_internal()(에서)libio/strops.c제공되지 않을 경우 문자열의 길이를 계산합니다.
언급URL : https://stackoverflow.com/questions/23923924/why-is-glibcs-sscanf-vastly-slower-than-fscanf-on-linux
'programing' 카테고리의 다른 글
| 대량 삽입 또는 최대 절전 모드로 업데이트하시겠습니까? (0) | 2023.09.16 |
|---|---|
| MySQL varchar 변수 선언 및 사용 (0) | 2023.09.16 |
| 서비스를 시작하는 동안 오류가 발생했습니다.Oracle MTSRecovery Service를 찾을 수 없습니다. (0) | 2023.09.16 |
| istringstream, ostringstream, stringstream의 차이점은 무엇입니까? / 모든 경우에 stringstream을 사용하면 어떨까요? (0) | 2023.09.16 |
| MySQL 원격 연결이 결과를 표시하는 데 3분 이상 소요됨 (0) | 2023.09.16 |