1

私は今日VC++2008でメモリマッピングをいじっていますが、それを使用する方法や、それが私の目的に合っているかどうかをまだ完全には理解していません。ここでの私の目標は、非常に大きなバイナリファイルをすばやく読み取ることです。

私は構造体を持っています:

typedef struct _data
{
    int number;
    char character[512];
    float *entries;
}Data;

これは何度もファイルに書き込まれます。「entries」変数は、浮動小数点の小数の配列です。このファイルを書き込んだ後(各「エントリ」配列が90000フロートである10000データ構造体)、データをより速く読み取ることができるように、次の関数を使用してこのファイルをメモリマップしようとしました。これが私がこれまでに持っているものです:

void readDataMmap(char *fname,      //name of file containing my data
                  int arraySize,    //number of values in struct Data
                  int entrySize)    //number of values in each "entries" array
{
    //Read and mem map the file
    HANDLE hFile = INVALID_HANDLE_VALUE;
    HANDLE hMapFile;
    char* pBuf;

    int fd = open(fname, O_RDONLY);
    if(fd == -1){
        printf("Error: read failed");
        exit(-1);
    }

    hFile = CreateFile((TCHAR*)fname, 
                       GENERIC_READ,          // open for reading 
                       0,                     // do not share 
                       NULL,                  // default security 
                       OPEN_EXISTING,         // existing file only 
                       FILE_ATTRIBUTE_NORMAL, // normal file 
                       NULL);                 // no template

    if (hFile == INVALID_HANDLE_VALUE) 
    { 
        printf("First CreateFile failed"));
        return (1);
    } 

    hMapFile = CreateFileMapping(hFile,
         NULL,                    // default security
         PAGE_READWRITE,
         0,                       // max. object size
         0,                    // buffer size
         NULL);                 // name of mapping object

    if(hMapFile == ERROR_FILE_INVALID){
        printf("File Mapping failed");
        return(2);
    }

    pBuf = (char*) MapViewOfFile(hMapFile,   // handle to map object
                        FILE_MAP_READ, // read/write permission
                        0,
                        0,
                        0);         //Was NULL, 0 should represent full file bytesToMap size
    if (pBuf == NULL)
    {
      printf("Could not map view of file\n");
      CloseHandle(hMapFile);

      return 1;
    }

    //Allocate data structure
    Data *inData = new Data[arraySize];
    for(int i = 0; i<arraySize; i++)inData[i].entries = new float[entrySize];

    int pos = 0;
    for(int i = 0; i < arraySize; i++)
    {
        //This is where I'm not sure what to do with the memory block
    }
}

関数の最後で、メモリがマップされ、メモリブロック「pBuf」の先頭へのポインタが返された後、このメモリブロックをデータに読み戻すにはどうすればよいかわかりません。構造。したがって、最終的には、このメモリブロックを10000データ構造体エントリの配列に戻したいと思います。もちろん、私はこれを完全に間違っている可能性があります...

4

1 に答える 1

7

メモリマップトファイルを処理することは、他の種類のメモリへのポインタを処理することと実際には同じです。メモリマップトファイルは、同じ名前を使用する任意のプロセスから読み取りおよび書き込みができるデータのブロックにすぎません。

ファイルをメモリマップにロードし、そこで自由に読み取って更新し、一定の間隔または既知の間隔でファイルにダンプしたいとしますか?その場合は、ファイルから読み取り、データをメモリマップポインタにコピーするだけです。後で、マップからデータを読み取り、それをメモリに整列された構造にキャストして、構造を自由に使用できます。

もし私があなただったら、おそらく次のようないくつかのヘルパーメソッドを作成するでしょう

data ReadData(void *ptr)

void WriteData(data *ptrToData, void *ptr)

ここ*ptrで、はメモリマップアドレスであり、はメモリ*ptrToDataに書き込むためのデータ構造へのポインタです。実際、この時点では、メモリがマップされているかどうかは関係ありません。ローカルメモリにロードされたファイルから読み取りたい場合は、それも実行できます。

memcpyを使用してソースからターゲットにデータをコピーする他のブロックデータとまったく同じ方法で読み取り/書き込みを行うことができ、ポインター演算を使用してデータ内の場所を進めることができます。「メモリマップ」について心配する必要はありません。これはメモリへの単なるポインタであり、そのように扱うことができます。

また、直接メモリポインタを処理するため、各要素をマップされたファイルに1つずつ書き込む必要がないため、次のようにすべてを1つのバッチで書き込むことができます。

memcpy(mapPointer, data->entries, sizeof(float)*number)

float*entriesのサイズをdata->entriesマップポインタの開始アドレスにコピーします。もちろん、好きなように、好きな場所にコピーできます。これは単なる例です。http://www.devx.com/tips/Tip/13291を参照してください。

データを読み戻すのも似たようなことですが、メモリアドレスを既知の場所に明示的にコピーしたいので、構造を平坦化することを想像してください。それ以外の

data:
  int
  char * -> points to some address
  float * -> points to some address

ポインタが他のメモリを指している場合は、次のようにメモリをコピーします

data:
  int 
  char * -> copy of original ptr
  float * -> copy of original ptr
512 values of char array 
number of values of float array

したがって、この方法で、メモリマップからローカルにデータを「再シリアル化」できます。配列はメモリへの単なるポインタであることを忘れないでください。別のときに割り当てられた可能性があるため、メモリはオブジェクト内でシーケンシャルである必要はありません。ポインタがメモリマップを指している実際のデータを必ずコピーする必要があります。これを行う一般的な方法は、オブジェクトをメモリマップに直接書き込み、次にすべてのフラット化された配列でオブジェクトを追跡することです。それを読み戻すと、最初にオブジェクトを読み取り、次にポインタをインクリメントsizeof(object)して次の配列を読み取り、次にポインタをもう一度インクリメントしますarraysize

次に例を示します。

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

typedef struct data{
    int size;
    char items[512];
    float * dataPoints;
};

void writeToBuffer(data *input, char *buffer){
    int sizeOfData = sizeof(data);
    int dataPointsSize = sizeof(float) * input->size;

    printf("size of data %d\n", sizeOfData);

    memcpy(buffer, input, sizeOfData);

    printf("pointer to dataPoints of original %x\n", input->dataPoints);

    memcpy(buffer + sizeOfData, input->dataPoints, dataPointsSize);
}

void readFromBuffer(data *target, char * buffer){
    memcpy(target, buffer, sizeof(data));

    printf("pointer to datapoints of copy %x, same as original\n", target->dataPoints);


    // give ourselves a new array
    target->dataPoints =  (float *)malloc(target->size * sizeof(float));

    // do a deep copy, since we just copied the same pointer from 
    // the previous data into our local

    memcpy(target->dataPoints, buffer + sizeof(data), target->size * sizeof(float));

    printf("pointer to datapoints of copy %x, now it's own copy\n", target->dataPoints);
}

int main(int argc, char* argv[])
{
    data test;

    for(unsigned int i=0;i<512;i++){
        test.items[i] = i;
    }

    test.size = 10;

    // create an array and populate the data
    test.dataPoints = new float[test.size];

    for(unsigned int i=0;i<test.size;i++){
        test.dataPoints[i] = (float)i * (1000.0);
    }

    // print it out for demosntration
    for(unsigned int i=0;i<test.size;i++){
        printf("data point value %d: %f\n", i, test.dataPoints[i]);
    }

    // create a memory buffer. this is no different than the shared memory
    char * memBuffer = (char*)malloc(sizeof(data) + 512 + sizeof(float) * test.size + 200);

    // create a target we'll load values into
    data test2;

    // write the original out to the memory buffer
    writeToBuffer(&test, memBuffer);

    // read from the memory buffer into the target
    readFromBuffer(&test2, memBuffer);

    // print for demonstration
    printf("copy number %d\n", test2.size);
    for(int i=0;i<test2.size;i++){
        printf("\tcopy value %d: %f\n", i, test2.dataPoints[i]);
    }

    // memory cleanup

    delete memBuffer;
    delete [] test.dataPoints;

    return 0;
}

構造体からメモリにデータを書き込むときは、データアライメントについても読みたいと思うでしょう。パッキング構造C ++構造体アライメントの質問、およびデータ構造アライメントの操作を確認してください。

読み取るときに事前にデータのサイズがわからない場合は、後で使用できるように、データのサイズをメモリマップの先頭の既知の位置に書き込む必要があります。

とにかく、ここでそれを使用する権利があるかどうかという事実に対処するために、私はそれがそうだと思います。ウィキペディアから

ファイルのメモリマッピングの主な利点は、特に大きなファイルで使用する場合に、I/Oパフォーマンスが向上することです。...メモリマッピングプロセスは、ページファイルの処理を担当するのと同じサブシステムである仮想メモリマネージャによって処理されます。メモリマップトファイルは、一度に1ページ全体をメモリにロードします。ページサイズは、最大のパフォーマンスを得るためにオペレーティングシステムによって選択されます。ページファイル管理は仮想メモリシステムの最も重要な要素の1つであるため、ファイルのページサイズのセクションを物理メモリにロードすることは、通常、非常に高度に最適化されたシステム機能です。

すべてを仮想メモリにロードすると、OSは必要に応じてファイルをメモリにページインおよびメモリからページアウトし、「遅延ロード」メカニズムを作成します。

とはいえ、メモリマップは共有されているため、プロセスの境界を越えてそれらを名前付きミューテックスと同期して、プロセス間でデータを上書きしないようにする必要があります。

于 2012-10-08T23:34:35.697 に答える