C++でrustとかD言語みたいなfor文
暇だったので作ってみました。
参考にしたのはこのへん。
C++11/14でrange based forで指定回数Loopする - Okiraku Programming
http://agc004.contest.atcoder.jp/submissions/867163
ベンチマークと動作チェック用のコード。
#include <bits/stdc++.h> using namespace std; class _in { struct my_iterator { int it; const bool rev; explicit constexpr my_iterator(int it_, bool rev = false) : it(it_), rev(rev) {} constexpr int operator*() { return it;} constexpr bool operator != (my_iterator& r) { return it != r.it;} void operator++() { rev ? --it : ++it;} }; const my_iterator i, n; public: explicit constexpr _in(int n) : i(0), n(n) {} explicit constexpr _in(int i, int n) : i(i, n < i), n(n) {} constexpr const my_iterator& begin() { return i;} constexpr const my_iterator& end() { return n;} }; const int MAX_N = 1e7; void loop1() { vector<int> v; for(int i = 0; i < MAX_N; ++i) v.push_back(i); } void loop2() { vector<int> v; for(int i : _in(MAX_N)) v.push_back(i); } void timer(int n) { auto start = chrono::system_clock::now(); if(n == 0) loop1(); else loop2(); auto end = chrono::system_clock::now(); cout << "elapsed: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "msec" << endl; } int main() { cout << "benchmark" << endl; for(int i : _in(2)) timer(i); cout << "check" << endl; for(int i : _in(5)) cout << i << ' '; cout << endl; for(int i : _in(5, 10)) cout << i << ' '; cout << endl; for(int i : _in(10, 5)) cout << i << ' '; cout << endl; return 0; }
結果は
g++ -std=c++11 -Wallだと
benchmark elapsed: 245msec elapsed: 312msec check 0 1 2 3 4 5 6 7 8 9 10 9 8 7 6
- O1をつけると
benchmark elapsed: 97msec elapsed: 92msec check 0 1 2 3 4 5 6 7 8 9 10 9 8 7 6
- O3だと
benchmark elapsed: 100msec elapsed: 76msec check 0 1 2 3 4 5 6 7 8 9 10 9 8 7 6
ということでデフォルトだと遅いものの-O1以降はほぼ変わらないし-O3だとかえって速いという。
どうせ資料とか読んでもよくわからない気がしますが、なんでなんでしょうね。
さりげなく逆順ループも対応してロバストにしてみました。というかそのせいでデフォルトだと遅いんですよね。-O1つければ変わらないとはいえ、なんとなく悔しいです。
要するにコンストラクタの中でテンプレート関数を実体化できればいいと思うんですが、何か方法はないのでしょうか。
また、constexprとかが大量についてるのは最近覚えたものを使いたがる初心者特有のアレです。
ところで、これ作ったのはいいけど使うんだろうか……。
[追記 2017/3/6]
新バージョン
class in_rev { struct It { int it; explicit constexpr It(int it_):it(it_){} int operator*(){return it;} bool operator!=(It& r){return it!=r.it;} void operator++(){--it;} }; const It i,n; public: explicit constexpr in_rev(int i,int n):i(i),n(n){} const It& begin(){return i;} const It& end(){return n;} }; class in { struct It { int it; explicit constexpr It(int it_):it(it_){} int operator*(){return it;} bool operator!=(It& r){return it!=r.it;} void operator++(){++it;} }; const It i,n; public: explicit constexpr in(int n):i(0),n(n){} explicit constexpr in(int i,int n):i(i),n(n){} in_rev rev() { return in_rev(n.it - 1, i.it - 1);}; const It& begin(){return i;} const It& end(){return n;} }; #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e7; const int MID_N = 2000; void loop1() { vector<int> v; for(int i = MID_N; i < MAX_N; ++i) v.push_back(i); } void loop2() { vector<int> v; for(int i : in(MID_N, MAX_N)) v.push_back(i); } void timer(int n) { auto start = chrono::system_clock::now(); if(n == 0) loop1(); else loop2(); auto end = chrono::system_clock::now(); cout << "elapsed: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "msec" << endl; } int main() { cout << "benchmark" << endl; for(int i : in(2)) timer(i); cout << "check" << endl; for(int i : in(5)) cout << i << ' '; cout << endl; for(int i : in(5, 10)) cout << i << ' '; cout << endl; for(int i : in(5, 10).rev()) cout << i << ' '; cout << endl; // for(int i : 10_) cout << i << endl; return 0; }
逆順ループをrustと同じように( ).rev()で書けるようにした。
ベンチは
lib ) ./a.out benchmark elapsed: 31msec elapsed: 30msec check 0 1 2 3 4 5 6 7 8 9 9 8 7 6 5
でまあ変わらないね。
今回の測定環境はi5skylake + Arch Linux + gcc6.2なのでMsys2よりも全然速いけど。