写保险丝文件系统时,我有一个unordered_map<std::string, struct stat>
作为缓存,它在启动时为所有文件和目录启动以减少硬盘驱动器上的读取。如何从路径列表中优化目录列表?
为了满足readdir()
回调我写了下面的循环:
const int sp = path == "/" ? 0 : path.size();
for (auto it = stat_cache.cbegin(); it != stat_cache.cend(); it++)
{
if (it->first.size() > sp)
{
int ls = it->first.find_last_of('/');
if (it->first.find(path, 0) == 0 && ls == sp)
filler(buf, it->first.substr(ls + 1).c_str(), const_cast<struct stat*>(&it->second), 0, FUSE_FILL_DIR_PLUS);
}
}
的想法是一个对象,它的路径与目录路径开始且其最后的斜杠的目录路径的终点将是一个它的成员。我已经彻底地测试了它,它的工作原理。
插图:
Reading directory: /foo/bar
Candidate file: /bazboo/oof - not in dir (wrong prefix)
Candidate file: /foo/bar/baz/boo - not in dir (wrong lastslash location)
Candidate file: /foo/bar/baz - in dir!
然而现在,这是惊人的慢(尤其是在文件系统有50多万的物体在高速缓存)。 Valgrind/Callgrind特别指责std::string:find_last_of()
和std::string::find()
电话。
我已经添加了if (it->first.size() > sp)
以尝试加速循环,但性能增益最多是最小的。
我也尝试了通过在四个区块中并行化循环来加速这个例程,但是在unordered_map::cbegin()
期间以段错误结束。
我没有实际的代码了,但我相信它看起来是这样的:
const int sp = path == "/" ? 0 : path.size();
ThreadPool<4> tpool;
ulong cq = stat_cache.size()/4;
for (int i = 0; i < 4; i++)
{
tpool.addTask([&]() {
auto it = stat_cache.cbegin();
std::next(it, i * cq);
for (int j = 0; j < cq && it != stat_cache.cend(); j++, it++)
{
if (it->first.size() > sp)
{
int ls = it->first.find_last_of('/');
if (it->first.find(path, 0) == 0 && ls == sp)
filler(buf, it->first.substr(ls + 1).c_str(), const_cast<struct stat*>(&it->second), 0, FUSE_FILL_DIR_PLUS);
}
}
});
}
tpool.joinAll();
我也试图与通过地图桶,这unordered_map::cbegin(int)
提供了一个方便的过载拆分它,但它仍然隔断了。
同样,我目前正在使用第一个(非并行)代码,并且希望对那个代码有所帮助,因为并行化代码没有工作。我只是认为我会把我的并行尝试包括在内,以便完整性,小白点和努力证明。
是否有任何其他优化此循环的选项?
如果代码在其他方面工作,并且您只是要求优化速度/效率,那么您应该在[CodeReview](http://codereview.stackexchange.com)上发布此代码。 –
使用不是'unordered_map'而是'map',调用'upper_bound'传入目录名以查找O(lg N)中的第一个条目,然后所有其他条目都相邻。 'readdir'的最终成本:O(k + lg N) –