programing

이 C 코드가 이 C++ 코드보다 빠른 이유는 무엇입니까? 파일에서 가장 큰 줄을 사용합니다.

powerit 2023. 9. 20. 20:49
반응형

이 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

에서 읽도록 stdinn이 큰을 미치지 음) 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

반응형