尝试1:一个幼稚的方案
让我们来进入下一步,解决这个问题。我们会从一个很直接的方案开始:就是保持一个有时间戳的“事件”列表:
然后我们就可以根据需要计算最近事件的个数。
这段代码易于理解吗?
尽管这个方案是“正确”的,可是其中有很多可读性的问题:
·for循环太大,一口吃不下。大多数读者在读这部分代码时会显著地慢下来(至少他们应该慢下来,如果他们要确定这里的确没有bug的话)。
·MinuteCount()和HourCount()几乎一模一样。如果他们可以共享重复代码就可能让这段代码少一些。这个细节非常重要,因为这些重复的代码相对更复杂。(让有难度的代码约束在一起更好些。)
一个更易读的版本
MinuteCount()和HourCount()中的代码只有一个常量不一样(60和3600)。明显的重构方法是引入一个辅助方法来处理这两种情况:
在这段新代码中有几件事情值得一提。
首先,请注意CountSince()的参数是一个绝对的cutoff,而不是一个相对的secs_ago(60或3600)。两种方式都可行,但是这样做对CountSince()来讲更容易些。
其次,我们把迭代器从i改名为rit。i这个名字更常用在整型索引上。我们考虑过用it这个名字,这是迭代器的一个典型名字。但这一次我们用的是一个反向迭代器,并且这一点对于代码的正确性至关重要。通过在名字前面加一个前缀r,使它在如rit!=events.rend()这样的语句中看上去对称。
最后,把条件rit->time<=cutoff从for循环中抽取出来,并把它作为一条单独的if语句。为什么这么做?因为保持循环的“传统”格式for(begin;end;advance)最容易读。读者会马上明白它是“遍历所有的元素”,并且不需要再做更多的思考。
性能问题
尽管我们改进了代码的外观,但这个设计还有两个严重的性能问题:
1.它一直不停地在变大。
这个类保存着所有它见过的事件——它对内存的使用是没有限制的!最好MinuteHourCounter能自动删除超过一个小时以前的事件,因为不再需要它们了。
2.MinuteCount()和HourCount()太慢了。
CountSince()这个方法的时间为O(n),其中n是在其相关的时间窗口内数据点的个数。想象一下一个高性能服务器每秒调用Add()几百次。每次对HourCount()的调用都可能要对上百万个数据点计数!最好MinuteHourCounter能记住minute_count和hour_count变量,并随每次对Add()的调用而更新。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论