我今天一直在 VC++ 2008 上研究内存映射,但我仍然没有完全理解如何使用它或者它是否适合我的目的。我的目标是快速读取一个非常大的二进制文件。
我有一个结构:
typedef struct _data
{
int number;
char character[512];
float *entries;
}Data;
多次写入文件。 “条目”变量是一个浮点小数数组。写入此文件后(10000 个数据结构,每个“条目”数组为 90000 个 float ),我尝试使用以下函数内存映射此文件,以便我可以更快地读取数据。这是我到目前为止所拥有的:
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 数据结构条目数组。当然,我可能做错了......
最佳答案
处理内存映射文件实际上与处理任何其他类型的内存指针没有什么不同。内存映射文件只是一个数据 block ,您可以使用相同的名称从任何进程读取和写入它。
我假设您想将文件加载到内存映射中,然后在那里随意读取和更新它,并以某个固定或已知的时间间隔将其转储到文件中,对吗?如果是这种情况,那么只需从文件中读取并将数据复制到内存映射指针即可。稍后您可以从 map 中读取数据并将其转换为内存对齐结构并随意使用您的结构。
如果我是你,我可能会创建一些辅助方法,例如
数据读取数据(void *ptr)
和
void WriteData(data *ptrToData, void *ptr)
*ptr 是内存映射地址,*ptrToData 是指向要写入内存的数据结构的指针。真的在这一点上它的内存是否映射并不重要,如果你想从加载到本地内存的文件中读取你也可以这样做。
您可以使用 memcpy 将数据从源复制到目标,以与任何其他 block 数据相同的方式对其进行读/写,并且可以使用指针算法来推进数据中的位置。不要担心“内存映射”,它只是一个指向内存的指针,您可以这样对待它。
此外,由于您将要处理直接内存指针,因此您不需要将每个元素一个一个地写入映射文件,您可以将它们全部写入一批,如
memcpy(mapPointer, data->entries, sizeof(float)*number)
将 data->entries 中的 float*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;
}
在将数据从结构体写入内存时,您可能还想阅读有关数据对齐的内容。检查working with packing structures , C++ struct alignment question , 和 data structure alignment .
如果在读取时不知道数据的大小,则应将数据的大小写入内存映射的开头已知位置,以备后用。
无论如何,要解决我认为在这里使用它是否正确的事实。来自 wikipedia
The primary benefit of memory mapping a file is increasing I/O performance, especially when used on large files. ... The memory mapping process is handled by the virtual memory manager, which is the same subsystem responsible for dealing with the page file. Memory mapped files are loaded into memory one entire page at a time. The page size is selected by the operating system for maximum performance. Since page file management is one of the most critical elements of a virtual memory system, loading page sized sections of a file into physical memory is typically a very highly optimized system function.
您要将整个文件加载到虚拟内存中,然后操作系统可以根据需要为您将文件分页进出内存,从而创建“延迟加载”机制。
总而言之,内存映射是共享的,因此如果它跨进程边界,您需要将它们与命名的互斥锁同步,这样您就不会覆盖进程之间的数据。
关于c++ - 将内存映射数据 block 读入结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12790820/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最