Сегодня я играл с отображением памяти на VC ++ 2008, и до сих пор не до конца понял, как его использовать или подходит ли он для моих целей. Моя цель здесь — быстро прочитать очень большой двоичный файл.
У меня есть структура:
typedef struct _data
{
int number;
char character[512];
float *entries;
}Data;
который записан много-много раз в файл. переменная «records» представляет собой массив десятичных чисел с плавающей запятой. После записи этого файла (10000 структур данных с каждым массивом «записей» по 90000 операций с плавающей запятой) я попытался отобразить в памяти этот файл с помощью следующей функции, чтобы я мог быстрее читать данные. Вот что у меня так далеко:
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 записей структуры данных. Конечно, я мог бы сделать это совершенно неправильно …
Работа с отображенным в память файлом на самом деле ничем не отличается от работы с любым другим указателем на память. Файл отображения памяти — это просто блок данных, который вы можете читать и записывать из любого процесса с тем же именем.
Я предполагаю, что вы хотите загрузить файл в карту памяти, а затем прочитать и обновить его по своему усмотрению там и сбросить его в файл с некоторым регулярным или известным интервалом, верно? Если это так, то просто прочитайте из файла и скопируйте данные в указатель карты памяти и все. Позже вы можете читать данные с карты и вносить их в выровненную структуру памяти и использовать свою структуру по своему желанию.
Если бы я был тобой, я бы создал несколько вспомогательных методов, таких как
data ReadData(void *ptr)
а также
void WriteData(data *ptrToData, void *ptr)
куда *ptr
это адрес карты памяти и *ptrToData
указатель на вашу структуру данных для записи в память На самом деле в этот момент не имеет значения, отображена ли его память или нет, если вы хотите прочитать из файла, загруженного в локальную память, вы тоже можете это сделать.
Вы можете читать / записывать в него точно так же, как и в случае с любыми другими данными блока, используя memcpy для копирования данных из источника в цель, и вы можете использовать арифметику указателей для продвижения местоположения в данных. Не беспокойтесь о «карте памяти», это просто указатель на память, и вы можете обращаться с ней как с таковой.
Кроме того, поскольку вы будете иметь дело с прямыми указателями памяти, вам не нужно записывать каждый элемент в отображаемый файл один за другим, вы можете записать их все в одном пакете, как
memcpy(mapPointer, data->entries, sizeof(float)*number)
Какие копии плавают * размер записей от 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 ++, а также выравнивание структуры данных.
Если вы не знаете размер данных заранее при чтении, вы должны записать размер данных в известную позицию в начале карты памяти для последующего использования.
В любом случае, для решения вопроса о том, имеет ли право использовать его здесь, я думаю, что это так. От википедия
Основным преимуществом отображения памяти на файл является увеличение производительности ввода-вывода, особенно при использовании больших файлов. … Процесс отображения памяти обрабатывается менеджером виртуальной памяти, который является той же подсистемой, которая отвечает за работу с файлом подкачки. Файлы с отображенной памятью загружаются в память по одной странице за раз. Размер страницы выбирается операционной системой для максимальной производительности. Поскольку управление файлом подкачки является одним из наиболее важных элементов системы виртуальной памяти, загрузка разделов файла размером с страницу в физическую память обычно является очень высокооптимизированной функцией системы.
Вы собираетесь загрузить все это в виртуальную память, а затем ОС может разместить файл в памяти и из памяти для вас, когда вам это потребуется, создав механизм «отложенной загрузки».
Все это говорит о том, что карты памяти являются общими, поэтому, если они выходят за границы процесса, вы захотите синхронизировать их с именованным мьютексом, чтобы не перезаписывать данные между процессами.
Других решений пока нет …