DuckDB
统计前 1000 的高频词,用时 20 秒。C++
代码#include <queue>
#include <string>
#include <vector>
#include <iostream>
#include "ankerl/unordered_dense.h"
const auto TOP_NUM = 1000;
int main() {
std::cin.tie(nullptr);
std::ios::sync_with_stdio(false);
std::string word;
ankerl::unordered_dense::map<std::string, size_t> dict;
dict.reserve(10000000);
// 逐词计算频次
while (std::cin >> word)
++dict[word];
using dict_item_type = decltype(dict)::value_type;
auto comp = [](const auto* lhs, decltype(lhs) rhs) {
return lhs->second > rhs->second;
};
std::priority_queue<
const dict_item_type*,
std::vector<const dict_item_type*>,
decltype(comp)> words(comp);
// 找出排名靠前的词频
for (const auto& item: dict) {
if (words.size() < TOP_NUM)
words.push(&item);
else if (item.second > words.top()->second) {
words.pop();
words.push(&item);
}
}
// 并排序好这些高频词
std::deque<const dict_item_type*> result;
while (!words.empty()) {
auto top = words.top();
result.push_front(top);
words.pop();
}
// 再逐一输出
for (auto&& i: result)
std::cout << i->second << '\t' << i->first << '\n';
}
std::cin >> word
这个io可能比较慢
确实,我改成 std::getline(std::cin, word)
后,就由 28 秒 → 25 秒了。
你可以自行统计一下分段用时,看看输入、计算和输出各占比多少。
我简单测了测,读取 1 亿 15 长度字符串用时。
std::cin >> word
:5.1 秒std::getline(std::cin, word)
:2.2 秒std::cin.read()
填充,std::memchr
找 \n
,str.append()
构造:1.4 秒感觉输入上,这速度也可以了。
而且,我 strace
了一下 DuckDB,它也是要读到缓冲区里的,且缓冲区贼大,32MB。。且一定要填充完整 32MB,才干活。。
怪不得我说,一边解压大文本/脚本实时生成内容,一边喂给 DuckDB,怎么耗时会变长。。明明 writer 产生内容的速度还挺快的呀。。
也许输入输出是大头(因为stdio是同步锁定的)
我取消与 stdio 同步了:std::ios::sync_with_stdio(false);
。
这个确实很耗时间。不取消的话,总时间直接翻倍。。
把文件mmap到内存然后使用
char*
指针直接访问
如果想解压大文本,再通过管道喂给程序时,好像不能 mmap 了吧。。
膜拜大佬!
https://www.chengyao.xyz
你充分利用多核CPU了吗?把任务分成多个部分跑在不同的CPU核心上,应该会比单线程快很多。就算各个部分得出结果之后还得合并再筛选一次,也应该比整体在单个CPU核心上运行更快。