wordcount设计与优化

简介:

原文档见:http://gitlab.alibaba-inc.com/middleware/coding4fun-3rd/blob/master/observer.hany/design.md

  • 淘宝中间件第三期编程比赛,题意概述:读入一个文件,统计其中最常出现的前 10 个单词。

系统设计

  • 按照题意,可设计如下简单拓扑图。

0-简单拓扑图

  • 图中方块表示计算节点箭头表示数据流动
    注意: Counter 和 Selector 之间需要设置一道栅栏 ,所有单词统计完毕后才能开始筛选单词。

优化1:同步 OR 异步

  • Reader 是 IO 集中型操作,其他计算节点都是 CPU 集中型操作。
    如果先读完文件再操作,读文件的这段时间 CPU 就白白空闲着浪费掉了。

  • 简单的优化就是异步读文件。
    增加一个后台 Task 线程,Reader 每读取一小块文件数据(Chunk),
    就交给 Task 线程处理,Reader 继续读下一个 Chunk 的时候,Task 已经跑起来了,
    一个占用 IO,一个占用 CPU,充分利用计算机资源。

1-异步读文件

  • 通过异步读文件,Reader 和 Task 能够并发处理数据,提高性能。

实现细节

  • ChunkedTextReader 实现按 Chunk 分块读文件。
    为了避免 Chunk 边界意外将一个单词拆成两半,
    除最后一个 Chunk 外,每个 Chunk 都将末尾的最后一个单词切开,
    拼接到下一个 Chunk 的前面,让下一个 Chunk 处理。
  • Reader 和 Task 之间通过 BlockingQueue 传输数据,
    这是一个线程安全的 "生产者-消费者" 队列。
  • 经测试,Chunk 分块太小队列操作过于频繁,性能下降。
    分块太大读文件阻塞太久,达不到异步读的目的,
    因此默认限制 Chunk 最小 1MB,最大 8MB。

优化2:并发 OR 并发

  • 读文件的速度比处理文件的速度快的多,一个线程 CPU 跑到 100% 也是远远处理不过来。
    测试机有 16 个核,可创建多个并发的 Task 线程,将每个核都利用起来。
    由于 Task 是高度 CPU 密集型操作,默认取 Task 线程数等于 CPU 核数。

2-并发处理

  • 栅栏控制所有数据处理完成才能开始按词频选择单词。

实现细节

  • ConcurrentBlockingQueueExecutor 管理所有 Task 线程,
    executor 在每个线程上等待线程结束,实现栅栏同步。
  • ConcurrentBlockingQueueTask 实现 Task 线程处理流程。
  • Reader 读完文件后在 executor 上设置 done 标识位,
    Task 发现 queue 为空且 executor 设置了 done 标志位,
    则说明文件已经读完并处理完,task 结束。
  • ConcurrentTrieNode 实现了线程安全的 Trie 树。

语言细节

  • 在 Java 实现中,
    ConcurrentBlockingQueueTask 
    ConcurrentBlockingQueueExecutor 互相依赖,
    但 C++ 不能处理互相依赖,
    所以将 task 对 executor 的依赖剥离到
    ConcurrentBlockingQueueExecutorSupport 中,
    避免互相依赖的问题。
    C++程序员通常使用前置声明、分离实现等办法解决互相依赖问题。
  • 程序先用 Java 设计开发完成,再逐个类翻译成 C++。
    编码尽量遵守 Java 约定,
    每个类放到独立的文件,方法实现直接写在头文件的类声明中,".cpp" 文件基本都是空的。
    排除 ".cpp" 文件,文件数量就少一半了,嘿嘿~~
    简单场景还能用 C++ 模拟一下Java,复杂场景就只能用 Java 了。

优化3:双保险模式避免加锁

  • DANGEROUS双保险模式已经被证明是不可靠的,禁止在生产代码中使用。
  • UPDATE@齐楠 @宏江 指出, jdk 1.5 之后加上 volatile 关键字双保险模式是可用的。早期版本不行。
ConcurrentTrieNode* getChild(char c) {
    int const index = c - 'a';
    if (children[index] == NULL) {
        synchronized: {
            Locker locker(childrenLock);
            if (children[index] == NULL) {
                children[index] = (ConcurrentTrieNode*) calloc(1, sizeof(ConcurrentTrieNode));
            }
        }
    }
    return children[index];
}

语言细节

  • ConcurrentTrieNode 是一个简单 struct, 不包含虚函数和复杂对象字段,
    其构造函数只是简单地将所有字段(包括Lock)初始化为 0。
    使用 calloc(1, sizeof(ConcurrentTrieNode)) 直接分配一块 0 初始化的内存,
    calloc 返回内存地址时,已经得到一个合法初始化的 ConcurrentTrieNode 对象,
    而不必调用构造函数。

优化4: 原子操作避免加锁

  • 并发统计 count 时,将 count 字段声明为 volatile(),
/**
 * Word 出现的次数.
 */
volatile int count;
  • 使用原子操作实现线程安全并避免加锁(),提高性能。
// atomic_inc
__asm__ __volatile__(
        "lock ; " "incl %0"
        :"=m" (node->count)
        :"m" (node->count));

优化5:统计单词结束后再过滤排除单词

  • 程序要求排除一些单词,在统计前排除,每个分词都要判断一次。
    统计结束再排除,相同单词已经合并,减少判断,性能更高。

总结

  • 优化过程中曾想过各种方案,
    比如并发 merge sort 排序再处理,每线程一个 Map 最后再合并等,
    结果发现使用 ConcurrentHashMap 不但编程复杂度明显简单,性能还更加理想。
    再一次证明, 最简单的方案往往就是最好的方案 
    不仅从开发维护的角度来看,有时从性能角度来看也是这样。
    Java 的 ConcurrentHashMap 性能相当赞,并发环境首选啊。

  • 程序开始是用 Java ConcurrentHashMap 实现的。
    为了提升性能翻译成 C++,过程可谓大费周折,相当痛苦,我会告诉你我大半夜还在调 segmental fault 吗?
    C++ 没有 ConcurrentHashMap,实现 ConcurrentTrie 相对简单,所以选择了 Trie。
    很多同学采用 Java 实现性能也非常好,相当赞!

相关文章
|
弹性计算 大数据 关系型数据库
阿里云MVP学院首开,重磅干货内容流出
2018年5月19日,“阿里云 MVP学院”第一期正式开班,阿里云技术研究员小邪亲临现场致辞,之后由百阿班主任带领大家一起学习了阿里的文化建设。除此之外,最重要的就是技术干货以及各不同上云之路的探索与学习。
|
1天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1088 0
|
10天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3623 3
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
22天前
|
人工智能 缓存 前端开发
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
DeepSeek Harness + DeepSeek V4 Pro 项目实战保姆级教程!手把手带你从零安装开源 AI 编程工具,开发架构图、知识讲解网站、3D 网页游戏、全栈 AI 应用 4 个项目,覆盖运行模式选择、插件安装与开发,看看能不能对标 Claude。
13356 91
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
|
15天前
|
Web App开发 人工智能 API
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
DeepSeek Harness 精选插件推荐合集,从图片识别、浏览器操控、多 Agent 协作到手机远程控制,一口气带你看完 DSH 社区热门的十几个插件,覆盖技能扩展、UI 界面增强、整活玩法三大类,让你的鲸鱼变得更强。
1884 5
|
8天前
|
人工智能 监控 测试技术
Qwen3.8-Flash 来了,100万上下文、Agent、Coding 都加强了
8月26日,通义千问发布Qwen3.8-Flash-Next:125B参数、每Token仅激活6B,原生支持26万Token、可扩展至100万上下文;Coding、Agent与工具调用能力显著增强,面向真实软件工程任务,推动大模型从“回答问题”迈向“完成工作”。
|
11天前
|
人工智能 Linux iOS开发
Ollama使用教程:Ollama官网下载、Ollama本地部署大模型(2026最新)
Ollama 是一款免费开源的本地大模型运行工具,支持在 Windows/macOS/Linux 上离线运行 Qwen、DeepSeek、Llama 等主流开源模型,数据不出本机、隐私安全。提供 OpenAI 兼容 API,命令行一键拉取/运行/管理模型,无需联网,无调用限制,是开发者与 AI 爱好者部署本地 AI 助手的理想选择。(239 字)
|
16天前
|
人工智能 Java BI
【AI】DeepSeek Harness 安装、运行、管理插件
本文介绍了如何运行DeepSeek开源的Agent框架DeepSeek Harness(dsh)。主要内容包括:使用nvm安装适配的Node版本;通过代理加速克隆GitHub源码;使用pnpm安装依赖并启动项目;配置DeepSeek API Token;安装扩展功能的插件。该框架自带Web界面,支持模型适配、文件编辑等插件化功能
2103 1