C++プログラムで特定の関数がLinuxで実行するのにかかる時間を調べたい。その後、速度の比較を行います。私はいくつかの時間関数を見ましたが、ブーストからこれで終わりました。クロノ:
process_user_cpu_clock, captures user-CPU time spent by the current process
さて、上記の関数を使用するかどうかは明確ではありませんが、その関数にCPUが費やした唯一の時間を取得できますか?
第二に、上記の関数を使用する例が見つかりませんでした。上記の機能の使用方法を教えてください。
追伸:現在、私はstd::chrono::system_clock::now()
を使用して時間を秒単位で取得していますが、毎回CPU負荷が異なるため、結果が異なります。
C++ 11のメソッドは非常に使いやすいです。 std::chrono::high_resolution_clock
ヘッダーから<chrono>
を使用する必要があります。
次のように使用します。
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
void function()
{
long long number = 0;
for( long long i = 0; i != 2000000; ++i )
{
number += 5;
}
}
int main()
{
high_resolution_clock::time_point t1 = high_resolution_clock::now();
function();
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<microseconds>( t2 - t1 ).count();
cout << duration;
return 0;
}
これにより、関数の継続時間が測定されます。
注:マシンのCPUは、コンピューターで実行されている他のプロセスによって使用されることが少なくなるため、常に同じ出力を取得する必要はありません。数学の演習を解くと、あなたの心は多かれ少なかれ集中することができるので、あなたは異なる時間にそれを解きます。人間の頭の中では、数学の問題の解決策を思い出すことができますが、コンピューターにとっては同じプロセスは常に新しいものなので、私が言ったように、常に同じ結果を得る必要はありません!
引数として渡された関数の実行時間を測定する関数は次のとおりです。
#include <chrono>
#include <utility>
typedef std::chrono::high_resolution_clock::time_point TimeVar;
#define duration(a) std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
#define timeNow() std::chrono::high_resolution_clock::now()
template<typename F, typename... Args>
double funcTime(F func, Args&&... args){
TimeVar t1=timeNow();
func(std::forward<Args>(args)...);
return duration(timeNow()-t1);
}
使用例:
#include <iostream>
#include <algorithm>
typedef std::string String;
//first test function doing something
int countCharInString(String s, char delim){
int count=0;
String::size_type pos = s.find_first_of(delim);
while ((pos = s.find_first_of(delim, pos)) != String::npos){
count++;pos++;
}
return count;
}
//second test function doing the same thing in different way
int countWithAlgorithm(String s, char delim){
return std::count(s.begin(),s.end(),delim);
}
int main(){
std::cout<<"norm: "<<funcTime(countCharInString,"precision=10",'=')<<"\n";
std::cout<<"algo: "<<funcTime(countWithAlgorithm,"precision=10",'=');
return 0;
}
出力:
norm: 15555
algo: 2976
実行された関数の実行時間を見つける簡単なプログラム。
#include <iostream>
#include <ctime> // time_t
#include <cstdio>
void function()
{
for(long int i=0;i<1000000000;i++)
{
// do nothing
}
}
int main()
{
time_t begin,end; // time_t is a datatype to store time values.
time (&begin); // note time before execution
function();
time (&end); // note time after execution
double difference = difftime (end,begin);
printf ("time taken for function() %.2lf seconds.\n", difference );
return 0;
}
Scott Meyersの本で、関数の実行時間の測定に使用できる汎用の一般的なラムダ式の例を見つけました。 (C++ 14)
auto timeFuncInvocation =
[](auto&& func, auto&&... params) {
// get time before function invocation
const auto& start = high_resolution_clock::now();
// function invocation using perfect forwarding
std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
// get time after function invocation
const auto& stop = high_resolution_clock::now();
return stop - start;
};
問題は、1回の実行のみを測定するため、結果が大きく異なる可能性があることです。信頼できる結果を得るには、多数の実行を測定する必要があります。 code :: dive 2015カンファレンスでのAndrei Alexandrescu講演によると-Fast Code Iの作成:
測定時間:tm = t + tq + tn + to
ここで:
tm-測定(観測)時間
t-関心のある実際の時間
tq-量子化ノイズによって追加された時間
tn-さまざまなノイズ源によって追加された時間
to-オーバーヘッド時間(測定、ループ、関数の呼び出し)
講義の後半で彼が言ったことによると、結果としてこの最小限の実行回数を取る必要があります。彼がその理由を説明している講義をご覧になることをお勧めします。
また、Googleの非常に優れたライブラリがあります- https://github.com/google/benchmark 。このライブラリは非常に使いやすく、強力です。このライブラリを実際に使用しているYouTubeで、チャンドラーキャルースの講義をチェックできます。たとえば、CppCon 2017:Chandler Carruth“ Going Nowhere Faster”;
使用例:
#include <iostream>
#include <chrono>
#include <vector>
auto timeFuncInvocation =
[](auto&& func, auto&&... params) {
// get time before function invocation
const auto& start = high_resolution_clock::now();
// function invocation using perfect forwarding
for(auto i = 0; i < 100000/*largeNumber*/; ++i) {
std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
}
// get time after function invocation
const auto& stop = high_resolution_clock::now();
return (stop - start)/100000/*largeNumber*/;
};
void f(std::vector<int>& vec) {
vec.Push_back(1);
}
void f2(std::vector<int>& vec) {
vec.emplace_back(1);
}
int main()
{
std::vector<int> vec;
std::vector<int> vec2;
std::cout << timeFuncInvocation(f, vec).count() << std::endl;
std::cout << timeFuncInvocation(f2, vec2).count() << std::endl;
std::vector<int> vec3;
vec3.reserve(100000);
std::vector<int> vec4;
vec4.reserve(100000);
std::cout << timeFuncInvocation(f, vec3).count() << std::endl;
std::cout << timeFuncInvocation(f2, vec4).count() << std::endl;
return 0;
}
編集:もちろん、コンパイラーが何かを最適化できるかどうかを常に覚えておく必要があります。そのような場合には、perfなどのツールが役立ちます。
古いC++またはCの簡単な方法:
#include <time.h> // includes clock_t and CLOCKS_PER_SEC
int main() {
clock_t start, end;
start = clock();
// ...code to measure...
end = clock();
double duration_sec = double(end-start)/CLOCKS_PER_SEC;
return 0;
}
秒単位のタイミング精度は1.0/CLOCKS_PER_SEC
です
- C++ 11のメソッドは非常に使いやすいです。
- ヘッダーからstd :: chrono :: high_resolution_clockを使用できます
- メソッドの実行時間を読みやすい形式で出力するメソッドを書くことができます。
たとえば、1〜1億のすべての素数を検索するには、約1分40秒かかります。したがって、実行時間は次のように出力されます。
Execution Time: 1 Minutes, 40 Seconds, 715 MicroSeconds, 715000 NanoSeconds
コードは次のとおりです。
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
typedef high_resolution_clock Clock;
typedef Clock::time_point ClockTime;
void findPrime(long n, string file);
void printExecutionTime(ClockTime start_time, ClockTime end_time);
int main()
{
long n = long(1E+8); // N = 100 million
ClockTime start_time = Clock::now();
// Write all the prime numbers from 1 to N to the file "prime.txt"
findPrime(n, "C:\\prime.txt");
ClockTime end_time = Clock::now();
printExecutionTime(start_time, end_time);
}
void printExecutionTime(ClockTime start_time, ClockTime end_time)
{
auto execution_time_ns = duration_cast<nanoseconds>(end_time - start_time).count();
auto execution_time_ms = duration_cast<microseconds>(end_time - start_time).count();
auto execution_time_sec = duration_cast<seconds>(end_time - start_time).count();
auto execution_time_min = duration_cast<minutes>(end_time - start_time).count();
auto execution_time_hour = duration_cast<hours>(end_time - start_time).count();
cout << "\nExecution Time: ";
if(execution_time_hour > 0)
cout << "" << execution_time_hour << " Hours, ";
if(execution_time_min > 0)
cout << "" << execution_time_min % 60 << " Minutes, ";
if(execution_time_sec > 0)
cout << "" << execution_time_sec % 60 << " Seconds, ";
if(execution_time_ms > 0)
cout << "" << execution_time_ms % long(1E+3) << " MicroSeconds, ";
if(execution_time_ns > 0)
cout << "" << execution_time_ns % long(1E+6) << " NanoSeconds, ";
}
以下は、関数またはコードブロックの経過時間を測定するための優れたヘッダーのみのクラステンプレートです。
#ifndef EXECUTION_TIMER_H
#define EXECUTION_TIMER_H
template<class Resolution = std::chrono::milliseconds>
class ExecutionTimer {
public:
using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock>;
private:
const Clock::time_point mStart = Clock::now();
public:
ExecutionTimer() = default;
~ExecutionTimer() {
const auto end = Clock::now();
std::ostringstream strStream;
strStream << "Destructor Elapsed: "
<< std::chrono::duration_cast<Resolution>( end - mStart ).count()
<< std::endl;
std::cout << strStream.str() << std::endl;
}
inline void stop() {
const auto end = Clock::now();
std::ostringstream strStream;
strStream << "Stop Elapsed: "
<< std::chrono::duration_cast<Resolution>(end - mStart).count()
<< std::endl;
std::cout << strStream.str() << std::endl;
}
}; // ExecutionTimer
#endif // EXECUTION_TIMER_H
以下にその使用例を示します。
int main() {
{ // empty scope to display ExecutionTimer's destructor's message
// displayed in milliseconds
ExecutionTimer<std::chrono::milliseconds> timer;
// function or code block here
timer.stop();
}
{ // same as above
ExecutionTimer<std::chrono::microseconds> timer;
// code block here...
timer.stop();
}
{ // same as above
ExecutionTimer<std::chrono::nanoseconds> timer;
// code block here...
timer.stop();
}
{ // same as above
ExecutionTimer<std::chrono::seconds> timer;
// code block here...
timer.stop();
}
return 0;
}
クラスはテンプレートであるため、時間をどのように測定および表示するかを簡単に指定できます。これは、ベンチマーキングを行うための非常に便利なユーティリティクラステンプレートであり、非常に使いやすいです。
steady_clock
とは異なり、単調であることが保証されているhigh_resolution_clock
を使用することをお勧めします。
#include <iostream>
#include <chrono>
using namespace std;
unsigned int stopwatch()
{
static auto start_time = chrono::steady_clock::now();
auto end_time = chrono::steady_clock::now();
auto delta = chrono::duration_cast<chrono::microseconds>(end_time - start_time);
start_time = end_time;
return delta.count();
}
int main() {
stopwatch(); //Start stopwatch
std::cout << "Hello World!\n";
cout << stopwatch() << endl; //Time to execute last line
for (int i=0; i<1000000; i++)
string s = "ASDFAD";
cout << stopwatch() << endl; //Time to execute for loop
}
出力:
Hello World!
62
163514