发表于Go4Pro《分布式版本控制(一)》
和CSDN《分布式版本控制(一)》
猛禽的归档8挂
发表于Go4Pro《分布式版本控制(一)》
和CSDN《分布式版本控制(一)》
最近这种分布式的SCM忽然就流行起来了,上周挑了半天决定拿Bazaar下手,结果折腾了一阵碰到一个问题:
把本地Repository通过SFTP
push到服务器上以后,再用SFTP从远程Repository上branch下来,居然只有.bzr的本地Repository副本,没有工作目录的
内容。试了update/checkout等命令也都不行——难道是因为我已经习惯了传统的集中式SCM,什么地方做得不对?
搞了很长时间没解决,一怒之下换了名气很大的Mercurial。这个东东很不错,找到这两页快速参考文档,打印出来很好用,上手很快。速度比Bazaar快不少,功能似乎也强一些,据说做分支合并很方便,不过我暂时还没有试。
可惜遗憾的是Mercurial没有Bazaar那样的通过SFTP上传的功能,这对我来说有些不方便——也许是因为它的使用模式与集中式SCM更加的不同,所以不需要这样的功能,但反正我是不太适应。
经过一番研究以后发现,用Bazaar作branch只有通过http才能取得工作目录的内容。
既然能取得工作目录,就暂时决定继续用Bazaar。
另,我喜欢这些DRCS(分布式版本控制系统),因为它们只会生成一个目录,而不是像CVS或是SVN那样,在每个子目录里都生成一个。
这几天被这个问题搞得焦头烂额,昨天临睡前才终于得到彻底的解决。
发于Go4Pro:《跨平台中文文件名乱码的解决》
关于Go4Pro——这是早在2005年圣诞节第二届BT(BCB Tech)群会时,由TR提议创办的,现在的这个是最新的版本。
看到这么一篇《构架师已死(转)》,虽然我很同意原文的三个结论:
中国程序员最愚蠢的三个认识:
35岁后写不动程序了;
我只要做Java(C++);
我想当构架师。
但是对于其中导出第三条结论的过程不以为然。
这三个愚蠢的认识在中国软件业流传已久,那个面试的小伙子深受其毒害也不足为奇,但是这个Simon的态度就比较的不合适。
而我最为反对的是Simon的这个观点:
这就是为什么实际上所谓的构架师不存在的原因。一个更简单的东西怎么会更有价值呢?每个人都能够画出这种构架图,但不是每个人都能写出好的代码。
一个构架图固然看上去简单,但是要作出这样一个简单而且正确的构架图却绝对不简单——我可以肯定地说,在大多数情况下,这要比写出好的代码更困难。
把一件复杂的事情弄简单了,是非常非常有价值的。
令狐评论说:
这次说简单一点:架构师是非常非常重要的——如果你对架构师的职责有正确理解的话。
发于CSDN《GCC的BUG研究(Rev.3)》(CSDN BLOG今天多次崩溃)。
====在CSDN BLOG未完全修复崩溃问题之前,这里备份一下====
Solidot报道GCC在Linux平台下有一个BUG。但是原文中说只有Linux平台有这个问题是不正确的,经过令狐的实际测试,在HP-UX(GCC 4.0.2),LINUX(UBUNTU,GCC 4.1.2),WINDOWS(GCC 3.4.5)下都存在在这个问题。
为了调查研究一下这个问题究竟是如何造成的,我们一帮人展开了一番讨论,经过对汇编代码的分析,结果看来是GCC的代码优化实现有问题。
测试的C源程序如下:
int main () {
int i=2;
if( -10*abs (i-1) == 10*abs(i-1) )
printf ("OMG,-10==10 in linux!\n");
else
printf ("nothing special here\n");
}
在X86 Linux平台下的汇编代码片段如下:
19 mov DWORD PTR [%ebp-8], 2
20 mov %edx, DWORD PTR [%ebp-8]
21 mov %eax, %edx
22 sal %eax, 2
23 add %eax, %edx
24 add %eax, %eax
25 sub %eax, 10
26 mov %edx, %eax
27 sar %edx, 31
28 mov %ecx, %edx
29 xor %ecx, %eax
30 sub %ecx, %edx
31 mov %eax, DWORD PTR [%ebp-8]
32 imul %eax, %eax, -10
33 add %eax, 10
34 mov %edx, %eax
35 sar %edx, 31
36 xor %eax, %edx
37 sub %eax, %edx
38 cmp %ecx, %eax
39 jne .L2
(完整的代码在这里:X86 Linux,HP-UX——由令狐虫友情提供)
代码并不难理解:
其中 DWORD PTR [%ebp-8] 就是变量 i 。21行到25行之间是计算 i * 10 – 10 ,结果保存在 eax 中。26行到30行是 abs() 函数的实现,我以前对这个倒还真没研究过,现在一看之下发现这个实现还真是有创意啊(回头细说)。31行到33行是计算 i * -10 + 10 ,结果保存在 eax 中。34行到37行同样是 abs() 函数的实现。38行到39行是比较跳转。
所以结果已经很清楚了,GCC把:
-10 * abs( i - 1 ) 和 10 * abs( i - 1 )
优化成了:
abs( -10 * i + 10 ) 和 abs( 10 * i - 10 )
结果当然就不正确了。
结论就是:不是 abs() 函数实现的问题,也不是代码解析的问题,是优化的问题——编译器最容易出问题的地方就是优化。
现在最不能理解的就是:这样做并不见得更“优”,何必要这样“优化”呢?
补充(Rev.2):
后来经三火指点,这种优化被称为Const Foldering,也就是说把常量提取出来在编译时计算,以优化运行时的性能。本来对于取绝对值这种情况是不应该这么做的,因为 abs() 本身是一个函数,但是与令狐讨论后,他认为GCC在这里实际上是把它优化为一个操作,所以同时对它进行了Const Foldering。
关于在这个Const Foldering的BUG,有人提供了一个补丁:《[PATCH] Fix PR34130, extract_muldiv broken》,其中的修正代码是这一段:
*** fold-const.c (revision 130238)
--- fold-const.c (working copy)
*************** extract_muldiv_1 (tree t, tree c, enum t
*** 6095,6100 ****
--- 6095,6103 ----
}
break;
}
+ /* If the constant is negative, we cannot simplify this. */
+ if (tree_int_cst_sgn (c) == -1)
+ break;
/* FALLTHROUGH */
case NEGATE_EXPR:
if ((t1 = extract_muldiv (op0, c, code, wide_type, strict_overflow_p))
在这个补丁代码里,是简单地对要处理的常量进行判断,如果是负数就跳过Const Foldering优化部分。但我和令狐都认为,这种解决方案只是一种权宜之计,是一种明显的坏味道。
我觉得根本的解决方案是将 abs() 从Const Foldering优化的操作列表中去除——但是将 abs()
优化为一个操作的确是很有用的,如果Const
Foldering是对所有操作都进行优化的话,这种修改也可能会带来别的坏味道。我不知道GCC中还把什么函数优化为操作,但是如果是对所有操作进行
Const Foldering的话,潜在风险还会有的,因为Const Foldering只对线性函数正确,而 abs() 出问题正是因为它是一个非线性函数。
补充(Rev.3):
关于优化选项的问题,我们刚才又做了一下研究。使用 -O0 选项关闭优化,仍然生成与无优化选项类似的代码,看来这种是属于默认优化的部分。使用 -O1 和 -O2 选项生成的目标代码很相似,都是高度优化的(但结果仍然是错误的):
mov DWORD PTR [esp], OFFSET FLAT:LC0
call _puts
(完整的代码在这里:X86 Linux——由Mike友情提供)
可见只剩下一句 printf("OMG…"); 。这也就意味着这个最优化代码是在Const Foldering 之后,编译器又发现了 i 本质上也是一个常量,所以优化成了现在这个样子。如果编译器是先发现 i 是常量,再作Const Foldering 的话,结果就会是正确的了——令狐昨天已经试过,把 i – 1 换成 1 以后,默认优化生成的代码与现在最优化生成的代码差不多,只不过输出结果是正确的 printf("nothing…"); 。
====附录的分割线====
附一段关于这个 abs() 函数实现的说明:
整数取绝对值的方法基本上就是判断是否小于0,如果是则取负,否则直接返回。GCC里的实现则比较巧妙,没有判断跳转的过程(我猜测是基于CPU运行优化考虑)。它的做法是用 sar
指令填充符号位得到一个数,对于正数,这个符号数为0,对于负数,这个符号数为全1(即-1)。然后用这个符号数与原数异或,如果是正数将不变(与0异或
不变),如果是负数将取反(与1异或取反)。最后将异或结果减符号数,对于正数来说,减0原值仍然不变,对于负数来说,减-1相当于+1——在补码中,一
个整数取反加1的结果正是等于对其取负。于是实现了绝对值的计算。
要汗一下的是,我今天刚在豆瓣上跟人说:不做底层工作不需要了解汇编。结果这就碰到一个反例。
(为避免地域之争,隐去具体地名)
某市不久前开始实施所谓的老年人免费乘车制度——这种事情在厦门早已经实施了很多年,甚至在我老家那种乡下地方也已经实施了好几年。但对于某市来说,这仍然是一种进步。
BTW:我对这一制度依然不够满意,因为上面说的三个地方的老年人免费乘车制度都仅限于当地人——又是万恶的户口制度。
不过这个“好制度”刚实施没几天,电视上又开始谈新的问题。因为这一制度的实行,导致了大量的老年人出门乘车,给行车安全带了很多的隐患。
这倒是一个新鲜的说法,我在其它地方都没有听说过这种事情。但回头想想也就理解了:
某市的老年人有一个别处人所没有的爱好,就是愿意为了一点小便宜付出巨大的代价。早在几年前就曾经发生过某超市每天早上限量提供优惠早餐,结果每天
早上都会有一大群的老年人大老远跑去排长队买。类似的事情还有很多,我就不一一举例了。当然这些人绝大部分也不是什么低收入者,一般都是些能拿到相对(国
内其它城市)不菲的退休金的人。
所以呢,有了这个免费乘车制度出来,他们当然也不会放过。那么在没有这个制度的时候,同样有老年人会出门乘
车,为什么那个时候就没有“行车安全隐患”呢?因为没有免费的话,出行的老年人要少得多,偶尔有个把老年人也容易有座位可坐,相对安全一些。而现在出行的
老年人一下多了起来,没有座位坐就很常见了,碰到急刹车之类的情况就难免出危险。而城市的路况大家也知道,这种事情是经常发生的。
那么为什
么在厦门或其它地方没有这种问题?以厦门为例,厦门的老年人乘公交车是基本上不用担心没有座位的,让座对于在厦门生活的人来说是一件再平常不过的事情,即
使是在我们那个乡下地方,这种事情也早已经是每位乘车者的习惯。但是在某市,让座这种事通常是需要司售人员提醒甚至是要求才会发生,倒是抢座位的事情在这
里司空见惯。甚至于发生过这样的事情:
在公交车上,一个当地妇女座位旁站了两个外地老年人,她不但没有让座,反而是直到她要下车前,还特地用方言把较远处一位陌生的本地人叫过来,把座位让给那个人。
我只能说有些人就是活该啊。
前几天突然想到要写一个生辰八字的计算程序,当然这个目的是纯属娱乐的。
生辰八字在算命学上称为四柱:年柱,月柱,日柱和时柱。每柱由一对干支组成,共八字,故名生辰八字。本质上就是以干支历法记录的一个人的出生时辰。(以下略去对以这种算命方法是伪科学的科学论证文字1587字)
在说干支历之前要特别提醒大家的一点是:中国的所谓农历并不是阴历,而是一种阴阳历。当我们说农历正月初一时,用的是阴历,以月亮的运行规律制定。当我们说到属相、干支、节气时,用的是阳历,以太阳的运行规律制定。所以说中国的农历本质上是一种相当复杂的历法。
幸好计算四柱只需要用到阳历部分,所以实现起来相对简单一些。
BTW:目前网上常见的一些万年历程序中,干支历的部分大多是错误的。
关于四柱的正确计算方法见《生辰八字计算》一文,本程序就是参考该文写成。
使用注意事项:
1、输入的日期至少要精确到小时,才能得到完整的四柱;
2、如果日期刚好是12节气(24节气中扣除12中气),则最好精确到分钟,否则可能得到错误的月柱。如果刚好碰到立春,则更要精确到分钟,否则不但月柱可能错,连年柱也可能错。
python源程序,基于GPL V2发布(下载:2kBytes,Revision:071018)。
一个朋友在写程序时,碰到一些选择方面的问题,不知道要用哪个方法来解决问题会比较好。我跟他说不要想那么多,随便选一个可行的方法做下去就是了,在实践之前分析几种解决方法的优劣很可能会得出错误的结论。实际的情况往往是你想到的问题没有发生,没想到的问题却发生了。
他说:总觉得如果不事先想透的话。写的代码感觉改来改去的怪怪的。
我跟他说:
所以说需要Agile,需要TDD,而且那也不叫改来改去,那个叫做Refracting。
貌似偶现在程序写得少,谈起编程来却很“时尚”嘛。
令狐对此有一段评论:
那天在写Barcamp总结的时候,我就说过,为什么老外说的话别人就很容易理解,而我们就喜欢动不动就冒出专业的术语呢?
其实仔细想想,跟一个对专业一点都不了解的人说术语,会给他带来多少帮助?答案是一点都没有。因为你用他不懂的话向他解释他不懂的东西,他怎么可能弄懂?
对话的目的是为了交流而不是炫耀,如果使用术语达不到交流的目的,那还不如不用。为什么现在有人说不用模式,不用框架,不用这个不用那个,因为在他们的团队里,大多数人不懂这些,跟他们说这些没用,那还不如不要用。──好吧,我承认上面的这段话跟这个blog没有什么关系。
下面说点正题的。
但凡选择,肯定是有目的的。比如说为了效率,为了一些处理一些特殊情况,为了节省空间,为了让代码清晰可理解,等等等等,诸如此类。如果完全没有目的,那么当然,很显然不需要考虑,闭上眼睛随便抓一个拿来用即可。如果是有目的的,那么很简单,我们功利一点,做一个简单的运筹考虑:选这个方法,实现复杂,但是很通用,效率很高;选那个则简单明了,但效率低而且不够通用──那么,请思考一下,你的项目里,对“通用”有多少需求?对“效率”有多少需求?如果用一个适合于百万级数据处理的算法,解决一个在99%情况下都不会超过1000个数据的应用,显然是太浪费了。──永远选择你目前可以接受的最简单的方法,是不会错的。将来怎么办?将来的事情,将来再考虑啦。但是,如果将来发生了情况B,你可不能只考虑解决情况B,而是要将目前的情况A和情况B进行综合考虑,寻找一个适合于他们的“通用”解法,用这个解法,可以解决类似的情况C、情况D……,这样你才不会变得太被动(为什么要这样,因为情况A的时候,你面对的只有情况A,而发生情况B的时候,事情已经变化了,它既然会变化到B,为什么不会变化到CDEFG,所以就必须为可能发生的变化做准备了)。这个其实就是传说中的敏捷开发方法。
──相信我,上面这段话的术语已经够少了。
发于CSDN《不用模式的理由》。