我有一系列文本文件,我想读取这些文件作为有限状态机的输入。因此,一次只需要该文件中的一个字符。现在,据我所知,内存访问是一项耗时的操作,导致了这个问题:
在使用数据之前,将整个(小到中等大小的)文件加载到内存中是否更快,或者ifstream是否经过了足够的优化,以至于重复插入硬盘不会影响性能?
如果你知道文件大小很小,你可以一次读取所有文件,如果文件很大,那么最好一次读取一行。现在,对于存储您所读取的内容,这可能取决于您正在读取的数据类型以及您如何打开文件(文本或二进制)。如果你用二进制打开,你可以把它们存储到unsigned char*
中,但你必须知道你的数据结构包含多少字节,这取决于预期的数据类型。如果您正在读取文本文件,则可以将内容保存到字符串中,但如果将其保存到字符串,则需要相应地解析字符串。打开和关闭文件往往比在内存中工作要慢。当你从硬盘访问文件时,你使用的总线往往较慢,而且你可能会有缓存未命中。在ram中工作通常更快。
表示这一点的一种简单方法是将3D对象渲染到CPU上的屏幕与将批量数据集发送到GPU进行比较。想象一个3D图形引擎,它存储一批顶点信息,其中每个顶点可能包含以下信息;(x,y,z)世界位置,(x,y)具有(r,g,b,a)颜色信息的屏幕像素信息,正常&用于光处理的切线矢量信息和用于纹理坐标信息的(t,s)或(u,v)。因此,一个简单的三角形可能看起来像这样:
// These Structs I Have Put Into Columns To Preserve Page Length
// 8 Bytes 16 Bytes 8 Bytes
struct Vec2f { struct Vec2d { struct Vec2i {
union { union { union {
float f2[2]; double d2[2]; int i2[2];
// Positional
struct { struct { struct {
float x; double x; int x;
float y; double y; int y;
}; }; };
// Texture Coords - Some Use S&T Others Use U&V
struct { struct { struct {
float s; double s; int s;
float t; double t; int t;
}; }; };
/*struct { struct { struct {
float u; double u; int u;
float v; double v; int v;
}; // Only Shown Here }; };*/
// Color Values.
struct { struct { struct {
float r; double r; int r;
float g; double g; int g;
}; }; };
}; }; };
}; }; };
// 12 Bytes 24 Bytes 12 Bytes
struct Vec3f { struct Vec3d { struct Vec3i {
union { union { union {
float f3[3]; double d3[3]; int i3[3];
struct { struct { struct {
float x; double x; int x;
float y; double y; int y;
float z; double z; int z;
}; }; };
struct { struct { struct {
float s; double s; int s;
float t; double t; int t;
float p; double p; int p;
}; }; };
struct { struct { struct {
float r; double r; int r;
float g; double g; int g;
float b; double b; int b;
}; }; };
}; }; };
}; }; };
// 16 Bytes 32 Bytes 16 Bytes
struct Vec4f { struct Vec4d { struct Vec4i {
union { union { union {
float f4[4]; double d4[4]; int i4[4];
struct { struct { struct {
float x; double x; int x;
float y; double y; int y;
float z; double z; int z;
float w; double w; int w;
}; }; };
struct { struct { struct {
float s; double s; int s;
float t; double t; int t;
float p; double p; int p;
float q; double q; int q;
}; }; };
struct { struct { struct {
float r; double r; int r;
float g; double g; int g;
float b; double b; int b;
float a; double a; int a;
}; }; };
}; }; };
}; }; };
// Not All Triangles Sent To Be Rendered To The Screen Will Have All Of This
// Information, But There Is A Good Chance That Many Will.
struct TriangleMeshF {
// If We Do The Math For This One Triangle Mesh
// The Float Vecs Are 8, 12 & 16 Bytes Each
Vec3f v3Vertices[3]; // 3 Vertexes That Make This Triangle
Vec4f v4Color[3]; // 3 Colors 1 For Each Vertex
Vec2f v2TexCoord[3]; // 3 Texture Coords 1 For Each Vertex
Vec3f v3Normal[3]; // 3 Normal Vectors 1 For Each Vertex
Vec3f v3BiNormal[3];
Vec3f v3Tangent[3];
Vec3f v3BiTangent[3];
// 12*3*5 + 16*3 + 8*3 = 252 Bytes Or There are 63 4Byte Floats
};
// 252 Bytes Doesn't Seem Like A Large Amount Of Data But What Happens When
// We Have A Model File That Fills In A vector<TrianleMesheF>
// Lets Say That This One Model File Fills This vector<T> with 80K TriangleMeshes
// The Amount Of Data Being Processed On The CPU Is Slower Than The GPU
// Also The Amount Of Rendering Calls From The CPU To The GPU Can Be Slow
// Because It Travels Over The Bus. This Is Why 3D Graphis Engine Developers
// Who Use Either DirectX Or OpenGL OR Both Use Batches To Collect Buckets
// Of Different Types Of Rendering Meshes Then Sends Them To The Graphics
// Card When The Buckets Become Full. This Is Kind Of How The Process
// Or Concept Of Programmable Shaders Came Into Play
// I Only Used Vec2f, 3f, & 4f Here, But Imagine If The User Had Used Vec4d
// Then The Mesh Would Double In Size And This Would Get Large Very Fast.
这似乎有点偏离主题,但这里的概念仍然适用。当您使用处理器通过总线与视频卡、声卡、硬盘驱动器、光盘驱动器、网络等进行通信时,这是一个比直接从ram工作更慢的过程,就像传统的OS-CPU ram比GPU-ram慢一样。
因此,这一切都取决于你的数据类型,你如何以文本或二进制形式读取文件,以及文件大小有多大。正如我之前所说,两种最常见的方法要么是一次性将文件读取到缓冲区结构中,要么是逐行将其内容保存到无符号字符数组、字符数组或字符串中。如果它都是纯数字,那么它可能是一个int数组。这对于float或doubles不起作用,因为您必须为此解析文本。
正如您所说,您使用的是txt文件,因此最好的选择很可能是,如果文件大小小于您设置的特定文件大小限制,那么很容易打开文件并逐行读取。如果它比这个大,但比太大小,你可以一口气读完。如果它太大,无法容纳可用的内存量,或者太大,不能容纳在单个缓冲区中,那么你有几个选项,你可以设置一个缓冲区大小,比如4MB或2GB,当你读取文件并将其保存到缓冲区时,你必须计算你已经读取了多少,然后一旦达到最大缓冲区大小时,你就必须存储第一个缓冲区,然后创建一个新的缓冲区并继续读取并重复此操作,直到达到EOF。逐行阅读也可以,这在阅读过程后会更好一些。逐行使解析文本字符串变得更加容易。与必须将此缓冲区分隔成文本行相反。
然而,要做到这一点,您必须有两到三个不同的解析机制。一个用于读取所有内容,另一个用于逐行读取并确定何时执行哪个操作。它可能只需要一些额外的函数或方法,但您可能想要解析整个读取,寻找换行符'/0'
,然后将其保存到字符串中,并将该字符串推送到向量中。然后将这个向量传递给解析器,以查找关键字或令牌。一行一行可以直接保存到向量容器中。在确定文件大小限制时,您需要知道在readAll()和readLine()之间切换,您可能需要进行基准速度测试来确定合适的文件大小。我希望这能帮助你做决定。
只要有足够的RAM,一次读取整个文件通常会更快。ifstream
是缓冲的,但(1)只有你才能知道缓冲区的最佳大小,(2)也有一些开销(与此相比)。