码迷,mamicode.com
首页 > 其他好文 > 详细

简洁和高效是我们永恒的眷恋

时间:2015-11-28 21:39:15      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:

0x00 申明

本文并非水文,不会对Go语言做任何推广性宣传,只是陈述一个事实!

0x01 缘起

对 于我们猿(媛)类来说,写出的每一本程序都希望是:对于阅读代码或者维护代码的人是简洁的,对于服务方来说是高效的!简洁和高效这两个词,自从计算机出现 那一天起,一直都是一个至高无上的目标。个人认为面向对象提出来的“可维护性,可扩展性”只是“简洁”的引申义,而“可靠性,稳定性”则是“高效”的引申 义而已。
不知道别人是怎么认为的,至少我是一直这么追求的。所以几乎每次打开电脑第一件事都是关注一下业内新闻趣事,总希望能在万花丛中,找出自己欣赏的那一朵 (因为自己渣渣,种不出花来)。大概是2011年,在国内第一次听说Go语言(事实上2009年就诞生了),那个时候四月份平民(真名不知道)他们一帮子 在积极的学习研究Go语言,翻译国外相关文献(一个小插曲,Go语言里的chan关键字,他们本来翻译成频道,我觉得太不形象了,建议修改成通道,后来就 一直延续下来了),我也因为她的显著特性而臣服,把玩了一段时间后,因为那个时候她还没有成熟,标准库功能又弱,加之她“蹩脚”的臭脾气(语法),非得大 家去迎合她,不像C/C++那样,随意摆弄,你要不怕被同事们拍死,完全可以写出只有空格一种字符的程序来!后来就远离了她,直到去年,一次意外的邂逅, 发现她已经出落的如此美丽可人。

0x02 问题

如 何才能最大程度上挖掘服务器的处理能力?答案显而易见:并发。并发原语从unix诞生那一天起经过三代进化,分别是多进程->多线程->多协 程,协程概念不清楚的,请自行脑补。如今各大语言工具都支持这个概念,然后实现难易繁简程度各有不同,下文着重比对C/C++与Go之间的差别。
实例:并发100万协程,每个协程处理从0累加至10000,然后阻塞
关注结果:1:并发100W,所需的时间 2:每个协程处理是否正常
(有的童鞋会奇怪为什么每个协程要处理从0累加至10000,有什么意义? 我是这么设计的,个人认为每一个功能从开始到结束,平均大概需要10000个指令周期,这么测,肯定不可能覆盖所有案例,但足以说明一个大概了)

0x03 手起

测试环境: centos 7
内核版本号: 3.10.0-229.20.1.el7.x86_64
CPU: 24核 MEM: 32G
Go语言版本号:1.4.2

Go测试代码

  1. package main
  2. import (
  3. "net/http"
  4. "runtime"
  5. "runtime/debug"
  6. "runtime/pprof"
  7. "time"
  8. )
  9. var maxRoutine int = 1000000
  10. var quit = make(chan struct{})
  11. func routine(index int) {
  12. // println("OK,Over I‘m routine NO. ", index)
  13. sum := 0
  14. for i := 0; i < 10000; i++ {
  15. sum += i
  16. }
  17. // println("routine No.[", index, "] sum=", sum)
  18. <-quit
  19. }
  20. func handler(w http.ResponseWriter, r *http.Request) {
  21. w.Header().Set("Content-Type", "text/plain")
  22. p := pprof.Lookup("goroutine")
  23. p.WriteTo(w, 1)
  24. }
  25. func main() {
  26. println("threads : ", debug.SetMaxThreads(maxRoutine))
  27. runtime.GOMAXPROCS(runtime.NumCPU())
  28. go func() {
  29. tick := time.Tick(10 * time.Second)
  30. for {
  31. select {
  32. case <-tick:
  33. func() {
  34. runtime.GC()
  35. }()
  36. }
  37. }
  38. }()
  39. println("test max routines!")
  40. var i int = 0
  41. begin := time.Now()
  42. for i < maxRoutine {
  43. i++
  44. go routine(i)
  45. }
  46. println("test max routines end. escape time:", time.Now().Sub(begin).Seconds())
  47. // select {}
  48. http.HandleFunc("/", handler)
  49. http.ListenAndServe(":9090", nil)
  50. }

C/C++测试思路简述:

统 计平台CPU核心数,根据核心数开辟等同的线程数,每一个线程绑定在一个CPU核心上,然后再在线程上开辟大量的协程来处理事务。在目前测试环境下,总共 24核,开辟24个线程绑定在24个核上,每个线程开辟4166个协程,第一线程开辟的协程数为:41682,最后让每一个协程处理累加事务,统计测试结 果!
(具体代码测试结果,随后补上,这一阵子,有点事,没空整这个)

0x04 刀落

测试结果:

程序运行中协程数:
技术分享

创建100万个协程所需要的时间(单位:秒):
技术分享
程序创建完成的协程是否仍然正常运行,根据打印出的日志可以看出是正常运行的,因为是并发,量大且凌乱,在这里就没有贴出图来。

说明

  • 本文的一次验证为了下一篇实现C1M(connection one million)级打下基础
  • 若清除去运行时监测代码,代码总量仅为50行,流程非常清晰
  • 两版代码相比,难易繁简度立分高下,更重要的C/C++实现的那一版,就看看思路就觉得心力交瘁,尚且不说这里面还有很多需要细致打磨的地方了

0x05 题外

  • 按照现行的主流服务器配置一般情况下,一个功能处理时间都该定界在10微秒以下,上到几十,几百微妙,就该考虑优化了,如果你跟我提建立一个数据库连接就需要几百毫秒了,那这就钻牛角尖纯扯淡了
  • 一台服务器的并发数到底能有多大,这个具体数字准确的讲是要依赖于你的应用到底占用了多少资源。在一个服务尚未实现,但又想知道一个概要值参考的 话,可以查看/proc/sys/fs/file-max这个文件里的数值(当然这个值是可以修改的),这个数值的默认值是系统根据标准堆栈数值以及硬件 资源自动计算出的一个数字,实际应用中可以根据具体情况做适当的调整。

0x06 责任

我们应该竭尽全力让各种资源发挥它应有的光芒。

简洁和高效是我们永恒的眷恋

标签:

原文地址:http://www.cnblogs.com/sunsc/p/5003423.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!