已经转轨到新项目有些日子了,这阵子主要focus在新项目的代码Debug上。Windows Message的一个问题着实让我这低手花了些时间。

首先得说明几点Windows在messages上的默认行为:

1. 鼠标位于NCArea内时,如果左键不按下,移动时触发的是WM_NCMOUSEMOVE;如果左键按下,移动时触发WM_MOUSEMOVE。从按下到松开,鼠标移动的消息从MOUSEMOVE变成了NCMOUSEMOVE,这个转变会触发TrackMouseEvent被调用。

2. TrackMouseEvent是一个很有意思的函数,又一次深 刻的意识到Platform SDK的函数还真是给我们很“方便”使用的(一句废话,“方便”之所以加引号是因为MSDN总是只给我们很少的几个关键句,总是在我兜了一大圈之后回头看MSDN,才感到这几句话的重要)。如果有人想订制诸如WM_MOUSELEAVE或WM_NCMOUSELEAVE消息时时,他需要handle WM_MOUSEMOVE或WM_NCMOUSEMOVE或WM_MOUSEHOVER消息,在消息中调用TrackMouseEvent或者_TrackMouseEvent函数(后者在comctl32.dll中)。设定好TrackMouseEvent的传入参数后,就可以安心等待接收Leave消息了。

一切看似平常关键是,什么时候发Leave消息?MSDN说了,当鼠标正在离开当前窗口,或者在一定范围内停留一定时间时发出。并且,只发出一次。

问:那么如果我鼠标左键在最小化按钮内按下会怎样?答:只要鼠标不移动位置,不会怎么样。

问:那么,如果我鼠标按住左键不放,移动鼠标会怎样?答:此时消息会从WM_NCMOUSEMOVE变为WM_MOUSEMOVE。

问:那如果此时我还只是在最小化按钮内移动,会怎样?答:那么对不起,一个不该发出的WM_NCMOUSELEAVE消息就发出来了,接着,因为你订制了最小化按钮的外观,那么它的样子就会从Pushed变为Normal,因为它“认为”鼠标已经移出了它的区域。

问:为什么会这样?答:因为在NCArea,鼠标按下与不按下时MOUSEMOVE消息是不一样的,这会误导TrackMouseEvent函数,当消息队列里下一个消息不再是上一个NCMOUSEMOVE,而是MOUSEMOVE时(事实上,任意一个不是NCMOUSEMOVE的消息都一样),TrackMouseEvent函数认为:恩,是时候发出MOUSELEAVE消息了(至于发出的是WM_MOUSELEAVE还是WM_NCMOUSELEAVE,完全取决于你的订制)。于是,杯具就不可避免的发生了,最小化按钮有了它不该有的显示状态。。。

到此为止,本文结束。不过有两个问题遗留了下来:

1. 这样的行为能够100%确定吗?不能。因为其中很关键的一点:鼠标事件从NCMOUSEMOVE变为MOUSEMOVE时,TrackMouseEvent真的认为是离开了区域并且发消息吗?不得而知。

2. 除了用系统(或者IE)默认的行为,或者不使用MOUSELEAVE,就没有办法解决这种情况了吗?有待确定。

OK. Time to go to bed. Tomorrow is another day.

现在居然如此的矜持
只想着自己如何拥有好的起点
一整天的忙忙碌碌
让我忘记了联络
但是心里居然出奇的踏实
只是因为我坚信你很爱我

前阵子在做一个接近实时的文本过滤功能,对于传入的文字文本,要能够根据以小组为单位的关键字组合进行过滤。

我把每一组关键字的最大数量限定为三个,允许有最多20组。于是乎,首先开始考虑用怎样的数据结构在提升过滤的性能。说老实话,现在的过滤如果一点不具备实时性,也基本可以考虑丢弃了。在Kris的建议下,接触到了Trie,了解了它的原理,也知道了自己的孤陋寡闻,呵呵。

当然,首先就是用数学验证Trie和最普通的循环过滤方式之间时间复杂度的差异:

假设一段文字长度为N(这里假设所有的长度单位都基于同一种字符集编码,比如16bits Unicode,也就是每2个字节记为长度1),假设要用一组三个关键字对其进行过滤,必须同组内三个关键字都满足,才标记为要过滤。用最普通的方法,就是扫描三遍文本,时间复杂度粗略为3N,当然,很有可能扫描不到完整的三遍,因为词语很可能在文本的中间就被找到,但是这种情况可以忽略,因为使用Trie在这个点上与之有同样的复杂度。

当我们使用Trie时,我们可以保证到,无论有多少个关键字,无论多长的文本,我们都只进行一次全扫描。这是由Trie高效的树形结构决定的(想知道为何会这样的,请参阅链接)。

在进行具体关键字匹配时,我采用一个一个字符比较的方式进行,对于两种过滤算法,在单一的关键字匹配上,拥有相同的时间复杂度,假设单个关键字长度为M,也总是<=M

因此,从总体上看,随着关键字数量和关键字组合数量的增加,随着文本长度的增加,Trie过滤方法会越发体现出优越性。当然,这也看通常情况下我们要过滤的文本长度,如果文本长度通常情况下都很小,那么用众多的关键字构造Trie树也是一笔不算太小的开销,性能上也不会体现出优势。所以也要视乎情况而定。

基本的问题解决了,但是后续的问题来了,也就是模糊过滤也叫(fuzzy filtering)。对于我想要过滤的关键字“AB”,假如在它中间插入了垃圾字符,例如“AXB”,我得有能力识别,重要的是,我要做到尽量保证在判断“AXB”其实为“AB”时,不会误杀,因为有可能“AXB”是有意义的啊。于是令我感到很头大的,语义,NLP,IR等等词汇就出现了,难道没有一种介于普通过滤和NLP之间的有效的fuzzy filtering方法吗?

找啊找啊找啊找。。。

问题来自于突然间对服务器的大流量访问,并且是多对一的,如何做到尽量降低那关键的一台服务器所产生的瓶颈?对这个问题的粗浅兴趣来自于公司内的一个Tech分享。

分享者深厚的经验当然不会全部向我们入门级的工程师介绍,只是为我们列举出了各种方案的优劣对比。

方案一:Share Memory 好处是,可以有很大的加速比(speedup radio),因为输入如果是1个bit,我可以根据具体需求,决定输出多少个bit。一个针脚对应一个bit,理论上,如果想让一个bit的输入放大到32个bit,只要做一个32针脚的share memory就OK,也就是可以做到32的加速比,很不错。但是,如果要100加速比呢,或者1K,会怎样?昂贵,并且resistancy & scalability差~ 经常插拔拥有100针脚的内存,阻力实在太大。

方案二:Fully meshed link / Fully connected network 全联通。一种适用于小规模负载均衡的模型。因为要建立fully meshed本身就是一个N平方级别的成本。链接两个节点的任意一条线路,都必须具备足够的带宽,不然完成一对一的传输必然减速。扩展性与冗余性确实很差,并且加速比不高。

方案三:Multi access bus 采用总线结构,多个节点链接于其上,好处是发送一组数据只要一个指令周期,相对的,比方案二要更能适应大规模流量。但流量不能太大,否则总线将因为自身各条线路之间的细微误差,导致bits顺序被打乱。实现起来还需要考虑不同节点本身带宽承受能力不同这类操作层面的问题。

方案四:Cross bar switch 很巧妙的采用了十字开关的问题在需要特定线路传输时将该线路打开。至于扩展,可以根据需要,增加更多的横向或者纵向线路,如果需要加速比更高,可以通过增加更多的输出线路来达到目的。开关由一个scheduler来调度。那如果十字线路太多了,不是也出现了bit位到达的时钟不同步问题了吗?是的。解决方案是将十字线路简化,用多个链路更少的小十字线路代替一整个大十字线路,并将这些小线路对其垂直放置,还由scheduler调度,每一次调度位于同一位置上的各个层内部的开关。原理类似于硬盘的磁头与磁盘。如果流量还需要扩展,还可以对scheduler进行分级,可扩展性相当出色。

总结:并没有完美的东西,只有适合的东西。根据应用场景的不同,我们需要选取或者创造出好用的技术。写出以上概念实则防止遗忘,因为今日的讲座确实很棒。联想起硬盘的发展历程,先后经历了从串行,到并行,再到串行的年代,其实就是一个合久必分,分久必合的过程。也想起了事物的发展总是螺旋式的上升过程,简略的感慨之。闭

十分兴奋,自己付费的就是感觉自由。说实话,平均算下来,还真不贵。平日生活里,随便节省一小笔无意的开销就足够了。

由于工作忙,只得等到晚上再慢慢整理。

It’ll be my place.