原文檔見:http://gitlab.alibaba-inc.com/middleware/coding4fun-3rd/blob/master/observer.hany/design.mdnode
系統設計
按照題意,可設計以下簡單拓撲圖。
程序員
優化1:同步OR異步

經過異步讀文件,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@齊楠@宏江指出,jdk1.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:統計單詞結束後再過濾排除單詞程序要求排除一些單詞,在統計前排除,每一個分詞都要判斷一次。統計結束再排除,相同單詞已經合併,減小判斷,性能更高。總結優化過程當中曾想過各類方案,好比並發mergesort排序再處理,每線程一個Map最後再合併等,結果發現使用ConcurrentHashMap不但編程複雜度明顯簡單,性能還更加理想。再一次證實,最簡單的方案每每就是最好的方案,不只從開發維護的角度來看,有時從性能角度來看也是這樣。Java的ConcurrentHashMap性能至關贊,併發環境首選啊。程序開始是用JavaConcurrentHashMap實現的。爲了提高性能翻譯成C++,過程可謂大費周折,至關痛苦,我會告訴你我大半夜還在調segmentalfault嗎?C++沒有ConcurrentHashMap,實現ConcurrentTrie相對簡單,因此選擇了Trie。不少同窗採用Java實現性能也很是好,至關贊!