C++相談室 part136
レス数が950を超えています。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 数値に対応する何か(グラフ?)をクリックすると
その周囲の文字列と数値が表示出来れば良いのかな?
と思った 文字列の解析は速く出来そうだから
あとはUIに凝ってくださいな 数値も文字列も数行ずつかと思ったけど
1000行とかになるのか
数値ブロック、文字列ブロックは
それぞれ平均何行くらい? メモリが大変な事になりそうなのでstringとint_to_floatの行のアドレスだけ取得して、
必要になった時に読みに行ってintからfloatに変換すれば? floatの配列は別に用意して
struct LOG {
uint32_t str_start;
uint32_t str_end;
uint32_t str_line;
uint32_t num_start;
uint32_t num_end;
};
みたいな、
ブロックごとの構造体に入れていく方がいいのかな? vector<pair<size_t, size_t>> baka;//stringの開始位置とint_to_floatの開始位置をペアで管理。 適切なデータの持ち方はUIの全体表示の仕様次第と言うことになる >>806に対して反応が無いから
数値情報が事前に必要なのかと思ったが あと気になったのはGUIに表示するのはstringの一行目? 1ブロック平均500行
1行平均30文字
でも100万ブロックある
全体画面で文字列は表示しないんじゃ? 初回DBに突っ込む
初回レコードのインデックスを作る 最低限のパースを実施して各レコードのオフセットを配列に入れて必要になった時に解析して表示すればいいだけかと >>776
構文的に難しくも無いのにわざわざ遅くする理由がわからん…
ていうかそもそもlexとかyacc使いでも滅多に使わん希ガス
今回の文字列処理の件はワッチョイ 0780-Rmg1の圧勝 >>851
テストまでしていただいだきありがとうございます。
1分ちょっとならとてもいい感じです。
なるほどDRAMよりも高速なCPU内のキャッシュを有効に使うと効果的ってことですね。
そんなこと今まで考えたこともなかったです。勉強になります。
>>854
ご推察の通りです。
>>857
文字列の方はせいぜい10行〜20行です。
数値の方は何万行にもなったりします。
>>858
メモリは10GBつかっても問題にならないくらい潤沢に使えると思うので、まずは速度重視で行こうと思っています。
>>861,862
すみません昨日書き込みエラーになって書き込めなかったのでそのままでした。
クリックするたびに毎回int->float演算したら1項目で何万行もあったりするので
読み込み時に演算しておいた方がよいかなと思っていました。
>つづきます >>863
全部のフォーマットは複雑になってくるので簡易的なフォーマットをと思って出していましたが
説明していくと細かい話になってきましたのでもう少し具体的なものを書きます。
テストまでしてもらっているのに変わってすみません。。
-------------------------------
SECTION_NAME @
11200 11200 2 Jun 9 23:23:00 2018 A
This is pen. B
hello world. B
x 1 2 C
100 1 -2000 10
101 10 -2001 10
y 2 4
-100 10000
-101 10100
x 3 28
QQ subname -1 0 0 1 -21000000 600000 2 D
100 100 110 110
100 100 110 110
〜あと26行〜
SECTION_NAME E
@GUI表示する
A11200がx or yの個数、3カラム目がテキスト行の個数、それ以降は捨てる。11200のところは1の時もあれば100万超えることもある
BGUI表示する
Cこれが非常に多く、ひたすら繰り返される。x,yはランダムでくる。
1カラム目はx or y固定。x=次行に来る数値は4カラム。y=2カラム
2カラム目は1からインクリメントしていき、Aの11200回出てくる。カウント数値
3カラム目は次行から始まる数値行が何行かの数値
Dx or yの次の行にたまにこの行があります。この行も使いますが、ちょっと複雑なのでまずはスキップで。
Eここからまた@の繰り返し、要するにファイルの最後はCのところで終わる。
------------------------------- すみません、上記QQはQQという固定文字です。
行頭が"Q"のときはこの行という判定で良いと思います。
>>859,860
格納先は構造体に入れようと思っていて、下記のようにテキスト行も入れようかなと思っていました。
struct x_or_y{
char type; // x or y
int num; // Cの2カラム目
float list;
};
struct elem{
char section_name;
char comments;
array系の何かの型 x_or_y_array; // 配列にしておくと(Cの2カラム目-1)で簡単にアクセスできそう
}; なんかのログなんだろうけど、ログ吐く時に読み込みやすいように出し方考え直した方がいいよ
大本が変えられないならパイプ繋いでフィルタ噛まして、読みやすいように直したファイルを並行して吐くとかさ
というかまず単にSECTION_NAMEごとにファイルぶった切っておくだけで良かったりしない?難しく考えすぎてない? どういうGUIが必要なのかわからんから的外れかもしれないけど
ワイならSECTION_NAMEごとに集計したHTMLファイルかなんかを出力するプログラムをワンパスかけてから
後でそのHTMLをブラウザで見ることを考えたくなるんだけどそれじゃダメなの? >>869
> 今回の文字列処理の件はワッチョイ 0780-Rmg1の圧勝
>>851の圧勝
>>851は0780-Rmg1みたいな遅いコードとは違う >>871
UIの表示内容はどういう感じを考えてる? A
1カラム目と2カラム目は同じ数字?
1カラム目がxの個数、2カラム目がyの個数?
C
y 2 4 の後、データ行が2行だけど4行の間違い?
データ行の値の範囲は? 初めて開くファイルは使いやすいように変換して(キャッシュとして)保存しておいて
次回以降それを使うか
全ファイルバッチ処理で事前に変換しておくか
かな
ファイルを開く度に分オーダーかかるのは使いづらい
どちらが良いかは使い方次第で 15GBオーダーのファイルがすでにいつくかあって
今後も増えるってことで良いんですよね? >>873,874
ソフトが出しているログなので変えられず、並行で吐くこともできないんです…
> ワイならSECTION_NAMEごとに集計したHTMLファイルかなんかを出力するプログラムをワンパスかけてから
これも考えたのですが、元ファイルと加工したファイルが常に等価ではないので、
間違って古いままの加工ファイルを参照する事故を、全員が気にする必要が
出てくるため今は保留とし、どうしてもできない時の最終手段と考えています。
こういうご意見も新しい視点が見えたりするんでありがたいです。
>>877
こんなのをイメージしています。
====================
項目名 | 個数 ※各カラムの説明行
====================
-SECTION1 | 11200
subname1 | 8800 ※subnameがあれば折り畳みツリー形式。なければ折り畳みなし
subname2 | 2400
+SECTION2 | 1
+SECTION3 | 666
====================
This is pen. ※SECTION1を選択するとそれに対応するテキストをここに表示
hello world.
====================
1 2 3 4 5 6 7 ※上と同じくSECTIONに対応した数値を特定できる番号を表示
8 9 10 11 12 13 14 ※ここはHTMLぽいものにし、数字クリックでその数値をprint
....
==================== >>878
> 1カラム目と2カラム目は同じ数字?
2カラム目が何の数字かまだ把握できていません。
とりあえず1カラム目と2カラム目の数値比較を行い、違う場合はエラーにしようと思います。
> y 2 4 の後、データ行が2行だけど4行の間違い?
ご指摘の通り4行の間違いでした。
> データ行の値の範囲は?
intの範囲を超えるか?ということですか?
int(-2147483648〜2147483647)で大丈夫と思います。
>>879
ユニークな名前で重複しません。
>>880
仰る通りどちらも一長一短で、今回は874さんに回答した通り、古いキャッシュを参照する事故を防ぐため、まずはテンポラリファイルを作らないことを考えています。
>>881
ファイルは毎回更新されて新しい15GBのファイル1つをインポートします。
15GBも現在のMAXサイズなのでそのうち20GBとかのものが出てくる可能性はありますが、
そこは「読み込みをひたすら待つ」と割り切りで考えています。
>>882
@の行→100行以下
Aの行→SECTIONごとに1行出るので同じく100行以下
Bの行→SECTIONごとにせいぜい20行程度。(20行*100行=20000行)
Cの行→SECTIONごとにたまに数千万のSECTIONあり(全SECTIONだと数千万〜億超えも出てきそう)
1つのSECTIONで数千万や億になり、それ以外のSECTIONは1000以下だったり、
1千万のSECTIONが複数SECTIONになることもあります。
そしてCに対して数値行が1行〜数十行、場合によっては数百行がくるので、Cとそれにかかる数値行が支配的になると思われます。
あと忘れていましたが、テキスト行は日本語がくることもあります。
それ以外は1byte文字です。 キャッシュせず
変換して保存しないとなると
ほとんど読み込み時間
解析はそれに比べれば速い
なので
もし高速化したければ
全読みしない方法を考えるしか
全読みの時間くらいは待てるっていうならその方が簡単だけど
読み込み15GB 30秒って異様に速いな
うちの8TB 7200rpmのHDD(内側の方) だと78秒
SSD? RAID? キャッシュ? RAMディスク? >>885
HDDだと思います。
サーバ用なので高速なんですかね。
ちなみに cat /proc/cpuinfo でCPUも調べたらXeonで20コア以上ありました。
(ハイパースレッドかもしれませんが)
〜次キャッシュも民生用CPUより多いと予想されます。 >>885
あと、全読みの時間は >>851 を参考にすると2分くらいにはなりそうなので、それくらいなら全然待てます。
fgets, sscanfの時は5分かかっていて、それで我慢するしかないのか、と諦めかけていたので。
評価もscanfもしないでfgets()だけで78秒なんですかね?
大きな違いはCPU、CPU内臓キャッシュ、HDD、メモリあたりと思いますが、それだけで本当に半分以下になるのか少し気になりますね。 とりあえずみなさんのコードを眺めましたが初めて目にする関数だらけで自分のレベルでは全くついていけてないです。
一つ一つの関数がどんな動作をするのか調べていきます。
>>782,833 を見るとcharのポインタをインクリして読むやりかたとか昔ちらっと読んだことある。程度のレベルなので。。
今833-837の動作確認ができたので、まずはこれがどう動いているかと、バイナリ読み込みの手法について調べていきます。 >>887
fgetsじゃなくて
freadで1MBずつ読むだけで78秒
庶民の普通のHDDはこんなもんです
解析入れてもほぼ同じなので
読むのに30秒なら解析入れても30秒で終わるよ
もちろんうまく作ればだけど ここはC++スレだからstd::regex使いなよ
>>833よりは速いし安全だぞ 速いわけが無い
コードを書いて時間を測ってみなさい sscanfが遅くてなんとかしたい
っていう話題なのに
DBだとかhtmlだとかregexだとか
頭がおかしいのが多いな 何度も表示させたいなら、解析結果をDBに格納しておくというのもいい方法だと思うが
事前にできるなら、見かけ上の実行時間も節約できるんだし エアプ野郎が多いからねこのスレ
何の意味があるのかは知らんが DBって要望がチャンと出てくる前の話だろう
サーバーで動かす位しか条件書いて無かったぞ >>892
ループごとにregexオブジェクト作り直して時間測ってりゃ遅くなるわな
あれは使い回すもんだよやり直し うん、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の処理で区切りまでポインタが進みます レス数が950を超えています。1000を超えると書き込みができなくなります。