首先,我想做的事情。我有一个类,可以处理存储上的一种特殊类型的大文件。我所说的处理,基本上是指以下几个方面。
class Large {
std::ifstream file;
Large(const char* fname) : file(fname) {
// read file header, create an index of stuff that's inside, that sort of stuff
...
}
void read_a_part(int which_part) const {
// read from ifstream, which unfortunately modifies its state
fstream.read(...)
}
...
}
现在,这都是伪代码,因为我对最佳实践感兴趣 而不是单一的解决方案。函数 read_a_state
将不会被编译,因为它修改了对象状态,但却被声明为 const
.
在我试图实现的目标中,我并没有修改我的File(我只是从它那里读取),也没有修改Large的实例。因此,所有的函数都应该被声明为 const
并应重入并行工作。看来,这不能用单一的 ifstream
身为 Large
. 不标记文件访问成员函数的解决方案 Large
作为 const
或使该成员 file
可变的都不好。这样的解决方案是不复存在的。由于 ifstream
也不能复制,似乎每次调用到 read_a_part
. 因此,我看到的唯一可行的解决方案是移动 file
的内部变量,从成员变成了 read_a_part
以及该类的大多数其他成员函数)。但是从性能上来说,这是个好的解决方案吗?成千上万次对ifstream ctor的调用不会给操作系统带来问题吗?就没有更好的方法吗?
把所有的都做成const而不做成const,这个逻辑稍微有点奇怪。
例如,编译器可能会错过读操作的重复调用(它会认为结果不会改变)。
但是,你可以在类中存储指针。
哼哼。祝您好运。
磁盘在顺序访问上表现最好(即使是SSD)。因此,并行化磁盘IO很可能会导致整体吞吐量的降低。
说到这里,有两种可能的解决方案。
按顺序读取文件,然后将部分文件交给线程池进一步处理。
使用mutex将文件访问序列化。
class Large {
std::ifstream file;
mutable std::mutex mtx;
public:
Large(const char* fname) : file(fname) {
// read file header, create an index of stuff that's inside, that sort of stuff
...
}
Part read_a_part(int which_part) const {
Part part;
std::lock_guard<std::mutex> lock(mtx);
file.seekg(which_part * part_size);
file.read(...&part...);
return part;
}
}
不知道为什么你的 read_a_part()
必须 const
. 如果没有,那么你可以直接删除 const
并去除 mutable
从mutex的。
我是在回答自己的问题,因为答案是否定的,几乎不存在一个 完美 的解决方案。所以,为了供大家参考,我列举了几种权衡解决方案,我已经考虑过了。
我选择了所以使用最后一种解决方案。这不是最高效的方案,但却是最简单的方案。