wordcount设计与优化

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

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

系统设计

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

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

优化1:同步 OR 异步

  • Reader 是 IO 集中型操作,其他计算节点都是 CPU 集中型操作。
    如果先读完文件再操作,读文件的这段时间 CPU 就白白空闲着浪费掉了。
  • 简单的优化就是异步读文件。
    增加一个后台 Task 线程,Reader 每读取一小块文件数据(Chunk),
    就交给 Task 线程处理,Reader 继续读下一个 Chunk 的时候,Task 已经跑起来了,
    一个占用 IO,一个占用 CPU,充分利用计算机资源。

  • 通过异步读文件,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 核数。

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

实现细节

  • 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 实现性能也非常好,相当赞!
时间: 2024-09-20 06:09:50

wordcount设计与优化的相关文章

遗传算法(C#),主要用于机械设计的优化

问题描述 遗传算法(C#),主要用于机械设计的优化 请问,有人有C#遗传算法的源代码么,或者下载地址也可以,谢谢. 解决方案 可以在百度网站的知道网页那里百度回来 解决方案二: 解决方案三: 参考

WOT博科聂小云:WLAN网络容量性能设计和优化

就在上周,由51CTO主办的WOTA全球架构与运维技术峰会在北京富力万丽酒店隆重召开.本次WOTA设置了15大前沿热点技术论坛,60+来自Google.LinkedIn.Airbnb.百度.阿里巴巴.腾讯等海内外一线互联网公司的技术大咖将带来超过50个历经沉淀的架构实战心得与成功经验分享案例,携手打造历时2天的行业顶级技术盛会. 大会第二天下午在以"网络性能优化实践"为主题的D会场, 博科(Brocade)公司网络接入产品部门的技术经理聂小云进行了主题为<WLAN网络容量性能设计

《路由设计的优化》一1.5 可扩展性

1.5 可扩展性 路由设计的优化 究竟拥有多少台路由器的网络才可以被认为是可扩展的网络呢?或许在网络拥有100台路由器时,就可以认为该网络是一个大规模网络,而且也能够证明该网络的设计师是一名合格的网络设计人员.但是,如果网络中的路由器超过了1000台呢?或者说网络规模与网络的可扩展性之间有没有什么必然联系呢?在探讨大规模网络的涵义时,应考虑以下场景. 某些网络很大(拥有上千台路由器)但是却不稳定,需要进行重大网络变更操作才能增加网元或调整网络拓扑结构,而且不进行重大变更操作就无法适配新的网络应用

《路由设计的优化》一1.4 可管理性

1.4 可管理性 路由设计的优化虽然网络怪才(geek)通常都不爱管事(这也是他们被称为网络怪才的主要原因),也不是管理者,但是作为网络设计人员,在考虑各种设计选项时,必须考虑网络的可管理性问题.这是因为如果网络的配置操作十分复杂,即便网络能够正常运行,那么也会让人在总体评价上大打折扣,而且在网络出现故障后也很难进行故障排查,甚至在网络出现新的需求而需要修改配置时也难以操作.一个易于管理的网络能够很好地适应网络规模的增长,而且在商业需求发生变化后能够很方便地进行修改和调整. 本书的目的不是要讨论

Hadoop集群环境下的网络架构的设计与优化

Hadoop集群环境下的网络架构的设计与优化 冯力 杨凯 杨帆 大数据时代,研究大数据的IT厂商把研究重心放在优化大数据系统软件架构.优化业务逻辑.优化数据分析算法.优化节点性能等方向,而忽略了大数据环境基础设置中网络环节的评估和优化.本文介绍了思科公司在Hadoop集群环境下的网络架构设计与优化经验.大数据Hadoop环境网络特性Hadoop集群中的各节点通过网络连接起来,而且MapReduce中的以下过程会在网络中传输数据. Hadoop集群环境下的网络架构的设计与优化

《路由设计的优化》一1.2 可靠性

1.2 可靠性 路由设计的优化可靠性和弹性是相辅相成的,弹性网络可以为网络应用提供更可靠.可稳定的运行平台,而检视一个高可靠性网络时,也必然能够发现其具备非常好的弹性能力.需要注意的是,不要将弹性能力与冗余性混为一谈,虽然冗余性能够在某些场合下提供很好的弹性能力,但是仅仅简单地在网络中部署冗余机制,是无法持续提高网络弹性能力的. 首先来看一下什么是可靠的网络,简单来说,可靠的网络就是能够为商业应用提供不间断运行的平台.那么商业应用的可靠运行又依赖于网络的哪些方面呢?当然是网络的可靠性和数据传送的

工信部将在五方面助推IC产业:做好顶层设计,优化重大布局

12月2日早间消息 在日前召开的"2016中国集成电路产业促进大会"上,工业和信息化部电子信息司龙寒冰副处长表示,集成电路产业发展的机遇和挑战并存. 从全球来看集成电路市场竞争日趋激烈,跨国领先企业通过强强联合加快产业链资源整合和战略布局,产业集中度进一步提高.从国内来看随着中国制造2025年"互联网+"等国家战略实施,内需市场活力将进一步释放,云计算.大数据等新业态蓬勃发展,新技术.新结构.新工艺带来巨大变革,为在新一轮竞争当中抢占制高点提高了机遇. 作为行业主管

《路由设计的优化》一导读

前 言 路由设计的优化当我们在1998年写作Advanced IP Network Design时,不曾想到我们会在将来越来越深入地研究路由式网络设计领域,也不曾想到我们会如此紧密地合作共事这么多年.当初写作Advanced IP Network Design的初衷是为了回答我们在Cisco 技术支持中心路由协议故障上报团队工作时遇到的一些问题. 在很多方面,写作本书的原因也大抵如此:帮助我们在日常工作中面对的客户回答他们提出的问题.比如,设计网络编址方案的最佳方式是什么?如何在不影响网络正常运

《路由设计的优化》一第1章 网络设计目标与方法1.1 网络设计目标

第1章 网络设计目标与方法 路由设计的优化进行网络扩容.网络设计或重新规划时,无论网络规模大小,首要考虑的问题是什么?是将要使用的链路类型.路由器类型,抑或是采用何种路由协议?我相信都不是!因为大家首要考虑的应该是网络的用途,网络应具备哪些功能特性?网络设计的目标是什么?网络应采取何种流量承载方式以最优化地满足商业需求? 1.1 网络设计目标 路由设计的优化由于在网络用户眼里,网络就是一系列应用的集合,而不是一系列线路.光纤连接器.协议.路由器和交换机的集合,因而在设计网络时,必须考虑网络为支撑