フォルダー上のファイルからデータを読み込む速度について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
wasawasa
記事: 94
登録日時: 10年前

フォルダー上のファイルからデータを読み込む速度について

#1

投稿記事 by wasawasa » 10年前

こんにちは、いつもお世話になっています。
プログラムを作成している途中でint型のデータを記憶する領域を大量に確保しなくてはいけなくなり、これを配列によって賄おうとしたら「配列サイズの合計は 0x7fffffff バイトを超えることはできません。」というエラーが出てきてしまったので、可能であれば変数以外の場所にデータを記憶する場所を用意する事を考えたいと思いました。

そこでお聞きしますが、もしフォルダーの中に空のテキストファイルを用意して記憶場所として使うとしたらデータを読み込む速度はどのくらいになるでしょうか?
また、バイナリデータからデータを読み込む場合はどのくらいの速度になるでしょうか?
あと、調べ物の最中に「SQLite」というデータを記憶するための何か(どんな物かまでは知りませんが)を目にしたのですが、SQLiteからデータを読み込む場合はどのくらいの速度になるのでしょうか?

結構変なことを聞いているような気がしますがどなたかよろしくお願いします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#2

投稿記事 by h2so5 » 10年前

wasawasa さんが書きました: そこでお聞きしますが、もしフォルダーの中に空のテキストファイルを用意して記憶場所として使うとしたらデータを読み込む速度はどのくらいになるでしょうか?
また、バイナリデータからデータを読み込む場合はどのくらいの速度になるでしょうか?
あと、調べ物の最中に「SQLite」というデータを記憶するための何か(どんな物かまでは知りませんが)を目にしたのですが、SQLiteからデータを読み込む場合はどのくらいの速度になるのでしょうか?
ディスクのアクセス速度に依存しますが、少なくともメインメモリへのアクセスよりかなり遅くなります。

なぜそんなに大きなデータを扱うのか、その種類によって適切な解決策は異なると思います。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#3

投稿記事 by softya(ソフト屋) » 10年前

「SQLite」はデータベースです。
大きなデータから絞り込んだ情報を検索して取り出し利用するためのモノですので、大きな情報をメモリに読み込むためのものではありません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#4

投稿記事 by h2so5 » 10年前

SQLiteはインメモリDBとしても動作させることができます。と言ってもプロセスが同じなのでメモリの上限を超えることはできませんが。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#5

投稿記事 by softya(ソフト屋) » 10年前

h2so5 さんが書きました:SQLiteはインメモリDBとしても動作させることができます。と言ってもプロセスが同じなのでメモリの上限を超えることはできませんが。
メモリ動作の場合は検索速度をアップしたり、一時的なサブデータベースの構築などが主な使い方だと思います。
まぁ、wasawasa さんが返答してくれないと目的もわからないのですが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: フォルダー上のファイルからデータを読み込む速度について

#6

投稿記事 by YuO » 10年前

64bit環境で64bit版としてコンパイルすれば,4GiB以上のメモリを利用することはできます。
ただし,staticな変数の2GiB制限は依然として存在する (char c[0x100000000LL];とか平然と通す割りにsizeof(c)が0になる……) ほか,
スタックのサイズも1MiBから変わっていないため,mallocする必要がありますが。

ちなみに,

コード:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

int main(void)
{
    void * p1 = 0;
    size_t size;

    for (size = 1LL << 32;; size += 1LL << 32)
    {
        void * p2;
        printf("%llu\n", size);
        p2 = realloc(p1, size);
        printf("\t%p => %p\n", p1, p2);
        if (p2 == 0)
        {
            free(p1);
            break;
        }
        p1 = p2;
    }

    return 0;
}
なんてコードを24GiBのメモリが載っている環境で実行したところ,12GiB確保後16GiBへのreallocでWindowsの反応が明らかに遅くなりました。
なので,64bit版を使ってもメモリを大量に載せないと意味がなく,スワップの速度も考えるとスワップファイルはSSD/RAID 0のような環境に作りたいところです。
オフトピック
最終的に,実行しているプロセスとVSを落としても反応が遅いままで (大量にスワップアウトしたと思われる),Windowsの再起動となりました。
4GiBのmallocであってもそれなりの時間的なコストがかかるので,なんのために
wasawasa さんが書きました:プログラムを作成している途中でint型のデータを記憶する領域を大量に確保しなくてはいけなくなり、
となったのかを検証した方がよいと思います。
そのint値は実は使い捨てでよかった,という可能性もありますので。

wasawasa
記事: 94
登録日時: 10年前

Re: フォルダー上のファイルからデータを読み込む速度について

#7

投稿記事 by wasawasa » 10年前

皆さん返信ありがとうございます。
>>ディスクのアクセス速度に依存しますが、少なくともメインメモリへのアクセスよりかなり遅くなります。
そうですか、であればファイルの読み込みを頻発させるのはやめた方がいいですね。

>>Yuoさん
うーん、やはりメモリの使用量を減らす方針で考えた方が良さそうですね。

>>「SQLite」はデータベースです。大きなデータから絞り込んだ情報を検索して取り出し利用するためのモノですので、大きな情報をメモリに読み込むためのものではありません。
>>SQLiteはインメモリDBとしても動作させることができます。と言ってもプロセスが同じなのでメモリの上限を超えることはできませんが。
なるほど、読み込みに関しては他のファイルと変わらないという事ですね。

>>なぜそんなに大きなデータを扱うのか、その種類によって適切な解決策は異なると思います。
>>まぁ、wasawasa さんが返答してくれないと目的もわからないのですが。
>>なんのために「プログラムを作成している途中でint型のデータを記憶する領域を大量に確保しなくてはいけなくなり」となったのか
記憶領域を大量に確保する目的は、不特定多数のデータを取り扱うためです。

詳しい経緯をお話ししますと、まず私はマップ型アクションゲーム用のマップをマップチップを使って作成するエディターのようなアプリを作りたいと思いました。
そこでアクションゲームのマップにはどんなデータが必要かを考えた時に、
1.マップにどのマップチップをどういう配置で使用するかというマップデータ
2.移動制限領域などの座標による判定に使用するための判定データ
以上の二つのデータが必要だと考えました。

次にこれらのデータを表す方法として、
マップデータ:マップチップに番号を割り当て、それを元に配列でマップチップの配置を表現する
判定データ:各マップチップに判定データを設定し、設定した判定データとマップ上のマップチップの配置から計算する
以上の方法を考えました。

そしてこれらの表現方法を前提にマップを作成するエディターの基本システムとして、
1.アプリ起動時に特定のフォルダから複数のマップチップを1枚の画像に保存した「オートタイル」「ノーマルタイル」、マップの背景として使用する「パノラマ」、マップの前面にアクセントとして配置する「フォグ」、以上の4種類の画像ファイルを素材として読み込み、それらのファイル名と画像ハンドルを取得する(これらの4種類のファイルはそれぞれ複数存在するとする)
2.「オートタイル」「ノーマルタイル」に対して判定条件をペイントソフトの図形描画のような操作で設定する
3.判定を設定した「オートタイル」「ノーマルタイル」と読み込んだ「パノラマ」「フォグ」データの中から「オートタイル」7種類と「ノーマルタイル」「パノラマ」「フォグ」各1種類からなるグループを「タイルセット」として定義し、「パノラマ」「フォグ」の表示方法に関するパラメータを設定する(同一の素材データを複数のタイルセットで指定できるものとする)
4.「タイルセット」から一つを選択し、そのデータを元にマップを作成する
5.マップのデータ、タイルセットのデータ、オートタイルとノーマルタイルに設定した判定のデータを画像ファイル名と紐付して出力する(マップを1枚ずつ書いて描画するのではなく、ゲーム内で使用するマップを全て作成した後にまとめて出力する)
以上のような仕様を手持ちのRPG用ゲーム作成ツールを見ながら考えました。

以上の方針でアプリを作成する過程で判定データ込の素材ファイルのデータやタイルセットのデータ、マップのデータ等を保存する領域が必要となったので下記のように定義したところ、やはりというべきか999×5×250000の配列となったマップデータを保存する配列で容量オーバーのエラーが発生してしまったという経緯になります。
もちろん、読み込むファイルや作成するマップの上限として設定する数値を少なくすればエラーは出なくなる事は分かっているのですが、そういった方法でエラーを回避しても後で読み込むファイルの種類を増やすなどの変更を加えた時にまた同じようなエラーが発生してしまいますし、上限を減らし続けた結果作成するものが手持ちのツールの下位互換となってしまうようであれば作成する意義自体が無くなってしまうと思ったので上限を減らす以外に何か方法は無いかと模索しているところです。
これを見て代案を提示して頂けるというのであれば幸いです。

コード:

#define FileNameMAX 256//ファイル名の字数上限(このくらいあれば足りるだろう)
#define FileKindMAX 256//種類ごとのファイルを読み込む上限(このくらいあれば略)

#define HanteiLineMAX 3000//1つの画像ファイルに対する判定図形数上限(マップチップ1000種類分の画像に単位マップチップあたり平均3個を最大と仮定)
#define HanteiKindMAX 10//判定の種類上限(四角形、三角形、円形、…etc 後から変更する可能性大)

#define TileSetNumberMAX 999//タイルセットパターン数上限(このくらいあれば略)
#define MapNumberMAX 999//マップ最大数(このくらいあれば略)
#define MapAreaMAX 250000//マップ最大面積(500×500を最大面積として想定)
#define MapLayerMAX 5//マップレイヤー最大数(最低5層は欲しい)

//---ファイル取扱い基本構造
struct FileBasicPal{
	char name[FileNameMAX];//ファイル名[文字列]
	int pic;//画像ハンドル
	int use;//マテリアルとして使用するファイルかどうか(Yes:1 No:0)
};
//---判定生成用座標データ
struct Jpoint{
	int x;
	int y;
};
//---判定データ構造
struct JudgeData{
	struct Jpoint p1;//第一指定座標
	struct Jpoint p2;//第二指定座標
	struct Jpoint p3;//第三指定座標
	int Jkind;//判定形種類(1:四角形 2:直角三角形 3:円)
};
//---マテリアルタイル構造
struct MaterialTilePal{
	char name[FileNameMAX];//ファイル名[文字列]
	int pic;//画像ハンドル
	struct JudgeData hantei[HanteiKindMAX][HanteiLineMAX];//判定線
	int use;//マテリアルとして使用するファイルかどうか(Yes:1 No:0)
};
//---タイルセットフォグ構造
struct TileSetFogPal{
	int fogNo;//使用画像番号
	int sx;//x速度
	int sy;//y速度
	int alpha;//透明度
	int zoom;//拡大率
	int forces;//合成方法
};
//---タイルセットパノラマ構造
struct TileSetPnrPal{
	int pnrNo;//使用画像番号
	int sx;//x速度
	int sy;//y速度
	int zoom;//拡大率
};

//---マテリアルデータ
struct InportData{
	struct MaterialTilePal ATile[FileKindMAX];//オートタイル
	struct MaterialTilePal NTile[FileKindMAX];//ノーマルタイル
	struct FileBasicPal Panorama[FileKindMAX];//パノラマ
	struct FileBasicPal Fog[FileKindMAX];//フォグ
};
//---タイルセットデータ
struct TileSetData{
	int ATileNo[7];//使用オートタイル番号
	int NtileNo;//使用ノーマルタイル番号
	struct TileSetFogPal Fog;//フォグデータ
	struct TileSetPnrPal Panorama;//パノラマデータ
};
//---マップデータ
struct MapData{
	int TileSetNo;//タイルセット番号
	int MapX;//マップx長
	int MapY;//マップy長
	int TilePat[MapLayerMAX][MapAreaMAX];//マップ配列
};

struct MapData MapData[MapNumberMAX];
struct TileSetData TileSetData[TileSetNumberMAX];
struct InportData InportData;

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#8

投稿記事 by h2so5 » 10年前

編集しているマップだけメモリにロードしておけばいいと思います。
999マップも一度に読み込んできたらメモリが足りなくなるのは当たり前です。
マップ面積もレイヤーも最初から最大数確保するのではなくて必要に応じて拡張すればいいのです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#9

投稿記事 by softya(ソフト屋) » 10年前

512MBのメモリでも動くRPGツクールなどは、そんは無茶な読み込み方はしていませんよ。
WOLFRPGエディタもメモリ128MB以上のシステム要件です。

h2so5さんの言われる基本方針でやれば、そんなにメモリが必要にはなりません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

wasawasa
記事: 94
登録日時: 10年前

Re: フォルダー上のファイルからデータを読み込む速度について

#10

投稿記事 by wasawasa » 10年前

返信ありがとうございます。
h2so5さんとsoftya(ソフト屋)さんの返信を見て配列のサイズの変更について調べてみたところ、malloc()とrealloc()という二つの関数を見つけました。これによって
・配列の存在そのものが必要になった時にmalloc()で領域を確保してあらかじめ用意しておいたポインタ変数に代入する
・必要な配列のサイズが拡大縮小した場合にはrealloc()で指定したサイズに変更する(正確には指定したサイズの配列を新たに用意してデータを移し替えた後元の配列を解放する)
という風にすればあらかじめ大量確保する必要が無くなるという事が分ったのですが、ここであらかじめ用意するポインタ変数について疑問に思った事があるので質問します。

仮にx1というポインタ変数を使って1枚目のマップのデータを記録していたとすると、1枚目のマップを保持したまま新たに2枚目のマップを作ろうとした場合は1枚目のマップを記録していたポインタ変数x1とは別のポインタ変数x2をあらかじめ用意する必要があるように思えました。
もしそうであれば、
・マップを制作する最大数nを最初に想定した上でポインタ変数をx1,x2,x3,…,xnという風に用意する必要がある
・最大数nを超える数のマップを作成できない
という事になると思うのですがこの認識で合っているでしょうか?

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#11

投稿記事 by h2so5 » 10年前

2枚目のマップに切り替えるときは1枚目のマップの內容をファイルに書きだした後メモリを解放し、同じポインタ変数に2枚目のマップのメモリを確保して代入すればOKです。
編集中のマップの番号は別に管理すればよいので。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#12

投稿記事 by softya(ソフト屋) » 10年前

>・マップを制作する最大数nを最初に想定した上でポインタ変数をx1,x2,x3,…,xnという風に用意する必要がある

ポインタの配列が使えます。そんなにレイヤー展開しても実用上で意味が無いので上限があっても良いんじゃないでしょうか。
レイヤー展開の話ですよね?
オフトピック
上限が不明な場合は基礎的なアルゴリズム・データ構造ですがリスト構造とかが利用できます。
「ポインタ虎の巻~リスト構造」
http://www.nurs.or.jp/~sug/soft/tora/tora75.htm

基礎的なアルゴリズム・データ構造は勉強されたほうが良いですよ。こんなふうに困った時の引き出しになります。
「C言語講座>アルゴリズム研究室(トップ)」
http://www1.cts.ne.jp/~clab/algorithm/algorithm.html
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

wasawasa
記事: 94
登録日時: 10年前

Re: フォルダー上のファイルからデータを読み込む速度について

#13

投稿記事 by wasawasa » 10年前

返信ありがとうございます。

>>ポインタの配列が使えます。そんなにレイヤー展開しても実用上で意味が無いので上限があっても良いんじゃないでしょうか。
>>レイヤー展開の話ですよね?
いえ、私の認識に誤りが無いか確認しておきたかっただけなので問題を感じたわけでも特定の何かの話という訳でもないです。深読みさせてしまってすみません。

お二人の話を聞いて領域の確保の方針が大分固まってきました。ありがとうございます。
それでmalloc、reallocについて調べたのですが、まだ理解に自信が無いところがあるので確認させてください。

コード:

//x=y=不確定多数 とすると、
int *a;                            //こう宣言するとaにはmallocによってint型の要素数xの1次元配列が用意できて、
int **b;                           //こう宣言するとbにはmallocによってint型の要素数yの配列x個からなる2次元配列が用意でき、
int *c[7];                        //こう宣言するとcにはmallocによってint型の要素数xの1次元配列が7個用意できる。

//配列をmallocによって用意する場合は
a=(int *)malloc(100);               //こう書く事でaに[100]の要素を持つ配列を代入でき、後からaの要素数を10個に変更したい場合は
a=realloc(a,10);                    //このように記述する
b=malloc(sizeof(int*)*5);
for(int i=0;i<5;i++){
   b[i]=malloc(sizeof(int*)*10);
}                                  //こう書く事で[5][10]の二次配列を代入でき、後からbの配列を[10][5]という形に変更したい場合は
b=realloc(b,sizeof(int*)*10);
for(int i=0;i<5;i++){
   b[i]=realloc(b[i],sizeof(int*)*5);
}                                  //このように書いて変更する

struct d{
   int e;
   char *f;
};
struct d *g;
g=malloc(sizeof(d*)*8);
for(int j=0;j<sizeof(g);j++){
   g[j].f=malloc(sizeof(char*)*10);
}                            //こう書く事で[10]の要素を持ったchar配列fを持つ構造体dの変数gを8個用意する事ができて、
                                   //char配列fの要素数を後から100個に変えたいのであれば
for(int k=0;k<sizeof(g);k++){
   g[i].f=realloc(g[i].f,100);     //という風に全てのgに対してfのサイズを変更する必要がある
}
以上のような認識で合っているでしょうか?

また、malloc、reallocについて調べる中で「reallocによって何度も要素の数を変更するとメモリが散らかって動作が不安定になる」という類の文をよく目にしたのですが、このような事に対する対策という物はあるのでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#14

投稿記事 by softya(ソフト屋) » 10年前

そもそも2次元配列ではなく管理の楽な1次元配列で十分です。計算式で2次元配列と同等に扱えます。
サイズを変えるときは、別にmallocしなおしてコピーしてもとはfreeで良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: フォルダー上のファイルからデータを読み込む速度について

#15

投稿記事 by YuO » 10年前

reallocの使い方,まずいですね。
reallocの戻り値を,reallocに渡した変数と同じ変数で受けてはいけません。以下はダメな例です。

コード:

p = realloc(p, size);
reallocはメモリの確保に失敗した場合,メモリを解放せずにNULLを返すため,メモリがリークします。

コード:

p2 = realloc(p, size);
if (p2 == 0)
{
    /* メモリ確保失敗時の処理 */
}
else
{
    p = p2;
}
のようなコードにする必要があります。

というか,C++ならstd::vectorという便利な物が……。

wasawasa
記事: 94
登録日時: 10年前

Re: フォルダー上のファイルからデータを読み込む速度について

#16

投稿記事 by wasawasa » 10年前

返信ありがとうございます。

>>そもそも2次元配列ではなく管理の楽な1次元配列で十分です。計算式で2次元配列と同等に扱えます。
ですよねぇ…。
分かってはいるのですがついついパッと見でデータの配分が理解しやすい多次元配列にしたくなるなーと。悪い癖みたいなものですね。

>>サイズを変えるときは、別にmallocしなおしてコピーしてもとはfreeで良いと思います。
成程、無理してreallocにする必要は無いという事ですね。

>>reallocはメモリの確保に失敗した場合,メモリを解放せずにNULLを返すため,メモリがリークします。
そうだったんですか。ご忠告ありがとうございます。

>>というか,C++ならstd::vectorという便利な物が……。
一応C言語のつもりでプログラムしているので控えたいと思います。
(何で「コード[C++]」って表示されてんだろう・・・。)

色々意見を頂きましたが、メモリの確保はmalloc()、サイズの変更はmalloc+free、メモリを扱う時の操作はYuOさんの記述を参考に進めたいと思います。
皆さんありがとうございました。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: フォルダー上のファイルからデータを読み込む速度について

#17

投稿記事 by h2so5 » 10年前

ちなみに、wasawasaさんが上で書いているコードはstructキーワードの省略があるのでC++です。

閉鎖

“C言語何でも質問掲示板” へ戻る