IOStream의 성능을 향상시키는 방법은 무엇입니까?
C를 배운 대부분의 C ++ 사용자 는 C ++로 코딩 할 때에도 printf
/ scanf
계열 함수 를 사용하는 것을 선호합니다 .
나는 인터페이스가 더 좋다는 것을 인정하지만 (특히 POSIX와 같은 형식과 지역화), 압도적 인 관심은 성능 인 것 같습니다.
이 질문을 살펴보면 :
가장 좋은 대답은 사용 fscanf
하는 것이며 C ++ ifstream
는 지속적으로 2-3 배 느립니다.
IOStreams 성능을 향상시키기 위해 "팁"저장소를 컴파일 할 수 있다면 좋을 것이라고 생각했습니다.
고려할 사항
- 버퍼링 (
rdbuf()->pubsetbuf(buffer, size)
) - 동기화 (
std::ios_base::sync_with_stdio
) - 로케일 처리 (잘린 로케일을 사용하거나 모두 제거 할 수 있습니까?)
물론 다른 접근 방식도 환영합니다.
참고 : Dietmar Kuhl의 "새로운"구현이 언급되었지만 자세한 내용을 찾을 수 없었습니다. 이전 참조는 죽은 링크 인 것 같습니다.
지금까지 내가 수집 한 내용은 다음과 같습니다.
버퍼링 :
기본적으로 버퍼가 매우 작은 경우 버퍼 크기를 늘리면 성능이 확실히 향상 될 수 있습니다.
- 그것은 HDD 히트의 수를 줄입니다
- 시스템 호출 수를 줄입니다.
기본 streambuf
구현 에 액세스하여 버퍼를 설정할 수 있습니다 .
char Buffer[N];
std::ifstream file("file.txt");
file.rdbuf()->pubsetbuf(Buffer, N);
// the pointer reader by rdbuf is guaranteed
// to be non-null after successful constructor
@iavr의 의례 경고 :에 따라 cppreference 이 전화하는 것이 가장 좋습니다 pubsetbuf
파일을 열기 전에. 그렇지 않으면 다양한 표준 라이브러리 구현이 다른 동작을 갖습니다.
로케일 처리 :
로케일은 문자 변환, 필터링 및 숫자 나 날짜가 관련된 더 영리한 트릭을 수행 할 수 있습니다. 동적 디스패치 및 가상 통화의 복잡한 시스템을 거치므로이를 제거하면 페널티 히트를 줄이는 데 도움이 될 수 있습니다.
기본 C
로케일은 변환을 수행하지 않고 시스템간에 균일 함을 의미합니다. 사용하기에 좋은 기본값입니다.
동기화:
이 시설을 사용하면 성능 향상을 볼 수 없습니다.
정적 함수를 사용하여 전역 설정 (의 정적 멤버 std::ios_base
)에 액세스 할 수 있습니다 sync_with_stdio
.
측정 :
이것을 가지고 gcc 3.4.2
SUSE 10p3에서 사용하여 컴파일 한 간단한 프로그램을 가지고 놀았습니다 -O2
.
C : 7.76532e + 06
C ++ : 1.0874e + 07
20%
기본 코드 의 경우 약 ... 의 속도 저하를 나타냅니다 . 실제로 버퍼 (C 또는 C ++) 또는 동기화 매개 변수 (C ++)를 조작해도 개선되지 않았습니다.
다른 사람의 결과 :
@Irfy on g ++ 4.7.2-2ubuntu1, -O3, 가상화 된 Ubuntu 11.10, 3.5.0-25-generic, x86_64, 충분한 ram / cpu, 196MB의 여러 "find / >> largefile.txt"실행
C : 634572 C ++ : 473222
C ++ 25 % 더 빠름
@Matteo Italia on g ++ 4.4.5, -O3, Ubuntu Linux 10.10 x86_64 with a random 180 MB file
C : 910390
C ++ : 776016
C ++ 17 % 더 빠름
@Bogatyr on g ++ i686-apple-darwin10-g ++-4.2.1 (GCC) 4.2.1 (Apple Inc. 빌드 5664), mac mini, 4GB ram, 168MB 데이터 파일이있는이 테스트를 제외하고 유휴
C : 4.34151e + 06
C ++ : 9.14476e + 06
C ++ 111 % 느림
@Asu on clang ++ 3.8.0-2ubuntu4, Kubuntu 16.04 Linux 4.8-rc3, 8GB ram, i5 Haswell, Crucial SSD, 88MB datafile (tar.xz archive)
C : 270895 C ++ : 162799
C ++ 66 % 더 빠름
그래서 대답은 : 그것은 구현의 품질 문제이며, 실제로 플랫폼에 따라 다릅니다.
벤치마킹에 관심이있는 사람들을위한 전체 코드 :
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <sys/time.h>
template <typename Func>
double benchmark(Func f, size_t iterations)
{
f();
timeval a, b;
gettimeofday(&a, 0);
for (; iterations --> 0;)
{
f();
}
gettimeofday(&b, 0);
return (b.tv_sec * (unsigned int)1e6 + b.tv_usec) -
(a.tv_sec * (unsigned int)1e6 + a.tv_usec);
}
struct CRead
{
CRead(char const* filename): _filename(filename) {}
void operator()() {
FILE* file = fopen(_filename, "r");
int count = 0;
while ( fscanf(file,"%s", _buffer) == 1 ) { ++count; }
fclose(file);
}
char const* _filename;
char _buffer[1024];
};
struct CppRead
{
CppRead(char const* filename): _filename(filename), _buffer() {}
enum { BufferSize = 16184 };
void operator()() {
std::ifstream file(_filename, std::ifstream::in);
// comment to remove extended buffer
file.rdbuf()->pubsetbuf(_buffer, BufferSize);
int count = 0;
std::string s;
while ( file >> s ) { ++count; }
}
char const* _filename;
char _buffer[BufferSize];
};
int main(int argc, char* argv[])
{
size_t iterations = 1;
if (argc > 1) { iterations = atoi(argv[1]); }
char const* oldLocale = setlocale(LC_ALL,"C");
if (strcmp(oldLocale, "C") != 0) {
std::cout << "Replaced old locale '" << oldLocale << "' by 'C'\n";
}
char const* filename = "largefile.txt";
CRead cread(filename);
CppRead cppread(filename);
// comment to use the default setting
bool oldSyncSetting = std::ios_base::sync_with_stdio(false);
double ctime = benchmark(cread, iterations);
double cpptime = benchmark(cppread, iterations);
// comment if oldSyncSetting's declaration is commented
std::ios_base::sync_with_stdio(oldSyncSetting);
std::cout << "C : " << ctime << "\n"
"C++: " << cpptime << "\n";
return 0;
}
두 가지 추가 개선 사항 :
std::cin.tie(nullptr);
입력 / 출력이 많기 전에 문제가 발생 합니다.
http://en.cppreference.com/w/cpp/io/cin 인용 :
std :: cin이 생성되면 std :: cin.tie ()는 & std :: cout을 반환하고 마찬가지로 std :: wcin.tie ()는 & std :: wcout을 반환합니다. 이는 std :: cin에 대한 형식화 된 입력 작업이 출력을 위해 보류중인 문자가있는 경우 std :: cout.flush ()를 강제로 호출 함을 의미합니다.
You can avoid flushing the buffer by untying std::cin
from std::cout
. This is relevant with multiple mixed calls to std::cin
and std::cout
. Note that calling std::cin.tie(std::nullptr);
makes the program unsuitable to run interactively by user, since output may be delayed.
Relevant benchmark:
File test1.cpp
:
#include <iostream>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
int i;
while(cin >> i)
cout << i << '\n';
}
File test2.cpp
:
#include <iostream>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int i;
while(cin >> i)
cout << i << '\n';
cout.flush();
}
Both compiled by g++ -O2 -std=c++11
. Compiler version: g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
(yeah, I know, pretty old).
Benchmark results:
work@mg-K54C ~ $ time ./test1 < test.in > test1.in
real 0m3.140s
user 0m0.581s
sys 0m2.560s
work@mg-K54C ~ $ time ./test2 < test.in > test2.in
real 0m0.234s
user 0m0.234s
sys 0m0.000s
(test.in
consists of 1179648 lines each consisting only of a single 5
. It’s 2.4 MB, so sorry for not posting it here.).
I remember solving an algorithmic task where the online judge kept refusing my program without cin.tie(nullptr)
but was accepting it with cin.tie(nullptr)
or printf
/scanf
instead of cin
/cout
.
Use '\n'
instead of std::endl
.
Quoting http://en.cppreference.com/w/cpp/io/manip/endl :
Inserts a newline character into the output sequence os and flushes it as if by calling os.put(os.widen('\n')) followed by os.flush().
You can avoid flushing the bufer by printing '\n'
instead of endl
.
Relevant benchmark:
File test1.cpp
:
#include <iostream>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
for(int i = 0; i < 1179648; ++i)
cout << i << endl;
}
File test2.cpp
:
#include <iostream>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
for(int i = 0; i < 1179648; ++i)
cout << i << '\n';
}
Both compiled as above.
Benchmark results:
work@mg-K54C ~ $ time ./test1 > test1.in
real 0m2.946s
user 0m0.404s
sys 0m2.543s
work@mg-K54C ~ $ time ./test2 > test2.in
real 0m0.156s
user 0m0.135s
sys 0m0.020s
Interesting you say C programmers prefer printf when writing C++ as I see a lot of code that is C other than using cout
and iostream
to write the output.
Uses can often get better performance by using filebuf
directly (Scott Meyers mentioned this in Effective STL) but there is relatively little documentation in using filebuf direct and most developers prefer std::getline
which is simpler most of the time.
With regards to locale, if you create facets you will often get better performance by creating a locale once with all your facets, keeping it stored, and imbuing it into each stream you use.
I did see another topic on this here recently, so this is close to being a duplicate.
참고URL : https://stackoverflow.com/questions/5166263/how-to-get-iostream-to-perform-better
'Development Tip' 카테고리의 다른 글
UITextView에 줄 바꿈 추가 (0) | 2020.11.29 |
---|---|
Hibernate 객체를 직렬화 할 때 발생하는 이상한 Jackson 예외 (0) | 2020.11.29 |
X-Frame-Options : firefox 및 chrome의 ALLOW-FROM (0) | 2020.11.28 |
.pem 파일을 읽고 개인 및 공개 키를 얻는 방법 (0) | 2020.11.28 |
Heroku에 로컬 MySQL 데이터베이스를 배포하는 방법 (0) | 2020.11.28 |