2010-12-12

三体杂评

  三体是年来看过最好的科幻小说系列.
  没有之一.
  因为这确实是许多年以后,才又第一次看起来科幻.

  从在某年某月某日把倪匡的除木兰花系列的所有科幻都看完之后,就再没碰过科幻小说.

  当然,这不是说倪匡的作品已经前无古人,后无来者了.
  只是说,那时候,知道的东西渐渐多了起来.
  于是,没有了童年,也自然没有了科幻.

  所谓科幻,如词所表达的.
  即是科学,又是幻想.

  它的魅力在于可有可无之间.

  当年着迷的倪匡便是如此.

  许多年过去之后,就没有碰过国内的小说.
  一部分原因是如四姑娘的阴影.
  
  当看到幻城末尾最有深度的人物刻画来自于古龙小说之后,便对国内小说水平从此绝望.

  当然,这里也不是要谈幻城,也不是要谈郭总.
  
  从三体3上市开始,twitter和reader里便不乏相关的消息.
  timeline上剧透和反剧透的在互相较量.
  
  此时的三体于我来说,毕竟不过是一本比较热门的书.
  即便,在一些比较"高端"/"成熟"/"理性"的圈子里,也有不少对刘慈欣的赞誉.
  但,也仅限于此.
  
  甚至于当同事看完三体3兴奋地向周围推荐的时候,植根深入的对国内小说的悲观感让我回了句:
  "都几岁了,还看科幻".

  在这本书被传阅几个人之后,终于也还是有了点兴趣.
  
  一句题外话,这便是social network的魅力/营销.
  所谓的口碑营销和virus便是如此.
  从一个节点外延扩散,然后形成爆炸趋势.

  找回了三体前两部来看.
  感觉与其说是科幻小说,不如说是科普小说.

  三体一谈论的是混沌.
  借用三体有些的发展来描述科学探索方法的演变.
  从纯经验,到纯理论,到理论结合试验.
  
  当到达现代物理的时候,混沌的出现以及不可观测性造成数学上的不和谐之后.
  刘慈欣把"智子"引入了进来.

  这是一个很有意思的想法.
  用这个来解释混沌和理论物理的各种不完美.
  犹如书中提到的靶子的例子.

  某种程度上说,这是一个有神论上帝式的科幻观点.
  规律还是存在,只不过在于一个观察不到的"维度"上.

  虽然这个科幻观点深入下去有些索然无味.

  三体一另外一个有趣的地方在于对文革的描写.
  对于纯幻想小说来说,很少会纠结到现实.
  
  而刘慈欣对"现实"的执着使我在翻开前几页的时候因为这是一本描写文革时期/知青的书.
  当然,也许这也是三体让人有"代入"感到理由.
  利用现实拉深科幻的层次.

  相对于三体一着眼于方法论,黑暗森林则更偏向于哲学/世界观层面.
  有一点博弈的感觉,但更多的是对于世界观的设置.

  从内容深度上说,三体二没有第一部那么"科幻".
  因为它已经超越的纯粹科幻的场面,而致力于营造一种世界框架.
  即所谓的黑暗森林体系.

  所有有点"科幻"味道的描写都在于补充强化"生存",这个第一法则.

  三体三则是对前两部的一个补充和扩展.
  从维度上进行的扩展.
  只是,想表达的东西有些模糊.

  似乎主要只是把黑暗森林法则进行扩展.

  于是,从这个程度上说,三体其实只有后两部在理念上是合一的.
  第三部跟第一部的联系也许只在于对一些前沿理论的科普式宣传和描绘.
  尽管,有些看起来比较玄.

  比如关于维度展开和跨越,智子的制造方式,四维宇宙的描述和维度坍塌.

  当然,对于量子/前沿理论物理我所知的并不多.
  对于维度的理解也只是限于它数学上的纯粹维度.
 
  比如描述宇宙需要11个基本变量,便是11维度.
  于是,对于"维度坍塌",能想象到的也就是"投影".
  "坍塌的维度"也即是被限制了的那个投影维度.

  所以,对于坍塌的景象,也许只是一个观测上的不同而已.

  回到主题.
 
  对于三体系列,从某些方面来说确实承担得了那些赞誉.
  毕竟,对于目前以"毫无根据的胡扯"居多的中文小说来说,算是一缕曙光.

  借用某人的一句评价就是:
  "还可以,就是有些地方太罗嗦."

  其实,作为科幻小说,能提供给人一些有理有据的白日梦就已经是成功.    
  只是,像三体这样没有明显硬伤的小说似乎已经越来越少了.    

2010-12-05

Java Class.cast的问题

  以前没觉得java的类型系统有什么问题.
  不过就两条机制在,box/unbox的问题而已.

  当然,接触java的时间不算久,所以没有经历过那段没有autobox的日子.

  不过,从一些例子看来,autobox也未必是好事情.
  比如这段代码

  int n = 0;
  int another = int.class.cast(n);


  从表面上看来,这应该是一段很符合表面语义的代码.
  一个类型转换,而且应该是没什么问题的.

  只是,由于cast的函数声明参数是一个Object.
  而在java里,primitive和object是两套类型系统.
  
  在java引入autobox之前,这段代码应该是会编译错误的.
  但是,有了autobox之后,没有编译错误,但是肯定会有运行错误.

  在调用的时候,java隐式地将int box成java.lang.Integer.
  
  想想,既然有autobox,那么应该也问题不大.
  最多返回Integer之后,再unbox隐式转换回int.

  但问题是,cast做了一些类型兼容的检测.
  从jvm的角度来说,int和Integer是两个不兼容的类型.
  于是,在调用cast的时候,会抛一个ClassCastException.

  也就是说,在java两套类型系统存在的前提下,
  primitive.class.cast()的行为就变得有些古怪了.
  
  一些语义上看上去无误的代码,也许运行时就必然要抛异常.
  
  当然,对于这类可以显式调用的也许可以避免.
  但程序复杂了就难免会有这样那样的trick/trap.

  比如:

  void  Type method(Class type){
       ....
      return type.class.cast(value);
  }
  
  这段代码.

  初看上去似乎还行.实现了某种程度的模板/复用.
  但是,注意那个cast.
  
  所以,很难保证在实际情况下不会出现这种box/unbox带来的cast的问题.

  于是,解决方案就是尽量避免使用cast做转型,或者时刻记住box/unbox的问题.
  
  问题是,如果不用cast转型就要自己写个检测null的东西了.
  这当然不是什么大问题.

  最好的方法估计还是统一一下类型系统吧.
  做接口的时候也许最好是使用非primitive的类型吧.
  这样或许能够避免一些问题.
  
  虽然在诸如做序列化的时候,非primitive类型会有一些额外开销.
  不过,真要做序列化的话,这些开销也是可以避免的.

  比如,最直接的一堆if,else做对应的类型映射.
  
  总而言之,java的两个类型系统是个头疼的存在.
  不仅存在陷阱,还使得在做reflect的时候经常让人崩溃.

  在这方面,后来者C#做得貌似就好得多了.

  比较,Java老了.

  语言这东西,也许不是说年代越久越成熟.

  毕竟,需要解决的总是新问题.

2010-11-28

发掘用户的支出成本

  周末在看一本讲犹太人的书.
  里面的一些内容带来的联想比较有意思.

  犹太人的经商理念里,借贷似乎是一件很自然的事情.
  从现代经济的概念来说,这是杠杆行为.
  但是,从另外一些角度来考察的话,似乎也能挖掘出一些东西出来.

  事实上,不论是所谓的杠杆,还是直接的借贷,本质上说,就是将一件东西从一个地方转移到另一个地方.
  回归到人身上,并且局限点说,就是把一个人暂时不用的/处理优先级比较低的资源转移到急需的/处理优先级比较高的人手中.
  
  就借贷来说,是从资本充裕到资本急需的转移过程.
  换句话说,这是一个转换/汇聚到过程.

  不考虑借贷双方资本数量级的关系的话,借贷本身是一个资本重定向/重定义的过程.

  简单点说,就是把资本从一个形式转换为另一个形式.
  
  这个的意义在于,如果把它推广开来,其实能在很多地方找到相似的痕迹.
  比如腾讯QQ的在线时间累计等级体系.

  用前面的话说,就是把用户的时间"借"过来,沉淀成为量化的"固定资产",然后进行资本形式的转换,变成一种会员体系,回过头来"贷"回给用户.
  
  其中的重点在于,把零散的时间收集起来,变化为价值.

  从用户层面上看,某种程度上来说,这是一种把支出收益比最大化的过程.
  在没有会员成长体系之前,付出的在线时间是0回报.
  而又了之后,价值在于所谓的QQ等级.

  虽然实际上,这种收益是腾讯认为制造出来的.
  但不可否认,这是一种一定程度上的双赢局面.

  延伸出来的一个话题就是,如果发现这种最大化收益的方式呢?

  回溯到QQ会员上.
  可以看出的是,关键点在于找到支出项.
  或者说成本项.

  就像做理财,要优化收支状况,先要知道收入支出的的详细情况.
  
  对于做产品来说,就是在收入不变的前提下,发现支出状况.
  同时改善之.

  于是,当把用户在线时长考虑到支出项目之后,就很容易有把支出变为收入的思路.

  换句话说,就是想办法让用户的每个操作的价值最大化.
  
  想想WOW的成就系统,其实差不多也是这个思路.
  把用户的各种零散行为汇聚提炼,尽可能地将之变回可视,可衡量的东西.
  认为地制造出价值所在.

  这是第一次层面.
  也就是从用户角度的增效投入产出比.

  第二层面应该是对service provider的实际利益来说的增效.
  WOW的成就系统通过设置阶梯式的成就,由简入繁地"培养"用户行为,从而将这些行为成为游戏的一种玩法.
  从另一个层面推动游戏本身的多元性而间接提升游戏价值.
  QQ的会员体系则是通过反向促进在线时长增加了用户粘性,从而进一步回馈回整个QQ生态系统.

  可以看出,这是一个类似螺旋型的推动方式.
  先推动用户观感上的投入产出价值的提高(等级/成就),然后在提到一个程度之后,介入.
  用一句白话说,就是设个套,入套之后圈之.

  因为一个普遍的理由是,人们对于已经获得的东西,通常第一观感是继续持有.
  所以,对于等级制度/成就系统,大多数人在力所能及的范围内,将一直追入下去.

  所以,根本的在于如何发现支出,并尽可能把它转换为收入.  

2010-11-22

谈谈通胀以及其他

  中国的经济是个很神奇的存在.
  
  一方面对外出口产生巨大逆差,美国人喊着要人民币升值.
  另一方面国内却物价飞涨,已然是通货膨胀的姿态.

  贸易顺差产生的财富貌似不能被国内自己消化.
  加之比较严格的外汇管制,创汇回来的美元需要转化为等值的人民币才能进入正常的国内流通渠道.

  于是,结果就是随着外汇继续创新高,印钞厂的工作流水线也跟着创新高.

  一般来说,在这种贸易顺差的情况下,由于一国的自我消耗循环,从长远来看,是会自动平衡两国间贸易出入和货币情况的.
  理由是,在充分自由的前提下,顺差要么沉淀为国民财富,要么成为流通中剩余的部分,从而因通胀带来变相的贬值,导致贸易中的差额优势减小.

  中国目前的出口贸易还主要是劳动密集型,在产业纵深度上层次比较浅.
  换句话说,出口企业的创汇很难通过产业链带入到其他行业,从而形成比较健康的资金循环.
  
  创汇产生的财富,还是主要集中在个人/小团体上.

  如此巨额的财富是很难通过个人的纯消费行为进行消化的.
  而产业的纵深感不强,也阻碍了其对资金的消化能力.
  于是,便产生了目前所谓的莫名热钱.

  中国经济系统里,比较缺乏各种可靠和有效的金融手段.
  于是,剩余的资本既不能通过直接反馈回各行业中,也不能通过金融手段间接地作用于市场.
  
  那么,囤积的资本如何处置?

  想想高热的房地产市场或许能找到答案.

  除了金融手段之外,最为保险的保值方式莫过于投资于不动产.
  既有抗通胀的天然优势,有不失为一个合适的增值投资途径.

  于是,可以理解为何如今的房地产行业如此火热,资金不断.
  即便是有所谓的调控手段在,也阻碍不了高企的房市.

  究其原因,还是这股热潮并不是由一般消费需求导致的,而是由于巨额资金的消化不了所致.

  回观央行的一系列政策,包括准备金上调和加息.
  这些,相对于正趋火热的地产市场的收益比来说,是很难拉动资金回头的.

  另一个也许可以说明问题的就是,几次对于其他物品的物价哄抬行为,表明着这股自己在寻找除了房地产之外的消化渠道.
  或者说,房地产不过是一种选择之下的表象而已.
  即便说能通过及其严厉苛刻的手段限制房价/地产发展,也不能避免出现第二个"楼市"情况.

  因为其根源在于无法消化的营养过剩.

  那么,普遍程度上的物价上涨和通胀味道如何解释呢?
  这也许不过是经过漫长的消费扩散之后,资金终于有一部分反馈回正常流通领域的结果.
  就好象说,一股洪流结果蜿蜒曲折的途径之后,终于有一部分变成细流回归到各个田地.

  也就是说,"财富"的某种程度上平衡的结果.
  从少数人手上接过多次流通交易,对宏观流通量产生的微弱影响.

  如同当初迅猛的房市一样,如今物价的上涨不过是一种比较缓和迟缓的上升房市而已.

  如果找不到缺口,无限期的上涨是可以预期的.
  
  但这并不意味这回发生通胀或者说货币贬值.
  
  至少,在相当一段时期内,这种情况还是不太可能发生的.
  原因在于,这种流通消化方式的增长速度相对缓慢.
  也就是说,明显的货币增发感需要一段比较长的时间才能完全反馈回市场.

  加上掌握资本的少数人无时无刻不在寻找投机机会,进行再次的财富聚累.
  其结果最直接的便是财富从穷人手上转移到富人手上.

  通过资本话事权进行市场的投机操控,从而吸引其他资本进入,然后快速推出.
  这是一种劫掠式的财富增长方式.

  从宏观层面上说,就是越来越多的财富掌握在越来越少的人手上.

  把这种非平衡的分配方式推向极致呢?
  
  假设政府还存有控制能力的话,自然最后会对财富入口进行限制.
  也就是就以抑制出口的方式,用一个比较长的萧条时期来消化过多的财富.
  自然,前提是还有能力控制.

  当然,如果"乐观"点,物价上涨速度加快,形成全面性的通胀,从而从一定程度上宣告货币贬值,也许也不失为一个快速消磨资本的方式.
  毕竟,在一个投机的巅峰时刻,通过突然对资本进行稀释,也能起到相当范围内的效果,尤其是对"中小"规模的资本巨头.
  原因是,按比例稀释的话,这些资本也许就不能够很好地维持资金链,从而因为暂时的断裂而引起多米诺效应.

  当然,这个时期必然不能太长.因为贬值的另一个结果就是可能加剧资本的体积.
  贬值一定程度上是加剧了贸易顺差,从而引入更多的不良资金.
  这样只会加剧情况,使得物价无止境地上扬和失控.

  而且,巨大的吸金能力可能不单是影响中国经济了.
  各路资本的嗜血性很可能将其演变成为一个全球性的资本灾难.
  
  源源不断,不顾后果地流向中国.  

2010-11-09

厮混

  好久没写东西了.
  
  原本在某个独立博客也写了些,但是不稳定的VPS导致数据丢失,也因此罢了折腾的想法.
  想想,还是这里舒服些.
  虽然多少有些限制.
  但,对于不怎么折腾的话,大概也就如此了.

  直觉上,最近思绪一直比较乱.
  许久没有新的东西刺激.
  而当有新鲜感的时候,会很快觉得疲倦.

  也许这就是所谓真正老了?
  毕竟,也不算是什么年轻人了.

  辗转地,回到了毕业最初的公司.
  外面绕了一圈,也确实没有比这里好的了.

  也许是所谓屈服.
  潜意识地承认世界上没有完美的东西.

  尽管,直觉上,完美这个词本身就是个瑕疵.
  只是,更多地,追求完美是一种精神,或者说境界.

  所谓的超然,其实就是把不可能当作目标而形成一种观念.
  或者说动力.

  毕竟,耐性有时候因为着消磨.
   
  有个词叫水滴石穿.

  时间可以磨灭很多东西.
  包括好的以及不好的.

  有时候,看到大学班群的名字,才会蓦然想起,大学是什么年代的事情.
  其实,这或许并不长久.
  只是,许多人,许多事都在悄无声息地改变.

  某年某月某日,群里还在荡漾着各种企业的传说.
  又某年某月某日之后,群里面依然荡漾着各种企业的传说.
  只不过前者有着光环,后者的更多是风华的背后.

  这或许便是事实.
  永远是充满许多想笑,却无力去笑的现象.

  能做的,只是努力保持这种笑意.

  世人皆醉我独醒,或者世人皆醒我独醉.
  这并无所谓.

  一个人活着,能在乎自己的意志,这本身已经不是一件容易的事情.
  尤其是当越入世,则愈难出世.

  因为羁绊,不仅仅来自于自己.

  想想,人活着是为什么,这个被无数人嗤之以鼻的话题.
  
  无他,聊以培养睡意.

  到底是年龄大了,还是神经衰弱?

  为伊消得人憔悴也不过是因为熬夜睡眠不足导致罢了.

  如此而已.

2010-06-16

再谈Redis

  
  redis的hash/key lookup的实现就是在redis.c的lookupKey里.
  里面除了hash的查找过程dictFind之外,还有一些关于swap/vm的内容.
  暂且按下不表.
  
  redis的hash表"实现"是dict这个数据结构,在dict.h里定义.

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */
    int iterators; /* number of iterators currently running */
} dict;

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

  可以看出,redic是用了两个数组做hash table.
  
  至于为什么要用两个hash tbale,这个可以从dictRehash函数里看出.
  在做rehash的时候,其实是把ht[0]的所有内容移到ht[1].然后再把两者交换而已.
  
  至于什么时候以及为什么要rehash,一般是在查key的entry的时候,也就是_dictKeyIndex这个拿table index的时候,会看当前table里元素的大小.
  扩容的策略很简单,double.

  接下来看下table的分布情况.
  对于key计算出来的hash值,_dictKeyIndex先会将其跟table的sizemask做一个掩码.
  sizemask的值其实是table的"size" -1.
  
  由于table是按幂级数扩容的,于是sizemask上基本就是说,保留hash值的低级位的bit而已.

  得到计算出来的掩码之后,就以这个计算出来idx作为table的index.
  
  事实上,hash table数组里的值是一个链表.
  
  换句话说,其实redis hash table的分布方式类似内存的分页.

  用sizemask找出key所在的页,然后直接loop这个页的内容.
 
  于是,单对查找时间来衡量的话,当然是页内的东西越少查找越快了(因为是线性查找).
  换句话说,对于32bit的hash值,出去sizemaske的bit越多,查找理论上会越快.
  也就是说,当table越大,hash表越大的时候,查找效率会越高.
  当然,这也只是理论上的.
  
  而且,是在一定数量范围内才成立.

  因为redis毕竟是基于内存的,当数量超过可用内存的时候,自然效率不能担保.
  还有就是redis做save的时候(save到硬盘是通过fork clone一个进程做到的),也会占用一部分.

  所以,在处理redis的时候,需要考虑好内存的使用状况.
  不然out of memory的问题就麻烦了.

  至于redis的vm选项,貌似当开启的时候,不是用的内存而是用的swap或者说文件来存储的.
  这个可以在 lookupKey里看出点端倪.

  但vm选项开启,并且,第一次lookup出来的值没有REDIS_VM_MEMORY标记的时候,是从文件load进来的.

  至于,什么时候swap出去的嘛.
  还没看明白.

2010-06-12

浅谈Redis

 
  redis的源代码(v1.3.14)应该算是挺简单的.
  "核心"部分就在redis.c,大概也就1W多行,不多.
  除去各种命令的处理,整体框架只有几千行吧.

  基本的处理流程在aeMain里.

  当然,在此之前,会先load数据.
  毕竟,这是一个基于基于内存的key-value.
  也所以呢,这成为它的第一个问题.
 
  appendonly模式下,是通过读log来恢复数据的.
  实际上,log的内容大概就是命令的内容,制造了一个fackeclient将log的内容重放一边.

  在非appendonly模式下,则是读数据文件,一个个都add回去.

  两者的不同在于,明显,非appendonly的文件必然是某个时期的数据快照.
  而log,只能说是某一段时间的数据操作过程吧.

  也就是说,如果停机回复的话,大概在log和数据文件上要小心处理.
  当然,省事的情况就是只load快照文件.

  不管是何种情况,可以肯定的一点是,启动时间跟数据量成正比.
 
  启动完成之后会在aeMain里面loop.
 
  redis是事件风格的,或者说是poll风格的.
  也就是说,每个命令会形成一个事件,然后如队列.
  在一次循环过程中poll出来处理.
  完了再下一次的从队列里select ready的event.


void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
  }


  处理的基本主题在aeProcessEvents里.
  当然,更直接的是在aeApiPoll里.
  
  apiPoll里面会涉及到具体的socekt操作.
  其实也并不复杂,就是把ready的file descriptor放到read write的ready列表里.然后设置相应的事件标记.
  剩下的只是返回一个ready的数量,其余的依然在aeProcessEvent里.

  值得注意下的是,其实apiPoll有三个实现,通过宏控制.
  对应的其实就是通常io的三个基本模型,select,epoll,kqueue.

  不仅仅apipoll
. 所有aeApi族的函数都相对应于三个版本.
  
  某种程度上说,这其实是api族函数其实是跟io相关的底层调用.
  
  值得注意的是,在apipoll里面,只是准备好fd,实际的read write实在processevent里做的.
  更实际的get set等操作,其实是在更早之前的beforeSleep里.

  也就是eventloop那一段.
  
  实话,beforeSleep这个名字多少有些不恰当.
  因为它并不sleep,相反,还做了挺多事情.
  
  核心的redis命令执行入口call()就是在这里调用的.

  一个while循环各个ready的client.
  
  对于每个client,尝试取一个command来执行,调用call.
  完了之后,如果还有堆积的命令则调用processInputBuffer,有里面的processCommand做未竟的事业.
  等所有client处理完了,则会根据参数配置,看是否对log做一次flush操作.
  
  注意到这一个过程是单进程单线程的.
  考虑到client很多,并且请求很多的话,恐怕数据的顺序就不好说了.
  
  运气好的api call,恰好在靠前的client队列里,那自然就在log里比较靠前了.
  一种理论上的极端情况是,两个并发写同一个key值,先发起的也许由于机遇问题,分在了靠后的client的话,那么,不管你早多少,事实上先写到库的是后发的那个连接.
  某种程度上说,这靠的不是先天,而是后知后觉.

  所以,目前来看,redis在高并发的时候情况可能不会很乐观.

  连接数多,导致loop ready client处理api call的时候,靠后的client在时序上靠后了.
  理论上,不能保证比较强的时序性.

  其次就是每次loop 完rady client之后的写操作.
  尽管,通过参数配置,可以让它隔一段时间再写,但毕竟这是同步写过程,属于block io吧.
  而且,如果reids并发访问很高,那么加上loop ready client时所花费的时间,积累的log越多,block io的时间会越来越长吧.
  
  当然,这只是纯纸上谈兵.实际如何,很难说.
  因为,怎样才算是高并发和高频读写呢,这个没有确切说法吧.
  
  至于各个readis命令的大致实现以及hash的内存策略问题,有空再谈谈.
  其实也挺简单的.

牛来餐馆

看了欢迎来龙餐馆. 总的来说,作为一个院线电影,成品多少是有点不太匹配的. 看起来更多像是一部网络大电影. 尤其涉及到一些大场面特效的时候,可以看得出服化道的降本增效. 剧本层面有一些比较有意思或者说闪光点. 就是试图用一个餐馆或者厨师来串起一个比较宏大的叙事题材. 这个可以说是...