没想到蓉儿已经逝世20周年了~~~,俏丽的形象依然清晰在目,当年TVB万人空巷的《射雕》的确是不可能再逾越阅的传奇,因为已经在没有一个人能如蓉儿一样把一个脚色用生命来演绎~~,里面的个个人物都有一种灵魂,而这是现代其它版《射雕》最缺的东西~~
转贴一篇贴子,以纪念已经逝去的蓉儿吧~~~
http://news3.xinhuanet.com/forum/2005-05/20/content_2976307.htm
星期日, 十月 08, 2006
关于“内存对齐”必要性的官方解释
很早以前就知道写程序的时候最后做到内存按4字节(doublewords)对齐能增加性能,这也是很多编译器在编译的时候都会加上
的原因,不过今天终于看见一份官方文档对此的解释了:
When used in a configuration with a 32-bit bus, actual transfers of data between processor and memory take place in units of doublewords beginning at addresses evenly divisible by four; however, the processor converts requests for misaligned words or doublewords into the appropriate sequences of requests acceptable to the memory interface. Such misaligned data transfers reduce performance by requiring extra memory cycles.
上面这段是说在32位的总线上,每一次实际的传输都是按双字(doublewords)为单位进行的,然后 cpu在进行组装,因此,如果你的地址不是按双字对齐的(即地址不能被4整除),那么cpu就会花费更多的周期(因为可能需要取两次才能组合成需要的东 东),因此,按双字对齐对于提高cpu的性能是显著的,这也是为什么我们常常看见堆栈顶都对齐到双字上,而每次执行pop操作,也总是4个字节4个字节的 压栈。这样,每次压栈只需存取一次内存就行了,而且压栈结束后,栈顶同样是按4字节对齐的。
不过并不是程序中的所有部份都需要4字节对齐,比如程序的代码就无需对齐,因为代码会经过预取及在cpu中排队(其实预取的时候一般都是一次取一批指令而且取的时候cpu在进行其它的流水操作),因此不对齐也不会对性能造成太大影响,下面这段解释了原因:
Due to instruction prefetching and queuing within the cpu, there is no requirement for instructions to be aligned on word or doubleword boundaries.(However, a slight increase in speed results if the target addresses of control transfers are evenly divisible by four.)
(注:以上两段英文说明均摘自《Intel 80386 programmer's reference manual 1986》)
.algin 4
的原因,不过今天终于看见一份官方文档对此的解释了:
When used in a configuration with a 32-bit bus, actual transfers of data between processor and memory take place in units of doublewords beginning at addresses evenly divisible by four; however, the processor converts requests for misaligned words or doublewords into the appropriate sequences of requests acceptable to the memory interface. Such misaligned data transfers reduce performance by requiring extra memory cycles.
上面这段是说在32位的总线上,每一次实际的传输都是按双字(doublewords)为单位进行的,然后 cpu在进行组装,因此,如果你的地址不是按双字对齐的(即地址不能被4整除),那么cpu就会花费更多的周期(因为可能需要取两次才能组合成需要的东 东),因此,按双字对齐对于提高cpu的性能是显著的,这也是为什么我们常常看见堆栈顶都对齐到双字上,而每次执行pop操作,也总是4个字节4个字节的 压栈。这样,每次压栈只需存取一次内存就行了,而且压栈结束后,栈顶同样是按4字节对齐的。
不过并不是程序中的所有部份都需要4字节对齐,比如程序的代码就无需对齐,因为代码会经过预取及在cpu中排队(其实预取的时候一般都是一次取一批指令而且取的时候cpu在进行其它的流水操作),因此不对齐也不会对性能造成太大影响,下面这段解释了原因:
Due to instruction prefetching and queuing within the cpu, there is no requirement for instructions to be aligned on word or doubleword boundaries.(However, a slight increase in speed results if the target addresses of control transfers are evenly divisible by four.)
(注:以上两段英文说明均摘自《Intel 80386 programmer's reference manual 1986》)
一个关于多行注释的正则表达式的问题
今天在写一个多行注释的正则表达式的时候发现了一个这样的问题,最开始写的是:
Slash.Asterisk.(NOT(Asterisk),Asterisk.NOT(Slash))*.Asterisk.Slash;
结果发现它不能匹配/***/的情况。因为在匹配中间那个*的时候,它还匹配了其后的NOT(Slash),这就把后面的那个*给消耗掉了,因此最后就只剩下一个/了,就无法匹配了。
后来我把它改成了:
Slash.Asterisk.(NOT(Slash),NOT(Asterisk).Slash)*.Asterisk.Slash;
这样就成功了。
其实第一次我想的是:*后不能有/,而第二次我想的是/前不能有*,就导致了一次成功一次失败,A后不能是B,与B前不能是A是不一样的,这也许是个哲学问题。
Slash.Asterisk.(NOT(Asterisk),Asterisk.NOT(Slash))*.Asterisk.Slash;
结果发现它不能匹配/***/的情况。因为在匹配中间那个*的时候,它还匹配了其后的NOT(Slash),这就把后面的那个*给消耗掉了,因此最后就只剩下一个/了,就无法匹配了。
后来我把它改成了:
Slash.Asterisk.(NOT(Slash),NOT(Asterisk).Slash)*.Asterisk.Slash;
这样就成功了。
其实第一次我想的是:*后不能有/,而第二次我想的是/前不能有*,就导致了一次成功一次失败,A后不能是B,与B前不能是A是不一样的,这也许是个哲学问题。
对JavaScript局部变量声明及使用的一些看法
在ms实习时碰见的一个问题,很老了,放在这里吧~~ ...
对于昨日一个局部变量、局部函数问题的看法:
建议:不使用局部变量、局部函数,所有变量、函数都明显的使用this指针
仔细分析一下,是由于在生成对象t的时候,重置了test_t()中的v为4了,故而this.m()中的v是重置后的v,而w()中的v却还是重置前的v
这说明在this.m中另生成了一个v,而这个v是各对象独有的,而m()中的v却是各对象共有的,这会使整个代码理解起来非常混乱,稍不注意就会分不清哪个函数中的变量是独有的,而哪个函数中的变量是共有的~~
因此,建议类中的任何东东必须显示的使用this,不要使用不属于对像的东东,使用任何东东的时候都必须显示的指明它属于什么对象(或作用域)
对于昨日一个局部变量、局部函数问题的看法:
建议:不使用局部变量、局部函数,所有变量、函数都明显的使用this指针
function test_t()上面这段代码,输出6,6,这是正确的,但是,现在我们把(*)句处的注释去掉,则会输出6,4!这非常奇怪!
{
var v = 4 ;
this.m = function()
{
w() ;
alert( v ) ;
}
w = function()
{
v+=2 ;
alert( v ) ;
}
}
function start()
{
f = new test_t() ;
// t = new test_t() ; //(*)
f.m() ;
}
仔细分析一下,是由于在生成对象t的时候,重置了test_t()中的v为4了,故而this.m()中的v是重置后的v,而w()中的v却还是重置前的v
这说明在this.m中另生成了一个v,而这个v是各对象独有的,而m()中的v却是各对象共有的,这会使整个代码理解起来非常混乱,稍不注意就会分不清哪个函数中的变量是独有的,而哪个函数中的变量是共有的~~
因此,建议类中的任何东东必须显示的使用this,不要使用不属于对像的东东,使用任何东东的时候都必须显示的指明它属于什么对象(或作用域)
一个关于指针访问效率的问题
昨天晚上再看一本杂志的时候发现上面提到了这样一个问题,我觉得这个问题很有意思,它的主要本质就是说尽量使用直接而非间接访问(通过指针访问)能极大的提供效率~~
比如:
这看起来工作出会很正常,但时有些时候也会出现很大的问题,比如,如果我的函数是
int q;
Foo(&q, &q);
这就出现问题了,因为经编译器优化后可能就成这样了:
其实一般来说,Foo(&q, &q),这样的调用形式是很少见的,绝大多数情况下,可以认为*x与*y指向不同的事物,这样编译器就能对其进行优化了。杂志上的文章指出,一些编译器提供了一个称之为unsafe的优化级别,它会假定所有的指针都指向的是不同的事物,当然,如果有多个指针指向同一个事物,那么采用这个级别的优化就会出问题。C99提供了一个新的关键字restrict,它可以告诉编译器,这个指针所指向的对象不会被其它指针所指代,因此,编译器就可以放心大胆的进行优化了,因为这个指示是程序员提供的,程序员当然知道这个指针所指的对象是否会被另一个指针所指代。
据说这个功能最初是来源于Fortran的启发,看来多了解一点其它的语言的确是有大大的益处的啊~~~,当然如果不使用C99,我想我们程序员自己也能在程序中采用这样的优化~~~
比如:
void Foo(int *x)这个程序中如果很多处都使用了*x,那么每次都要先从x中取出地址,然后再通过这个取出的地址去访问值,这其间需要两次访存,这是很低效的(在某些体系结构的计算机上,间接访问的开销可能非常大。因此,编译器可以做这样的优化,比如:
{
....
}
void Foo(int *x)即在进入函数时,就把*x中的内容在一个局部变量中保存起来,然后在后面的使用中都只使用这个局部变量的值,最后再把这个局部变量的值返还给*x。
{
int m = *x;
.....
*x = m;
}
这看起来工作出会很正常,但时有些时候也会出现很大的问题,比如,如果我的函数是
void Foo(int *x, int *y)而调用时我这样调
{
....
}
int q;
Foo(&q, &q);
这就出现问题了,因为经编译器优化后可能就成这样了:
void Foo(int *x, int *y)这时,由于m,n不是指向同一个事物的,因此本来对*x,*y的操作应当作用于同一个事物上,但现在作用在了两个事物上,因此就会使最后的结果出错。
{
int m = *x;
int n = *y;
...
*x = m;
*y = n;
}
其实一般来说,Foo(&q, &q),这样的调用形式是很少见的,绝大多数情况下,可以认为*x与*y指向不同的事物,这样编译器就能对其进行优化了。杂志上的文章指出,一些编译器提供了一个称之为unsafe的优化级别,它会假定所有的指针都指向的是不同的事物,当然,如果有多个指针指向同一个事物,那么采用这个级别的优化就会出问题。C99提供了一个新的关键字restrict,它可以告诉编译器,这个指针所指向的对象不会被其它指针所指代,因此,编译器就可以放心大胆的进行优化了,因为这个指示是程序员提供的,程序员当然知道这个指针所指的对象是否会被另一个指针所指代。
据说这个功能最初是来源于Fortran的启发,看来多了解一点其它的语言的确是有大大的益处的啊~~~,当然如果不使用C99,我想我们程序员自己也能在程序中采用这样的优化~~~
两种有关编译优化的观点
编绎优化一直都存在两种争论:
一种认为,编译优化应当由编译器能做,程序员不应当管。因为编译器几乎者是由专家编写研究出来的,专家对于怎么优化程序比普通的程序员现然更有见解。程序员不可能个个是专家,与其让不太精通的程序员去优化,不如让专家的编译器来优化。而且一个高性能的编译优化器能对它所编译的所有程序进行优化,而这些程序不一定也不可能都是由相当出色的程序员完成的。再者,按现在软件工程的要求,程序源代码的可读性被放在相当重要的位置,这当然也在很大程度上限制了程序员能对其进行的优化。当然,表面上看一种接合良好的方案是编译器提供足够多的预编译指令,让程序员有能力指导编译器进行优化,但这使得编译器的设计复杂度大大增加,而且不通此道的程序员很可能是“瞎指挥”反倒使编译器的优化品质下降。
另外一种观点认为,编译优化应当尽可能多的由程序员优化代码完成。因为无法保证每个编译器都能提供足够强劲的最优化指令,有些甚至在产生最优化指令时会因最优化玩得过火而出错(现在的gcc中就有一个很著名的bug,因为优化而导致程序执行出错)。而一旦最优化指令出错,对于程序员来说几乎是灾难性后果。因为检查、排错最优化后的二进制代码远比对源代码进行相应的操作麻烦。另外产生高性能的最优化编译器难度很大,有时候这种开发代价高于它产生的实际价值。
对于我个人来说,我是比较倾向于第二种看法:程序员应当从自身做起尽可能的优化自己的程序,这样才能有信心在任何编译平台上都能取得较高的性能。
一种认为,编译优化应当由编译器能做,程序员不应当管。因为编译器几乎者是由专家编写研究出来的,专家对于怎么优化程序比普通的程序员现然更有见解。程序员不可能个个是专家,与其让不太精通的程序员去优化,不如让专家的编译器来优化。而且一个高性能的编译优化器能对它所编译的所有程序进行优化,而这些程序不一定也不可能都是由相当出色的程序员完成的。再者,按现在软件工程的要求,程序源代码的可读性被放在相当重要的位置,这当然也在很大程度上限制了程序员能对其进行的优化。当然,表面上看一种接合良好的方案是编译器提供足够多的预编译指令,让程序员有能力指导编译器进行优化,但这使得编译器的设计复杂度大大增加,而且不通此道的程序员很可能是“瞎指挥”反倒使编译器的优化品质下降。
另外一种观点认为,编译优化应当尽可能多的由程序员优化代码完成。因为无法保证每个编译器都能提供足够强劲的最优化指令,有些甚至在产生最优化指令时会因最优化玩得过火而出错(现在的gcc中就有一个很著名的bug,因为优化而导致程序执行出错)。而一旦最优化指令出错,对于程序员来说几乎是灾难性后果。因为检查、排错最优化后的二进制代码远比对源代码进行相应的操作麻烦。另外产生高性能的最优化编译器难度很大,有时候这种开发代价高于它产生的实际价值。
对于我个人来说,我是比较倾向于第二种看法:程序员应当从自身做起尽可能的优化自己的程序,这样才能有信心在任何编译平台上都能取得较高的性能。
订阅:
博文 (Atom)
