(性能优化…我这一辈子都在做性能优化…)

1. 线程。不要啊不要自己做锁…并行计算首先要让被并行的任务尽量独立,然后只要把任务放进Thread扔出去,在主线程等待子线程完成任务就行了。
var thread = new Thread(worker.Execute);
thread.Start();
...
thread.Join();

2. 还是线程。然则,.NET的线程是昂贵的,据说一个线程启动需要100毫秒左右,还需要1M左右内存。所以,不能肆无忌惮地开线程…要用 BackgroundWorker …话说,为什么要设计出这种重量级的线程呢…

3. 容器。List不能赋值给IList,IList不能赋值给ICollection…设计这种傻逼容器框架的人真的会写程序吗?

4. 还是容器。List不是线程安全的,多个线程一边写一边读就会告诉你 InvalidOperation 因为内容被修改了;多个线程一起写就会告诉你 ArgumentError 因为某个线程可能以为容器空间够用而不去extend它直接往里放东西结果空间被另一个线程用掉了。好吧…可是,据说ArrayList是线程安全的…但多个线程一起写也会ArgumentError…

5. 日志。Log4Net很慢 …真的非常慢…所以只要把大量的log关掉,于是性能就好了…其实最大的优化是这个…

总之,感想是…真的有人用.NET这种东西做什么严肃的应用程序吗?这些东西…设计得也未免太弱智了吧…呃,想起来了,还有一个:

6. 还是日志。胡凯 说,做个小工具吧,统计一下日志里的信息…嗯,我觉得,那个工具应该叫 grep …一个连grep都没有的操作系统啊…

Dreamhost还是挺不方便的

November 16th, 2009

想装个 NewRelic RPM 玩玩,结果,装不上…Dreamhost他不允许Rails应用在后台起进程…

于是,只好郁闷地继续在自己机器上玩…

难度Dreamhost就这么残忍么?

(选自《 ThoughtWorks文集 》第14章)

性能需求采集的重要性经常被人们低估。在这一节里,我将尝试阐明几个重要问题:要度量什么?如何知道我们需要什么?以及如何得到确实有用(而非帮倒忙)的数据?

要度量什么?

最重要的性能度量点有两个:最大吞吐量,以及给定吞吐量下的响应时间。一个好的做法是:分别度量几种不同吞吐量下的响应时间,从中分析负载对响应时间的影响。如果响应的及时性非常重要,那么在确保满足响应时间要求的前提下所能达到的吞吐量可能就会明显低于最大吞吐量。你需要通过度量找出两项数据:当响应时间恰好可以接受时的吞吐量,以及达到预期吞吐量时的响应时间。伸缩性度量的关键则在于:随着数据规模、用户数量或者运行系统的硬件变化,起初得到的性能度量数据会发生怎样的变化。

可靠性的关键度量点是:当负载量高得超乎寻常,或者连续运行了很长时间以后,系统是否仍然正常工作。

如何设定目标?

要想知道系统需要达到怎样的吞吐量目标,你首先需要知道有多少用户会使用这个系统,以及他们的使用模式。用户会多频繁地使用某个功能?这个功能需要多快完成?

业务用户会知道这些问题的答案。你应该让他们明白,你会经常需要向他们咨询这方面的事。然后你应该建立一个良好的沟通流程,以确保信息的获取畅通无阻。

总而言之,你需要有一个可靠的流程与机制来获得所需的信息,使你及时获知支撑业务需求所需的性能指标。如果不经常去计算这些数据,就有可能最后发现你正在朝着已经过时的目标努力。

弄清当前需要负载的吞吐量之后,下一个需要考虑的就是响应时间。在结合UI考虑这个问题时,人们常会有钻牛角尖的想法:既然用户界面要在几秒钟之内响应,那么功能自然必须在更短的时间内完成。但事实并非如此。UI应该立即响应,告知用户:他们的请求已经得到处理;但实际的处理未必马上完成。在整个过程中,系统的其他部分应该照常工作。

响应时间的目标应该主要针对用户界面,并且数值越低越好。而且,不应该期望所有功能都能在同样的一段时间内完成。

如果对前面所说的还不明白,下面我将简单介绍一个采集性能需求的流程。

如何将性能测试融入日常开发流程?

理想情况下,项目组每周应该召开一次会议,确定当前的性能需求。参加这次会议的人应该包括项目经理、关注性能的客户、资深开发者、以及性能测试人员。如果某些性能需求明显无法达到或者完全不合理,开发者就能在第一时间指出。客户的参与是为了提供业务上的信息与知识,从而帮助判断需求的合理性。项目经理需要知道团队做了哪些决定,并提供一些方向性的指导。至于性能测试人员,他们显然应该在场,这样他们才知道需要测试什么。

接下来,你需要找到适当的讨论对象。开发团队需要从客户中找到一个联系人,与他一道决定性能需求,这样才能确保客户和开发者都清楚目标所在。不要把性能需求看作神圣不可侵犯之物,和所有需求一样,它们也应该是开发者与客户对话的起点,双方需要共同讨论决定最终的目标。

一旦需求确定下来,就能决定当需求得到满足时如何向客户展示,并对编写测试的工作进行评估和计划,就跟其他的任务一样。

程序员需要性能测试告诉他们什么?

开发者的需求有很多种,但背后的驱动力总是一致的:如果某段代码需要返工,他们就需要更多的信息来了解当时的情况。这些信息可能来自代码检查工具,也可能来自线程转储,甚至来自日志。他们可能需要知道数据库的忙碌程度,或是负载达到峰值时网络的忙碌程度。

试图预先回答所有这些问题可能并不划算,因为这会需要很大工作量。但我们可以做的是:当问题出现时,弄清哪些信息会有助于开发者解决问题,然后把获取这些信息的任务加到你的任务列表上,并告知客户。此时你就可以判断应该如何进行这些测试:是从此刻开始持续测试,还是只针对眼下的特定问题做一次性测试。

如果开发者的需求以这种方式在会议上提出的,那么所有人都将知道这些需求的存在。客户可以为这些需求排优先级,可以把它们纳入项目计划。最终性能测试将满足各方的需求:它让客户对正在开发的软件保持信心,它也能帮助开发者找到并解决性能问题。

找不到关注性能的客户怎么办?

如果找不到一个关注性能需求的客户,就会有几方面的风险。首先,正在开发的软件可能不符合业务要求,项目可能彻底失败。其次,不管最终的产品如何,客户都可能说它不符合要求,因为他们感觉开发团队没有征求他们的意见。第三,这可能会在团队内部造成紧张气氛,开发团队会觉得自己在被迫做不必要的工作,因为需求不是来自客户──不管项目经理的担心是否正确,这种想法都有可能出现,并导致必要的工作没有被完成,或是相反,开发者们浪费时间去做不必要的工作。

如果客户不懂技术又非要坚持不可能的需求该怎么办?

这种可能性总是存在:客户希望产品的性能达到某个水平,而达到这个水平是不可能或者不经济的。这时你就需要提出一些中肯的问题,把对话引导到真实的业务需求上来,从而打消客户不切实际的要求。

如果客户的要求是关于吞吐量的,可以考虑的问题有:每个工作日处理多少事务?这些事务的时间分布如何?是平均分布还是有明显的高峰期?每个周五下午会有集中访问吗?或者峰值的出现没有特别的模式可循?

关于响应时间,可以考虑的问题有:用户界面的响应时间会对系统的处理能力造成什么影响?能不能把界面与实际的计算操作分离?比如说,可能有这样一种场景:用户输入一些数据,然后进行较长时间的数据处理。此时用户不希望一直等到处理完成,而是希望立即输入下一段数据。所以这时合理的期望不是在一秒钟内完成数据处理,而是将用户界面与数据处理分离,让系统在后台处理前一段数据,同时让用户在界面上输入更多的数据。

以这种讨论方式,我们就能让开发者和客户共同寻找一个对业务价值有意义的性能水平,并且分清什么是当务之急、什么是锦上添花。我们都曾遇到这样的情况:在项目的现有条件下,客户急切希望的某个性能目标不可能达到、或是需要付出高昂的代价。如果相关的分析能尽早开展,客户就有可能在更早的时候做出决定,从而使这些目标成为可能。

如果客户期望的目标不能达成,他们会对最终交付的系统感到失望,哪怕系统其实足以满足业务需求。上述这些讨论有两方面的作用:不仅让开发团队了解客户的真实需求,而且让客户自己也有一个清晰的目标。这样一来,只要系统达到了双方认可的目标,客户就会感到满意。有这些讨论作为基础,客户就不太会坚持不切实际的期望;如果他们仍然感到失望,至少那也是出于合理的原因。

何不让业务分析师一并采集这些需求?

采集性能需求时不一定需要业务分析师在场,原因有几点:首先,此时功能需求的采集应该已经完成了;其次,即使业务分析师在场,开发者还是不能缺席,因为分析性能问题需要获得哪些信息只有开发者才清楚,也只有他们才能判断获得这些信息的途径和难度。性能测试人员应该提出前面介绍的这些问题,以此推动讨论进行,他们也能够判断每个需求是否容易测试。所以,当这些人坐在一起讨论时,业务分析师大可以把时间花在其他更有价值的地方。

小结

需求采集是为了让所有人都清楚:最终交付的产品需要有怎样的性能才能支撑业务目标。之所以要让客户参与,是因为他们最了解自己的业务,这样才能确保采集到的需求足够准确。而且通过讨论也能帮助客户清晰自己对性能的需求,从而有效管理他们对系统的期望。

2 sets of configurations:
  • A. 20 mongrel instances * 300MB memory limitation for each
  • B. 10 mongrel instances * 512MB memory limitation for each
Findings without surprise:
  • with config A, total memory usage is about 6000+MBytes
  • with config B, total memory usage is about 4000+MBytes
  • in normal cases, the momory usage of each mongrel instance is about 400MBytes
  • for operations with light calculation, config A is generally faster than config B
Findings with surprise:
  • for operations with heavy calculation, config B is more efficient than config A when load (amount of concurrent users) grows
  • more importantly, config B performs more consistenly than config A
    • config A fails 2 test cases for 15 concurrent users, and the possibility of failure grows significantly to 5.57% for 20 concurrent users
    • config B doesn’t fail any test case
    • the degrading curve of performance of config B is flatter than config A
Conclusion:
  • besides the memory usage, CPU usage is yet another bottle neck of performance
  • 20 mongrel instances on 1 server overloads the CPUs, and thus degrades the performance
  • reducing the amount of mongrel instances makes HAProxy works as a queue, therefore makes the system performs linearly
Suggestion:
  • prefer config B than A: 10 mongrel instances on each of 2 production servers can handle 20 concurrent users, which (based on my experience) effectively means at least 1000 users online AT THE SAME TIME
  • try config C (15 mongrel instances * 400MB limitation for each) and D (5 or 7 mongrel instances * 512MB limitation for each), IF NECESSARY

一年两度的performance review,现在开始的是2007年第一次。这是我在ThoughtWorks最喜欢的事情之一,因为有那么多人给我提意见,告诉我什么地方做得不够好。

关于ThoughtWorks的performance review,敏捷中国有一个讨论串值得一看:敏捷开发中的绩效管理。摘录我自己的一段话:

“大家都明白,指出别人的不足之处是为了帮助别人成长和自我完善,而不是互相打击。就我个人而言,溢美之辞我听得太多了,但除了ThoughtWorkers之外没有别的人能如此直接而准确地指出我的缺点,这些对我帮助非常大——我把上次review收到的批评意见打印出来,过去的半年里一直放在书包里,随时提醒自己。就像我经常说的,一个好的批评者是一笔宝贵的财富。”

那一次的批评意见,确实是前所未见的尖锐,刚才和Michael(Robbinson,我的sponsor)聊天也说到这件事。另外还说到的是“craftsmanship”和“journeyman”——在《软件工艺》整本书里,我最喜欢的两个词就是“reputation”和“journeyman”。我自己想做的,就是这样一个journeyman,旅行,学习,成长。我最想要的,是和那些master们一道工作,并在那之前做好准备向他们学习。

每次的performance review,总让我看看自己是不是还在这条路上。迄今为止,走得还算稳。