이 C 코드가 이 C++ 코드보다 빠른 이유는 무엇입니까? 파일에서 가장 큰 줄을 사용합니다.
저는 기본적으로 동일한 작업을 수행하는 두 가지 버전의 프로그램을 가지고 있는데, 파일에서 줄의 최대 길이를 얻을 수 있고, 약 8,000 줄의 파일을 가지고 있으며, C의 코드는 C++의 코드보다 약간 더 원시적입니다(물론!).C 프로그램은 실행하는 데 약 2초가 걸리지만 C++의 프로그램은 실행하는 데 약 10초가 걸립니다(두 경우 모두 테스트하고 있는 동일한 파일).하지만 왜 그랬을까?같은 시간이 걸리거나 조금 더 걸리지만 8초 정도는 느릴 줄 알았어요!
내 코드는 C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if _DEBUG
#define DEBUG_PATH "../Debug/"
#else
#define DEBUG_PATH ""
#endif
const char FILE_NAME[] = DEBUG_PATH "data.noun";
int main()
{
int sPos = 0;
int maxCount = 0;
int cPos = 0;
int ch;
FILE *in_file;
in_file = fopen(FILE_NAME, "r");
if (in_file == NULL)
{
printf("Cannot open %s\n", FILE_NAME);
exit(8);
}
while (1)
{
ch = fgetc(in_file);
if(ch == 0x0A || ch == EOF) // \n or \r or \r\n or end of file
{
if ((cPos - sPos) > maxCount)
maxCount = (cPos - sPos);
if(ch == EOF)
break;
sPos = cPos;
}
else
cPos++;
}
fclose(in_file);
printf("Max line length: %i\n", maxCount);
getch();
return (0);
}
C++로 된 내 코드:
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string>
using namespace std;
#ifdef _DEBUG
#define FILE_PATH "../Debug/data.noun"
#else
#define FILE_PATH "data.noun"
#endif
int main()
{
string fileName = FILE_PATH;
string s = "";
ifstream file;
int size = 0;
file.open(fileName.c_str());
if(!file)
{
printf("could not open file!");
return 0;
}
while(getline(file, s) )
size = (s.length() > size) ? s.length() : size;
file.close();
printf("biggest line in file: %i", size);
getchar();
return 0;
}
내 생각에 그것은 당신이 사용하고 있는 컴파일러 옵션, 컴파일러 자체 또는 파일 시스템의 문제인 것 같습니다.방금 (최적화 기능이 설정된) 두 버전을 모두 컴파일한 후 92,000 줄의 텍스트 파일에 대해 실행했습니다.
c++ version: 113 ms
c version: 179 ms
그리고 C++ 버전이 더 빠른 이유는 fgetc가 느리기 때문이 아닐까 생각합니다.fgetc
버퍼 I/O를 사용하지만 모든 문자를 검색하기 위해 함수 호출을 수행하고 있습니다.해 본 이 있고요.fgetc
는 한을 읽기 만큼 빠르지 다예:교).fgets
).
그래서 저는 사람들의 대답을 몇 가지 언급에서 반복해서 들었습니다. 문제는 C++ 버전에서 행해지는 추가 복사 때문일 것이고, 문자열로 행을 메모리에 복사하는 것입니다.하지만 저는 그것을 시험해보고 싶었습니다.
우선 fgetc와 getline 버전을 구현하고 타이밍을 잡았습니다.디버그 모드에서 getline 버전이 fgetc 버전의 경우 약 130 µ 대 60 µ로 느리다는 것을 확인했습니다.이는 iOS 스트림이 stdio를 사용하는 것보다 느리다는 통념을 감안할 때 놀라운 일이 아닙니다.그러나 과거에는 최적화를 통해 iOS 스트림이 상당한 속도를 내는 것이 경험이었습니다.이것은 getline을 사용하는 약 20개의 µ와 fgetc를 사용하는 48개의 µ로 나의 릴리스 모드 시간을 비교했을 때 확인되었습니다.
적어도 릴리스 모드에서는 getline을 iostreams와 함께 사용하는 것이 fgetc보다 빠르다는 사실은 모든 데이터를 복사하는 것이 복사하지 않는 것보다 느려야 한다는 추론과 배치되므로 모든 최적화를 피할 수 있는 것이 무엇인지 확신할 수 없으며 실제로 어떤 설명도 찾지 못했습니다.하지만 최적화된 방법이 무엇인지 이해하는 것은 흥미로울 것입니다.edit: 프로파일러를 사용하여 프로그램을 살펴보니 서로 다른 방법이 너무 달라 보여서 성능을 비교하는 방법이 명확하지 않았습니다.
를 피해서 더 수 있는지 알고 .get()
하고 C fstream하고 C합니다. 때 다를 하는 것을 상당히 .fstream::get()
디버그 및 릴리스 모두에서 fgetc 및 getline 메서드보다 상당히 느렸습니다. 디버그에서는 약 230 µs, 릴리스에서는 80 µs입니다.
는 앞으로 를 들어 fstream 된 stream_buf 를했고를 에는하면,snextc()
그것에 대한 방법.이 버전이 가장 빠릅니다. 디버그에서는 25 µ, 릴리스에서는 6 µ입니다.
를 fstream::get()
메소드가 훨씬 느리다는 것은 매 호출마다 보초 개체를 구성한다는 것입니다.안 저 보이네요.get()
는 단순히 stream_buf에서 다음 문자를 얻는 것을 넘어 이러한 초병 개체를 제외하고는 훨씬 더 많은 일을 합니다.
어쨌든 이야기의 교훈은 빠른 io를 원한다면 stdio보다는 높은 수준의 io 스트림 기능을 사용하는 것이 최선일 것이고, 정말 빠른 io를 위해서는 기본 stream_buf에 액세스하는 것이 최선일 것입니다.edit: 실제로 이 도덕성은 MSVC에만 적용될 수 있습니다. 다른 툴체인의 결과에 대해서는 하단의 업데이트를 참조하십시오.
참고용:
VS2010과 boost 1.47의 크로노를 타이밍으로 사용했습니다.32비트 바이너리를 만들었습니다(부스트 크로노가 해당 lib의 64비트 버전을 찾을 수 없는 것 같아 필요한 것 같습니다).컴파일 옵션을 조정하지는 않았지만 내가 주변에 있는 스크래치 대 프로젝트에서 이 작업을 했기 때문에 완전히 표준이 아닐 수 있습니다.
제가 테스트한 파일은 Project Gutenberg, http://www.gutenberg.org/ebooks/35390 의 Fredédéric Bastiat의 Ouvres Compètes de Frédéric Bastiat의 1.1 MB 평문 버전입니다.
해제 모드 시간
fgetc time is: 48150 microseconds
snextc time is: 6019 microseconds
get time is: 79600 microseconds
getline time is: 19881 microseconds
디버그 모드 시간:
fgetc time is: 59593 microseconds
snextc time is: 24915 microseconds
get time is: 228643 microseconds
getline time is: 130807 microseconds
여기 있습니다.fgetc()
버전:
{
auto begin = boost::chrono::high_resolution_clock::now();
FILE *cin = fopen("D:/bames/automata/pg35390.txt","rb");
assert(cin);
unsigned maxLength = 0;
unsigned i = 0;
int ch;
while(1) {
ch = fgetc(cin);
if(ch == 0x0A || ch == EOF) {
maxLength = std::max(i,maxLength);
i = 0;
if(ch==EOF)
break;
} else {
++i;
}
}
fclose(cin);
auto end = boost::chrono::high_resolution_clock::now();
std::cout << "max line is: " << maxLength << '\n';
std::cout << "fgetc time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n';
}
여기 있습니다.getline()
버전:
{
auto begin = boost::chrono::high_resolution_clock::now();
std::ifstream fin("D:/bames/automata/pg35390.txt",std::ios::binary);
unsigned maxLength = 0;
std::string line;
while(std::getline(fin,line)) {
maxLength = std::max(line.size(),maxLength);
}
auto end = boost::chrono::high_resolution_clock::now();
std::cout << "max line is: " << maxLength << '\n';
std::cout << "getline time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n';
}
fstream::get()
n
{
auto begin = boost::chrono::high_resolution_clock::now();
std::ifstream fin("D:/bames/automata/pg35390.txt",std::ios::binary);
unsigned maxLength = 0;
unsigned i = 0;
while(1) {
int ch = fin.get();
if(fin.good() && ch == 0x0A || fin.eof()) {
maxLength = std::max(i,maxLength);
i = 0;
if(fin.eof())
break;
} else {
++i;
}
}
auto end = boost::chrono::high_resolution_clock::now();
std::cout << "max line is: " << maxLength << '\n';
std::cout << "get time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n';
}
.snextc()
{
auto begin = boost::chrono::high_resolution_clock::now();
std::ifstream fin("D:/bames/automata/pg35390.txt",std::ios::binary);
std::filebuf &buf = *fin.rdbuf();
unsigned maxLength = 0;
unsigned i = 0;
while(1) {
int ch = buf.snextc();
if(ch == 0x0A || ch == std::char_traits<char>::eof()) {
maxLength = std::max(i,maxLength);
i = 0;
if(ch == std::char_traits<char>::eof())
break;
} else {
++i;
}
}
auto end = boost::chrono::high_resolution_clock::now();
std::cout << "max line is: " << maxLength << '\n';
std::cout << "snextc time is: " << boost::chrono::duration_cast<boost::chrono::microseconds>(end-begin) << '\n';
}
업데이트:
저는 libc++로 OS X에서 clang(트렁크)을 사용하여 테스트를 다시 진행했습니다.는 ( 있는 iosstream는에서)되었습니다.fstream::get()
보다 훨씬 std::getline()
보다 훨씬 filebuf::snextc()
의 .fgetc()
에 비해 getline()
구현 속도가 빨라졌습니다.입니다에서 를 했기 일 것입니다.getline()
MSVC의 문제가 아닌 이 툴체인의 문제가 되는 것은?마이크로소프트의 CRT 구현인 fgetc()가 나쁜 것은 아닐까요?
어쨌든 다음과 같은 시간이 있습니다(저는 훨씬 더 큰 파일인 5.3MB를 사용했습니다).
-O 사용
fgetc time is: 39004 microseconds
snextc time is: 19374 microseconds
get time is: 145233 microseconds
getline time is: 67316 microseconds
-O0 사용
fgetc time is: 44061 microseconds
snextc time is: 92894 microseconds
get time is: 184967 microseconds
getline time is: 209529 microseconds
-O2
fgetc time is: 39356 microseconds
snextc time is: 21324 microseconds
get time is: 149048 microseconds
getline time is: 63983 microseconds
-O3
fgetc time is: 37527 microseconds
snextc time is: 22863 microseconds
get time is: 145176 microseconds
getline time is: 67899 microseconds
C++ 버전에서는 std::string의 인스턴스를 지속적으로 할당 및 할당 해제합니다.메모리 할당은 비용이 많이 드는 작업입니다.또한 생성자/파괴자가 실행됩니다.
그러나 C 버전은 일정한 메모리를 사용하며, 각 새 줄에 대해 한 글자씩 읽고 줄 길이 카운터를 새 값으로 설정하면 됩니다.
당신은 사과를 사과와 비교하는 것이 아닙니다.의 C 의 C다에서 .FILE*
버퍼를 프로그램의 메모리에 저장합니다.원시 파일에서도 작동합니다.
은 각 번한은 C++ 의 한 할지 알 수 . 스트림 코드에 한 번 입력하면 반환되는 문자열을 종료할 시기를 알 수 있고, 생성자에 한 번 입력해야 합니다.std::string
,
네 암호에 따라 한 번은 .s.length()
를 들어 , C 를 C시킬 수 getc_unlocked
당신이 이용할 수 있다면요.하지만 가장 큰 이점은 데이터를 복사하지 않아도 된다는 것입니다.
EDIT : bames53의 코멘트에 대응하여 편집됨
8,000줄에 2초?대사가 얼마나 긴지는 모르겠지만 아주 잘못하고 있을 가능성이 높습니다.
이 사소한 파이썬 프로그램은 프로젝트 구텐베르그에서 다운로드 받은 엘키요트(40006행, 2.2)로 거의 즉시 실행됩니다.MB):
import sys
print max(len(s) for s in sys.stdin)
타이밍:
~/test$ time python maxlen.py < pg996.txt
76
real 0m0.034s
user 0m0.020s
sys 0m0.010s
문자로 읽는 것보다 입력을 버퍼링하여 C 코드를 향상시킬 수 있습니다.
C++가 C보다 느린 이유에 대해서는 문자열 객체를 구축한 다음 길이 방법을 호출하는 것과 관련이 있어야 합니다.C에서 당신은 당신이 가는 길에 문자들을 세는 것 뿐입니다.
C++ 소스의 40K 라인에 대해 귀사의 프로그램을 컴파일하여 실행해보았는데, 모두 약 25ms 만에 완료되었습니다.당신의 입력 파일은 줄당 10K-100K 문자 정도로 매우 긴 줄을 가지고 있을 뿐입니다.이 경우 C 버전은 긴 줄 길이에서 부정적인 성능이 나타나지 않지만 C++ 버전은 문자열의 크기를 계속 증가시키고 이전 데이터를 새 버퍼에 복사해야 합니다.크기가 커져야 한다면 과도한 성능 차이를 설명할 수 있는 충분한 횟수가 필요합니다.
여기서 핵심은 두 프로그램이 동일한 작업을 수행하지 않기 때문에 결과를 실제로 비교할 수 없다는 것입니다.입력 파일을 제공할 수 있다면 추가적인 정보를 제공할 수 있을 것입니다.
당신은 아마도 사용할 수 있을 것입니다.tellg
그리고.ignore
이것을 C++에서 더 빨리 할 수 있습니다.
C++ 프로그램은 선의 문자열 객체를 만들고, C 프로그램은 단지 문자를 읽고 문자를 봅니다.
편집:
찬성표 주셔서 감사합니다만, 토론 끝에 저는 이제 이 답변이 틀렸다고 생각합니다.이는 합리적인 첫 번째 추측이었지만, 이 경우 실행 시간이 다른(그리고 매우 느린) 다른 것에 의해 발생하는 것으로 보입니다.
저는 이론에 대해서는 괜찮아요 여러분.하지만 경험적인 것을 알아보겠습니다.
1,300만 줄의 텍스트 파일로 파일을 생성했습니다.
~$ for i in {0..1000}; do cat /etc/* | strings; done &> huge.txt
에서 읽도록 stdin
n이 큰을 미치지 음) 2했습니다.
C++ 코드:
#include <iostream>
#include <stdio.h>
using namespace std;
int main(void)
{
string s = "";
int size = 0;
while (cin) {
getline(cin, s);
size = (s.length() > size) ? s.length() : size;
}
printf("Biggest line in file: %i\n", size);
return 0;
}
C++ 시간:
~$ time ./cplusplus < huge.txt
real 1m53.122s
user 1m29.254s
sys 0m0.544s
'C' 버전:
#include <stdio.h>
int main(void)
{
char *line = NULL;
int read, max = 0, len = 0;
while ((read = getline(&line, &len, stdin)) != -1)
if (max < read)
max = read -1;
printf("Biggest line in file %d\n", max);
return 0;
}
C 성능:
~$ time ./ansic < huge.txt
real 0m4.015s
user 0m3.432s
sys 0m0.328s
당신만의 수학을...
언급URL : https://stackoverflow.com/questions/8852835/why-is-this-c-code-faster-than-this-c-code-getting-biggest-line-in-file
'programing' 카테고리의 다른 글
"내부/modules/cj/loader.js:582 throw err" (0) | 2023.09.20 |
---|---|
브라우저의 스크롤 바 크기를 얻으려면 어떻게 해야 합니까? (0) | 2023.09.20 |
angularjs-app에서 ($q 사용) 모든 비동기 동작에 대한 로딩 지시자를 만드는 방법 (0) | 2023.09.20 |
도커 파일의 상위 이미지에서 진입점을 제거하는 방법 (0) | 2023.09.20 |
Oracle Sequence nextval이 앞뒤로 점프하는 숫자입니다. (0) | 2023.09.20 |