C++相談室 part136
レス数が1000を超えています。これ以上書き込みはできません。
次スレを立てる時は本文の1行目に以下を追加して下さい。
!extend:on:vvvvv:1000:512
C++に関する質問やら話題やらはこちらへどうぞ。
ただし質問の前にはFAQに一通り目を通してください。
IDE (VC++など)などの使い方の質問はその開発環境のスレにお願いします。
前スレ
C++相談室 part135
https://mevius.5ch.net/test/read.cgi/tech/1522495206/
このスレもよろしくね。
【初心者歓迎】C/C++室 Ver.102【環境依存OK】
http://mevius.5ch.net/test/read.cgi/tech/1509780815/
■長いソースを貼るときはここへ。■
http://codepad.org/
https://ideone.com/
[C++ FAQ]
https://isocpp.org/wiki/faq/
http://www.bohyoh.com/CandCPP/FAQ/ (日本語)
----- テンプレ ここまで -----
VIPQ2_EXTDAT: default:vvvvv:1000:512:----: EXT was configured うん、regexよりはspirit::qiだよね
ワッチョイ a781-UVFsは的外れにも程がある
そしてそれ以前にregexもspiritもC++初心者に勧めるようなものではない 要件が全部出ていたわけじゃないのに
エスパーが登場して的当てたのか?そりゃスゴイな >>743の時点で低レベルな記述が適していることくらいわかるだろ
15GBのテキストだぞ 遅いから早くしたい
どんなに頑張っても無理なら
遅くてもよい仕組みにするという手もある そもそも最初のフォーマットと全然違うやんけ
普通にfgetsでとって、必要最低限の位置をマーキングするほうが
メモリに入れるのはそれだけ
位置からメモリブロックをとって、>>834>>835みたいなやりかたで表示が必要になったときに解析 そもそも解析アプリが出力してる規定されてるフォーマットなのに
いちいち形式をチェックする必要すらない
ホントな知恵遅れはなにをいってんのか意味不明だからな
クソニートのエアプログラマがテキトーなことばっかりいってんのは分かる スケジュールを開始するボタンに名前をつけたいけどいい名前が思い浮かばない
できれば6文字以内で
開始とか実行はダメ ちなみな
fgets()は知恵遅れのキミラが考えてるよりぜんぜん速い
このスレの知恵遅れが書くようなクソコードより全然速い
書式付の標準関数は書式解析のオーバーヘッドがあるからクソ遅い ちなみにな手で入力したときは
fscanfやsscanfは使えない
書式より引数が少ない場合簡単に死ぬからな
手で入力してるデータの場合fgets()でデータとって丹念に解析するしかない
つまりfscanfやsscanfの使用も想定する=間違いなく妥当な形式の入力がある
ことを意味する 同じ事を何度も書かなくて良い
>>758
あとfgetsよりfreadの方が速い
当たり前ですが メモリブロックとるときは
普通にfreadでいい
どうせ改行位置は解析しないといけないから
fgetsにやらせとけばいい
知恵遅れはTPOにあった関数の使い方がわかってないからな 知恵遅れはなにをどういった場面で使うのか分かってないからな
そもそも話がかみあうワケがない
知恵遅れにありがち
電車みて電車の型番いえるだけみたいな頭悪いのが
このスレにはウヨウヨいる わざわざ改行検索だけの為にメモリスキャン
ガチガチにチューニングするつもりならあり得ないですね 適度なチューニングで妥協するんであれば
fgetsで良いかも知れませんが freadで読む時間とほぼ同じ時間で解析まで終わるので
中途半端な解析で止める必要もなくて
直接使いやすい形にすれば良い
あとは>>885 >>889
なるほどですね。
解析入れてもあまり変わらないのか。
scanfって本当に遅いんですね、嬉しい誤算です。
話題に出ているregexですが、↓ここを見た感じ、
ttps://www.sejuku.net/blog/25962
下記のように感じました。
perlやpythonの正規表現と似た感じなので使えるかもしれません。
1文字づつシフトしてスペースや改行を判定しながら抽出するより早いのであれば試して見ようと思います。
regcomp →正規表現オブジェクト?の作成。()でグループ化。読み込み前にすべてのフォーマットパターンを生成して使い回す。
regexec →正規表現オブジェクトを使ってパターンマッチ
rm_so、rm_eo →マッチの先頭、終端が取れるので文字列が拾える。こんな感じ?→strncpy(&str, data+match[i].rm_so, match[i].rm_eo - match[i].rm_so);
regfree →読み込み終了後にすべてのオブジェクトをこれで開放すればいい?
spirit::qiはググりましたがまったくわかりませんでした。。 15GBともなると
関数を呼ぶ時間でもトータル時間に影響するんでね
fgetsの関数コール回数だってバカにならんでしょ
全てのfloatをvectorにpush_backするだけでも
結構な時間ですよ
この辺も工夫しないと 正規表現なんか使ったらコンパイル済の正規表現でも
クソ遅いにきまってるやんけ
そんなもん使うならスクリプトでやったほうがいい >>922
だからregexもspiritも使わなくていいってばw
すでに実例出してくれてるような昔ながらのC言語的な書き方でいいと思う
まともな実例も示さずに「○○使え」は無視していいよ ちなみに gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0 だと、 C++がちゃんとしてて、std::getline()はfread()と同じくらい速い。
各自で実際に試してみるとよいだろう。
Visual Studio だとstd::getline()はfgets()よりも遅い。
コンパイラによって性能に極端な差がでるのがC++のiostream周り。iostreamが地雷扱いされる主因。 15GB なんて、もし画像ファイルなら、触った途端に、メモリ不足でフリーズするレベル。
普通、8GB ぐらいしか、メモリを積んでいないだろ
1行毎に、読んでは捨てる方式じゃないと無理。
それか、ファイル分割する
Ruby で、HTML, Node.js などが良さそう。
それか、DB >>871のフォーマットでC++で作ったら
解析15GBで8.6秒
1バイト平均2クロック!
これを越えるには
マルチスレッド / AVX命令 /アセンブラ / GPU
に手を出さないと無理かな
----
Haswell
3.4GHz固定
シングルスレッド
C++で1文字ずつ15G文字解析
普通の命令のみ使用(SIMD命令は使用しない)
数値の合計のみ計算して結果を最後に表示
固定4KBを繰り返し解析、トータル15GB分の時間を計測
---- 1ブロック内に数値が数万あってどうUIで表示すんのか気になる 数値ならグラフにするとか画像にするとかフィルターを通してから間引くとか音声にして鳴らすとか
まあ色々とあると思う >>926
おとなしく1文字づつ評価します。
>>933
8.6秒早いですね。
自分はまず初バイナリ読み込みなので勉強から始めてとりあえず1行読みができましたが、
アスキー読み込みのfgetsの方が早かったです、、
コードを載せるのでどこが悪いか見てもらって良いですか?
対象=13GBのファイル(約8億行)。
○fgets版 →約17秒
if((fp=fopen(file_path,"r"))==NULL){
printf("file not open %s\n", file_path);
return 1;
}
while( fgets(buf,MAX,fp) != NULL ){
buf;
}
次にバイナリ読み↓ ○バイナリ読み版 →約44秒
struct CCC {
FILE *fp ;
bool read(char* file_path);
t_read_db read_db;
};
bool CCC::read(char file_path[]){
FILE *fp;
if((fp=fopen(file_path, "rb"))==NULL){ printf("ファイルを開けません。%s",file_path); return 0; }
unsigned char buf[BUF_SIZE];
int newline_index;
while( !feof( fp ) ){
size_t size = fread( &buf, sizeof(buf[0]), sizeof(buf), fp );
// 終端処理。最大値で取得されてなければそこを末尾にする
if( size != BUF_SIZE ) buf[size] = '\x00';
// 取得bufの最後尾が改行でなければfpを改行まで戻す
if( buf[size-1] != '\x0a' ){
newline_index = -2;
for(; buf[size+newline_index] != '\x0a'; --newline_index);
fseek(fp,newline_index+1,SEEK_CUR);
// bufの最後の改行の次にx00(null)を入れてそれ以降をカット
// 「配列参照はポインタの移動より遅い」とあったがbufは実体で移動できないので[]参照で代入。
buf[size+newline_index+1] = '\x00';
}
unsigned char const* c_buf = buf;
while( c_buf[0] != '\x00' ){
print_line(c_buf, &c_buf);
}
}
} bool print_line(unsigned char const* p_buf, unsigned char const** pct_next);
bool print_line(unsigned char const* p_buf, unsigned char const** pct_next){
unsigned char const* c_buf = p_buf;
//改行位置を検索
for(; *c_buf != '\x0a'; ++c_buf);
char line[LINE_SIZE];
strncpy( line, (char const*)p_buf, c_buf - p_buf );
// nullで区切らないと過去に代入した文字数より少ないときにゴミが残る
line[c_buf - p_buf] = '\x00';
++c_buf;
*pct_next = c_buf;
return true;
}; ○fgets版 →約17秒
○バイナリ読み版 →約44秒
両方ともとりあえず文字列の読み取りまでしていて、条件は同じではないかと思うのですが、freadのほうが倍以上遅いです。。 >>935
fgetsで悩んでる人がそのハードル超えることできるか心配してるんですよ すみません、44秒はfreadの読み込みサイズ(BUF_SIZE)が512byteでした。
16MBにすると34秒になりましたが、それでも倍の差があります。 >>942
mmapがいるかなぁ
BSD grep のソースのコメントにこの辺りの高速化の
コツが書いてあるので一読されたし >>943
mmapですか。
要チェックですかね。
でもそれだけで数倍早くなるとも思えないし、 >>933 さんの8.6秒は圧倒的パフォーマンスですね。
シングルスレッドで特殊なものは使ってないようだし、たった4KBの繰り返しだし。
根本的なところから違いそう。。
>>933
もし可能であれば、テストしたコードを見せていただくことはできませんでしょうか? あのな
そこまで読みこみ速度を気にするなら
そもそもFILEポインタ使う関数なんか使うなよ
そもそもFILEポインタ使う関数はバッファリングしてるから
いちいちメモリコピーしてんのに
そこまでガタガタいうなら
openとreadで普通にメモリブロック読みこむ処理にしろよ
ハゲ >>940,941
setvbufも初耳です。
941さんのコメントからすると、これまた難しそう。。
色々と情報ありがとうございます。 ちなみになFILEポインタは構造体にファイルデスクリプタもってる
fopenでopenを呼び出してファイルディスクリプタ生成して構造体に保存してる
ファイル読むときはファイルディスクリプタでread使ってバッファリングしながら読みこんでる
このスレの低学歴どもはこういう基本的なことわかってんの そのsetvbufというのが
バッファリングするバッファのサイズだ
つまり、バッファにたまったメモリをひたすらコピーしてる 32bit越えるmmapとか
そんなやばそうなもん使うのか
まずちゃんと動作するか確認することになるわ 休日にオレのエレガントなファイル読みこみ処理作ってやるから
楽しみにしてなさい HDDの一番外側15GB
セクタ直読みで60.1秒
平均 250MB/s でした
7200rpmの8TBのHDDです >>851の77.9秒はHDDの内側の方でfreadでの読み込み
どちらもHDDの限界と思います >>944
解析時間のほとんどが>>782のようなコードです
ちょっと変えましたが 改行を探す為だけにスキャンする必要はありませんし、コピーする必要もありません
ほとんどをしめる数値の行は
>>782の処理で区切りまでポインタが進みます >>951
ありがとうございます。楽しみにしています。
>>933,952,953
解析と読み込みを分けているんですね、8.6秒は解析で、読み込みがHDDのハード限界の60秒(高速な外側)、77.9秒(低速な内側)。
そこで >>851 の読み込み、解析合わせると78.1秒でほとんどがHDD律速ということか。
>>954,955
ご説明ありがとうございます。 >>782 の動作を調べてみます。
freadの扱いで質問なのですが、byte単位で取得すると最後尾が改行ではなく途中で終わることがあるので、
改行区切りになっているdata変数がだと思って、937に書いたコードでは、自分がわかる知識で考えて、
freadで読み込んだあとに改行のところまでfseekで戻しているんですが、この考え方はあっているのでしょうか? data変数がだと思って →×
data変数が必要だと思って →○ >>952
すみません、あと、 >>936 に書いているfgets版のコードだとどれくらいの速度が出ていますでしょうか?
fgetsで回したものと fread + >>782 のコードでどれくらいの比率になるのか気になりました。 MS製の金毘羅でfgets(), fgetc()を試す場合は、_CRT_DISABLE_PERFCRIT_LOCKS 必須で。 無能な働き者が書いた半角スクリプトなんて誰も走らせたくない >>956
読み込みの境目の処理方法はいくつかあります
A. 1行全体が連続してバッファに存在しなくてもいい作りにする
B. リングバッファ
C. fseekでファイルポインタを戻してから読み込む
D. あまりをmemcpyでバッファの先頭にコピーしてから読み込む
E. ほか
読み込み単位が大きければ C. or D. のコストは無視出来るので C. D. で
読みこみ単位が小さければ A. B. なども考える
といった感じかと思います
今回私は解析処理が簡単に作れて、HDDの読み込みに一切影響を与えない、D.で作りました >>958
fread
fgets
ReadFile
読み込みだけだとどれも78秒 >>959
ttp://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-8.1.0/gcc-8.1.0.tar.xz
これを使ってるので、まずはMSのオプションは関係なさそうです。
でもMSのコンパイラは早いという記事をこの前読んだのでそっちの方に乗り換えた方がいいのかな。
完成した後に両方テストして良好な方を検討しようと思います。
>>962,963
A,Bは難易度高そうですね。
Cは自分がやった手法のようですが、Dはポインタを戻す分の再取得が無いのでCより良さそうに見える。
Dを検討してみます。
fgetsもfreadも同じ時間ですか。
となるとfgets 13GB 17秒だから、そのレベルの速度は出るはずなのか。
今782のコードを調べて動作がわかってきました。
char演算で文字コードを数字の始点に移動させ9以下で数字以外(スペースor改行)を判定。
*10で桁を表現し、文字コードの番号で演算。(文字→数字変換していない!!)
このファイルで支配的な数字行が文字→数字変換が一度もないおかげですごく効きそう。
原理がわかるとなるほど!となるけど、1桁ずつ演算、文字コード演算は自分がやってたら絶対にたどり着かない発想です。 text/byte stream は異なる。
文字列はバイトじゃなく、テキスト
バイトは構造体など、構造が決まっているもの
改行区切りで、改行の位置が変わるものは、テキスト処理 通信データなどは、可変長構造体
ヘッダーなどは固定サイズで、データ部分は可変サイズ。
可変部分のサイズが、ヘッダーなどに書いてある >>969
C99なら規格化されたのでgcc拡張は要らなくなった C99の可変長配列はC++には取り入れられなかったし、
C11ではオプションに格下げされたんじゃなかったっけ?
年式でフラフラする機能の典型例のような記憶がある。 >>971
可変長配列(VLA)については、そのとおり
しかし >>970 >>969 で指しているのは、構造体内の 0 長配列メンバ、または C FAQ 2.6 >>971
VLA のことと誤解してないか?
ここで言ってる「可変長構造体」は構造体の最後の要素が不完全型であることを許すルールのことだと思う。
struct a {
int a;
int b[];
};
このとき sizeof(struct a) は要素 b を含まない大きさを返す。
malloc(sizeof(struct a)+(bの大きさとして確保したい大きさ)) とすれば b が可変長な構造体として使える。
このルールが出来る前 (C89) は仕様に沿ってこのようなことをしようと思うと
配列 b の大きさを便宜上 1 として指定しておく、
つまりは
struct a {
int a;
int b[1];
};
としておいて malloc のサイズ指定のときに要素一個分の大きさを差し引いて調整するようなことをしていた。 すまん、皆の言う通りだ。ちゃんと読めば
構造体の末尾メンバの0サイズ配列の話だと分かる流れだったのに、
なんでか裸の配列の要素数だと思いこんでしまった。 GCC拡張をしたとして
struct a {
int b[0];
};
のsizeof(a)はいくつなんじゃろうか… >>977
ゼロが返ってきたぞ。
ただし、 C/C++ におけるオブジェクトはメモリの一部を占有するものでありポインタで示せるものという要求があるので、
それと矛盾なく使うには色々と気を付けないといけないかもしれない。
ちなみに、クラスのメンバにストレージを割り当てないこと (要するにサイズゼロのオブジェクト) を許す [[no_unique_address]] という属性が C++20 で追加されてる。 そんなクソみたいな使い方しかされない機能入れるなよ。。 複数回allocateを抑制するための機能(関数)はc++にもたくさんあるじゃない >>979
非staticなメンバを一つも持たない上、そのクラスのポインタを扱わない(=多態性を必要としない)のに
継承したりメンバとして持ったときに1バイト追加されるのがうざいからだろ >>978
ええー!!!
struct c {
};
のsizeof(c)は1だった(と思った!)が!! 最適なプログラムを書く必要のない立場の雑魚は黙ってろよw >>981
static なメンバしかない、しかもポリモルフィズムしない(interfaceな使い方でもない)
ってそもそも継承するのが設計として間違いじゃねーの? c++つかってるとロジックやアルゴリズムなどの本来の目的より
こういう横道にそれた話題ばかりになることが多い
手段が目的化するので困る 関心の集中の先はプログラムで実現できることになるべきだけど
c++はそっちより言語自信や仕様や実装に目が向く
時間が無駄に費やされやすい 最適なプログラムを書くのもC++の目的だ
ロジック以外のことに目をそらしたいなら他の言語を使え c++は幅広い書き方ができてしまうのが一番困ったところ
10年前のコードが完全に動いてるにも拘わらずゴミに見えてしまう >>984
非staticなメンバが無い、ってのはメンバ変数ね(非staticなメンバ関数はあっても関係ない、つまりインターフェースも含む linuxと聞いてた(>>743)のになんでMSのコンパイラの話がでてんねんと
まず実機でコレ走らせて試験をしなさい
@ 下のソースをコンパイルしてbaka_testという名前の実行オブジェクトをつくりなさい
linuxなら↓こっち
https://ideone.com/e9iA5m
windowsなら↓こっち
https://ideone.com/D4T1zh
A で、こんな試験をする
https://ideone.com/82BnFZ
ちなみにwindowsはバッファサイズを
セクタバイト数の倍数でないとちゃんと動作しない
それはwindowsの仕様だからな(メモリアドレスはmallocで返ってくる先頭のポインタオフセットで問題ないことは分かってる)
バッファサイズを何バイトにするのが読みこみで効果的か
決めたほうがいい
試験結果をちゃんと報告するようにな
あとOSは64bitでいいんだな
それによっても話がかわってくる ちなみにwindowsでopenとかread使うのはクルクルパーだからな
msのランタイムがウンコなのは有名だからな
msウンコランタイムのopenでCreateFile呼び出してる
msウンコランタイムのfopenでopen呼び出してる
msウンコランタイムのreadでReadFile呼び出してる
msウンコランタイムのfgetsやfreadでread呼び出してる
わかった? >>993
ありがとうございます。
g++ -o baka_test $file_name
echo 512,`./baka_test $input 512` > baka_result.txt
echo 1024,`./baka_test $input 1024` >> baka_result.txt
〜
上記を13GBのファイルで試したら下記結果が出ました。
512,13
1024,7
2048,5
4096,3
8192,2
16384,2
32768,2
65536,2
131072,1
262144,3
524288,2
1048576,2
2097152,2
4194304,2
8388608,2
16777216,3
33554432,2
67108864,3
134217728,3
268435456,3
536870912,3
1073741824,3 これは一度に読み込むサイズは4KBを超えると速度に影響はなく、
>>962 のC,Dパターンの場合だと、最低1行分のバッファが必要なので、
その場合、一行のバイト数のMAX値で決めれば良い。
ということでしょうか? どんだけ速いHDDやねん。。。
すでにキャッシュされてんのか
その13とか7とか5とかいう数字は
ディスク読みこみ(ディスクの内容をバッファにコピー)にかかった時間は秒だぞ
ちゃんとコード書いてmsecにして計測したほうがよかったのかもな
暇だったら、シェルで精度の高い計測値だせるように工夫しなさい
数十秒はかかると思ってたからmsecなんか考えてもなかったわ
で、実機のOSは64ビットでいいのか
ここ結構重要だからな すみません忘れてましたlinux 64bitです。
> 128Kで2秒かからない
これのことですね。 →131072,1
昨日の夜(今日の朝5時)までやって >>962 のCパターンでようやく一通りファイルを
読み込めるようになりましたが、やはり速度が出ていません。
13GBファイルで確認
fgetsのみ解析なし →17秒
freadで各文字列解析、数値行の演算を行ったもの →1分超え
コード書いてて気になる箇所がいくつかあるので質問しようと思ったけどもう1000になりますね。
次スレで質問させてもらいます。 なんかよく分かってもらえてないようだが
この値はブロックで読みこむ固定のバッファリングサイズ
1行分とか関係ない
プログラムではきっと
バッファリングされたメモリを解析することになる
とりあえず今回は、必ず128K単位で読みこむ
128K単位のメモリを
正直もっと増やしてもいいとは思う
あととりあえず行の最大バイト数は256バイトで十分でいいや レス数が1000を超えています。これ以上書き込みはできません。