あなたのアイデア自体は悪くありません。改行で区切られたファイルを仮定すると(つまり、問題なしに行間を切り取ることができます)、そのようなブロックを持つブロックを見つけることができます(別のプログラムから切り出したので、最初に確認してください)
// just in case
#define _LARGEFILE_SOURCE
#define _BSD_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
// TODO: should be calculated
#define FILE_PARTS 100
// TODO: should not be global
off_t positions[FILE_PARTS + 1];
int slice_file(FILE * fp)
{
off_t curr_pos = 0;
off_t filesize = 0;
off_t chunk_size = 0;
int fd;
int i, res;
char c;
struct stat sb;
// get size of file
fd = fileno(fp);
if (fd == -1) {
fprintf(stderr, "EBADF in prepare_and_backup() for data-file pointer\n");
return 0;
}
if (fstat(fd, &sb) == -1) {
fprintf(stderr, "fstat() failed\n");
return 0;
}
// check if it is a regular file
if ((sb.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr, "Not a regular file\n");
return 0;
}
// TODO: check if filesize and chunksize >> 1
filesize = sb.st_size;
chunk_size = filesize/((off_t) FILE_PARTS);
positions[0] = 0;
curr_pos = 0;
for (i = 1; i < FILE_PARTS; i++) {
res = fseeko(fp, curr_pos, SEEK_SET);
if (res == -1) {
fprintf(stderr, "Error in fseeko(): %s\n",
strerror(errno));
return 0;
}
curr_pos += chunk_size;
// look for the end of the line to cut at useful places
while ((c = fgetc(fp)) != EOF) {
curr_pos++;
// TODO: add code to honor Apple's special needs
if (c == '\n') {
c = fgetc(fp);
if (c == EOF) {
break;
}
curr_pos++;
break;
}
}
positions[i] = curr_pos - 1;
}
// Position of the end of the file
positions[i] = filesize;
// Is that even needed?
rewind(fp);
return 1;
}
スレッドを開始することができます。スレッドの開始点と終了点(上の関数で計算したものとそうでないものがあります)と、個々のスレッド内でのmマッピングを心配する必要はありません。出力がブロックと同じサイズであれば、インプレースで作業することもできます。あなたはNULL
にそれを設定し、特定のアドレスのために気にしない場合は
EDIT
mmap
の宣言は
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
です。
length
は、マップを初期化するバイト数です。この場合、ファイル記述子fd
の内容で埋められます。
この充填の開始点は、offset
によって1つの不快な警告が設定されています。ページサイズの倍数である必要があります(正確な数はsysconf(_SC_PAGE_SIZE)
となります)。あまり問題ではなく、開始前にページに設定して、実際の開始時に作業を開始すれば、すべての必要な情報が存在します。あなたはそのページの残りの部分を無視することができます。
またはファイル全体を取り出してマップし、ドライブ上のファイルを使用するときと同じように使用します。すべてのスレッドにそのマップのブロック(必要な情報はpositions
)を与え、そこから作業します。
最初のメリット:複数のCPUブロックを使用すると、OSによって簡単に移動できるため、複数のCPUでキャッシュミスが少なくなることもあります。すべてのCPU /グループに独自のRAMまたは少なくとも非常に大きなキャッシュがあるクラスタまたはその他のアーキテクチャを実行する場合は、これは必須です。
後者の利点:実装が簡単ですが、マップの大きな塊が1つあります。ランタイムに影響する場合もあります。
ヒント:近代的で高速なSSDでの私の経験:最近は読み込み速度が非常に速いため、マッピングの代わりに直接ファイルアクセスで簡単に始めることができます。かなり遅い「通常の」HDDであっても、合理的なスピードを得ることができます。上記のスニペットをリッピングしたプログラムは、120GB以上の大容量のCSVファイルを検索しなければならず、十分なRAMを搭載していないため、ドライブに十分なスペースがなくてDBにロードできませんでした。数年前)。それは鍵 - >「たくさんの、異なる、価値観」ファイルでしたが、ありがたいことに既にソートされていました。そこで私は、上記の方法(KEY-> position)でインデックスファイルを作成しましたが、私の例では100ブロックよりはるかに多くのブロックを作成しました。インデックスファイル内のキーもソートされていましたので、検索するキーが大きい場合(データは昇順でソートされています)、インデックスエントリよりキーがブロック内にあることを意味する場合は、位置が存在する場合はそれを示します。ブロックは、キャッシュとして動作するためにRAMの一部を保持するのに十分なほど小さかったが、あまり得られなかった。着信要求はかなり均一にランダムだった。
貧しい人のDBだからといって、ユーザーからの苦情なしに仕事をするのに十分速いです。
面白いことに、キーは英数字で、ソートアルゴリズムでは「aAbBcC ...」がソートされています。つまり、strcmp
を直接使用することはできません。私はしばらくの間私の頭を傷つけましたが、解決策はかなり単純です:大文字小文字を無視して比較してください(例:strcasecmp
、利用可能な場合)でない場合と等しい そうでなければ、通常の結果の逆を返しますstrncmp
例えば、ちょうどreturn -strcmp(a,b);
)。
あなたは仕事が必要な種類のデータについては非常に黙っていましたので、上記のことはあなたにはあまり関心がないかもしれません。
異なるスレッドで同時にファイルの異なる部分を読み取る必要がありますか? – yano
区切られたレコードを '\ n'読みたいですか?行がページ境界を超えている場合はどうなりますか? – wildplasser
@yano Yesssあなたが言ったように、それを行う方法はありますか? – superrman777