我最近写了一个
grep
克隆。它对指定目录的所有文件进行字符串查询的递归搜索。程序的核心是如下所示的函数。
/** Do recursive search of query string in files under the given directory. */
void Search::searchFiles()
{
std::vector<std::future<void>> futures;
auto files = fs::recursive_directory_iterator(_directory);
for (auto& file : files) {
if (!fs::is_directory(file)) {
futures.emplace_back(std::async(&Search::searchFile, this, file));
}
}
for (auto & ftr : futures) ftr.wait();
}
我创造了很多未来,但我本可以使用
std::thread
。例如,在下面的代码片段中 searchFiles
使用 std::thread
代替。
void Search::searchFiles()
{
std::vector<std::thread> threads;
auto files = fs::recursive_directory_iterator(_directory);
for (auto& file : files) {
if (!fs::is_directory(file)) {
threads.emplace_back(&Search::searchFile, this, file);
}
}
for (auto & th : threads) th.join();
}
其中一种实施方式更好吗? 此外,如何使用迭代器实现
searchFiles
?这个想法是创建固定数量的线程,并且如果有更多文件需要获取,每个searchFile
实例都会增加文件迭代器。下面是我的一个想法的草图。
void Search::searchFile(FileIterator it)
{
while(true) {
// get next file from iterator
std::unique_lock<std::mutex> ulock(_mutex);
if (it == it.end()) break;
auto file = *it;
++it;
ulock.unlock();
// open file and loop through lines.
std::ifstream filestream(file);
...
}
void Search::searchFiles()
{
std::vector<std::thread> threads;
auto fileIterator = fs::recursive_directory_iterator(_directory);
// create only 10 threads
for (int idx = 0; idx < 10; idx++) {
if (!fs::is_directory(file)) {
threads.emplace_back(&Search::searchFile, this, fileIterator);
}
}
for (auto & th : threads) th.join();
}
其中一种实施方式更好吗?
您没有告诉我们您的关键绩效指标,您的“更好”指标, 如果没有比较标准,它们都同样好。
假设在16核机器上我们以“经过的时间”作为这样的标准。 那么这个提交将是关于性能的, 但它不包括任何性能测量。 OP 可以在其感兴趣的目标主机上记录此类时间;我们不能。
有些文件很短,有些文件很长。 该信息可通过 POSIX stat() 调用预先获得。 较长的文件往往意味着基准测试所需的时间较长。 OP代码以任意顺序安排文件, 接近结尾时很有可能出现长文件。 我们称这样的匹配任务为“落后者”, 因为在许多其他核心闲置后,它会让一个核心保持忙碌。
在 N 核机器上,此代码应立即调度 N-1 个任务, 然后识别所有输入文件的大小并对其进行排序。 然后按照大小降序执行剩余的任务。 这保证了在运行结束时我们将分配 持续时间短的任务,因此所有核心往往会在大约同一时间空闲。