jianwu's profile乱舞的弦~~~张牙舞爪的warningPhotosBlogLists Tools Help

Blog


    15 June

    程序员创业失败主要原因

    阿。。。从某条开始基本上全中。警醒下。
     
    1)没有明确的生活目标。没有奋斗的中心目标或明确的努力主向,就没有成功的希望。 

    2)没有非同寻常的雄心抱负。 
      如果对凡事漠不关心,不想在人生中求发展,不愿付出代价,那么这样的人也将成功无望。   
     
    3)缺乏自律。 
      纪律来自自我控制,这意味着人必须控制所有的消极思想,只能先控制自己,才能控制环境。自制是人类面对的最艰巨任务,如果无法战胜自我,就会被自我征服。 

    4)拖拉。 
      这是失败最普遍的原因之一,拖拉“老人”存在于每个人心中的阴暗角落,正是因为一直都在等待“适当时机”,才开始做那些值得做的事情,其实时机永远不会“适当”。   
     
    5)缺乏毅力。 
      不管做什么,大部份人开始时都满怀信心,但却不能善始善终。大部份人一遇到失败就容易放弃。毅力是不可取代的。把毅力当座右铭奉行到底的人,发现失败会自行退下,失败是无法对抗毅力的。   
     
    6)消极的个性。 
      因为消极的个性,而将别人拒于千里之外者,不会有成功的希望。成功来自力量的运用,而力量又来自与他人的合作,消极的个性无法促成合作。   

    7)无法克制“不劳而获”的欲望。这种机体本能使很多人走向失败。   

    8)缺乏果断的决策力。 
      成功的人士会果断决策,然后如果有必要,再慢慢改进。失败者往往花很长时间才能作出决策,但很快就需要修改,而且要频繁修改。犹豫和拖拉是一对双包胎,只要找到其中一个就一定能找到另一个,所以必须趁它们没有将你完全束缚在失败的车轮上,果断地把它们消灭。   
     
    9)过度谨慎。 
      不主动抓住机会的人往往只能捡别人挑剩的机会,过度谨慎和不够谨慎都不可取,人生本来就充满偶然成分。   
     
     
     
     

    10)迷信与偏见。 
      迷信是恐惧的一种形式,也是无知的表现,成功人士心胸宽广,无所畏惧。 
     
    11)目标不专。“万事通,万事松”,要全心全意专注于一个主要目标。   
     
    12)缺乏热情。 
      没有热情,一个人就没有说服力,而且热情有一种感染力,一个人如果拥有热情,并能适当控制热情,往往会受到人们的欢迎。   

    13)偏执。心胸狭窄很难取得任何进步,偏执说明一个人不积极获取知识。   
     
    14)蓄意不忠。 
      诚实是一种不可替代的品质,受无法控制的环境所迫,一个人可能一时不忠诚,也不会带来永久的破坏。但是,如果一个人蓄意不忠,则无药可救。他的得行为迟早会被发现,他负出的代价可能是失去信誉,甚至失去自由。   
     
    15)自私和虚荣。这些品质问题好比亮起红灯,让人不敢靠近,是妨碍成功的致命因素。   
     
    16)猜测而不思考。 
      多数人往往漫不经心或者过于懒惰,不愿费心获取用于准确思考的事实。他们喜欢根据猜测或仓促得出的“结论”了事。观念改变人生!选择决定未来!行动决定成败!
     
     
    来吧,老规矩,加上一张无关图片结束文章。前天北京vs天津的一场比赛,球迷打出的横幅:
    [没插件,饮恨吧mina] 

     

    躲进小楼成一统之技术贴---release要比debug快么

     

    读熊力先生的windows用户态程序高效排错的时候遇到这样一个问题:

     

    ------------------------------------------------------------------------------------------------------------

    分别在debug/release模式下运行下面的代码比较效率,会发现debugrelease更快。你能找到原因吗?

     long nSize = 200;
    char* pSource = (char *)malloc(nSize+1);
    char* pDest = (char *)malloc(nSize+1);
    memset(pSource, 'a', nSize);
    pSource[nSize] = '\0';
    DWORD dwStart = GetTickCount();
    for(int i=0; i<5000000; i++)
    {
    strcpy(pDest, pSource);
    }
    DWORD dwEnd = GetTickCount();
    printf("%d", dwEnd-dwStart);

     

    ------------------------------------------------------------------------------------------------------------

    vc6调了一下果然如此,根据cpu性能的不同相同的代码debugrelease的性能差距竟然有34倍之多。决定把此问题搞清楚。

     

    先拿出代码来吧:

    ----------------------------------------release部分代码-----------------------------------------------------

    00401044 8B FB                mov         edi,ebx

    00401046 83 C9 FF             or          ecx,0FFh        ;循环次数

    00401049 33 C0                xor         eax,eax             ;搜索内容

    0040104B F2 AE                repne scas  byte ptr [edi]            ;一直重复搜索到EDI字符串末尾的0

    0040104D F7 D1                not         ecx  ;得到搜索次数,也就是字符串的完整长度

    0040104F 2B F9                sub         edi,ecx             ;EDI后退到源字串头

    00401051 8B C1                mov         eax,ecx

    00401053 8B F7                mov         esi,edi                   ;ESI指向源字串地址

    00401055 8B FD                mov         edi,ebp                   ;EBP指向目的字串地址(之前malloc结果)

    00401057 C1 E9 02             shr         ecx,2                     ;双字mov 次数为长度除以2

    0040105A F3 A5                rep movs    dword ptr [edi],dword ptr [esi] ;内存拷贝

    0040105C 8B C8                mov         ecx,eax                   

    0040105E 83 E1 03             and         ecx,3

    00401061 4A                   dec         edx

    00401062 F3 A4                rep movs    byte ptr [edi],byte ptr [esi] ;完成剩余内容的拷贝

    00401064 75 DE                jne         main+44h (00401044)

    ----------------------------------------------------------------------------------------------

     

     

     

    debug版本调用的从strcat.asm就能够直接看到了,但是注释中实现的strcpy太过简单,不能显示汇编代码的全貌阿,以下是代码:

    -----------------------------------------------------------------------------------------------

    mov     edi,[esp+8]         ; edi points to dest string



    copy_start::
           mov     ecx,[esp+0ch]       ; ecx -> sorc string
           test    ecx,3               ; test if string is aligned on 32 bits
           je      short main_loop_entrance

    src_misaligned:                     ; simple byte loop until string is aligned
           mov     dl,byte ptr [ecx]
           add     ecx,1
           test    dl,dl
           je      short byte_0
           mov     [edi],dl
           add     edi,1
           test    ecx,3
           jne     short src_misaligned
           jmp     short main_loop_entrance

    main_loop:                          ; edx contains first dword of sorc string
           mov     [edi],edx           ; store one more dword
           add     edi,4               ; kick dest pointer
    main_loop_entrance:
           mov     edx,7efefeffh
           mov     eax,dword ptr [ecx] ; read 4 bytes

           add     edx,eax                                  7e fe fe ff
           xor     eax,-1

           xor     eax,edx
           mov     edx,[ecx]           ; it's in cache now

           add     ecx,4               ; kick dest pointer
           test    eax,81010100h

           je      short main_loop
           ; found zero byte in the loop
    ; main_loop_end:
           test    dl,dl               ; is it byte 0
           je      short byte_0
           test    dh,dh               ; is it byte 1
           je      short byte_1
           test    edx,00ff0000h       ; is it byte 2
           je      short byte_2
           test    edx,0ff000000h      ; is it byte 3
           je      short byte_3
           jmp     short main_loop     ; taken if bits 24-30 are clear and bit
                                       ; 31 is set
    byte_3:
           mov     [edi],edx
           mov     eax,[esp+8]         ; return in eax pointer to dest string
           pop     edi
           ret
    byte_2:
           mov     [edi],dx
           mov     eax,[esp+8]         ; return in eax pointer to dest string
           mov     byte ptr [edi+2],0
           pop     edi
           ret
    byte_1:
           mov     [edi],dx
           mov     eax,[esp+8]         ; return in eax pointer to dest string
           pop     edi
           ret
    byte_0:
           mov     [edi],dl
           mov     eax,[esp+8]         ; return in eax pointer to dest string
           pop     edi
           ret

    -----------------------------------------------------------------------------------------------

     

     

     

    1.最直接(或许也是最正确的)反应,编译器做多余的事情了,就检查debugrelease模式下编译参数的区别。最终目标集中在了 /O2/Ot上,使用/O2效率低,使用/Ot效率高。查阅资料之后发现 /O2 /Ot的超集,在我单独添加/O2的优化选项时,效率仍然是比较高的,开始怀疑资料的准确性。此路暂时不通放弃。

     

    2。接下来就开始怀疑release版本内存对齐造成效率低下,检查后win32平台malloc()内存地址必然对齐。pass

    副产品对齐版的malloc/free函数如下:

    /* align_size需要为2的倍数*/

    void *aligned_malloc(size_t size, size_t align_size) {

     

    char *ptr,*ptr2,*aligned_ptr;

    int align_mask = align_size - 1;

     

    ptr=(char *)malloc(size + align_size + sizeof(int));

    if(ptr==NULL) return(NULL);

     

    ptr2 = ptr + sizeof(int);

    aligned_ptr = ptr2 + (align_size - ((size_t)ptr2 & align_mask));

     

     

    ptr2 = aligned_ptr - sizeof(int);

    *((int *)ptr2)=(int)(aligned_ptr - ptr);

     

    return(aligned_ptr);

    }

     

    void aligned_free(void *ptr) {

     

    int *ptr2=(int *)ptr - 1;

    ptr -= *ptr2;

    free(ptr);

    }

    3。怀疑编译器release版本经常进行的内联优化上。检查代码以及常识告诉我这样的优化只能让release版本效率更高而已。

     

    4.开始修改代码,使用动态从msvcrtload strcpy,使用strncpy,自己实现注释版的strcpy

    并强制inline,最终的结果都是相同,开始不得不关注进strcpy代码里了,debug版本的strcpy在效率上一定比release的效率高(一直不愿相信这个问题)。我们就只能再回过头来看上面的代码了。Release版本的代码算是比较常规的拷贝操作了,遍历获取字串长度,然后一个DWORD一个DWORD拷贝(很显然比单字节拷贝效率高),最后如果还有内容的话,就按照byte拷贝。下面是debug的,

    a)       首先检查是否内存对齐,未能对齐的话先诸字节拷贝直至对齐。对win32平台我们直接pass因为内存分配好就是对齐的。

    b)      进入main_loop_entrance就比较神奇了,看下面这段神奇的代码,判断字符串是否到了结尾的,如果对一个DWORD拆分成4byte分别判断,那4bytes一起拷贝节省下拉性能立刻浪费掉了,ms采取了一个非常聪明的办法

     

    -----------------------------------------------------------------------------------------------------------------

    mov     eax,dword ptr [ecx]     ; read 4 bytes
    mov     edx,7efefeffh ;
    就是
    -0x81010101H
    add     edx,eax ; eax - 1000 0001 0000 0001 0000 0001 0000 0001B
    ;
    使用减法取反,如果为0000 0000就是NULL,那么取反的时候会向前一字节借位

    xor     eax,-1 ;
    直接取反,不借位

    xor     eax,edx
    add     ecx,4
    test    eax,81010100h ; 1000 0001 0000 0001 0000 0001 0000 0000B
    ;
    看看直接取反和减法取反,可能存在借位的那些位是否相同。相同则不存在借位,继续循环

    ;
    否则就扫描到NULL了,字符串结束

     

    简单的解释一下前三个字节我们可以这样判断,如果某字符减去0x1还要向前字节借位的话,那么该字符必然是’\0’,这样的话前一字节的最后一个bit必然会被借位。如此以是否向前一字节借位这一标准来判断该字节是否为’\0’.这样的方法适用于前三个字节,但是第四个字节怎么办?ms使用的方法是这样的以第四个字节的最高bit来判断,如果该字节为0,则在进行减法操作的时候,字节的最高位由于借位产生了1(不是纯0就不会借位到这个倒霉的最高bit身上),以此标准来判断该字节是否为0

    效率的节约也就在于此,dword进行拷贝以及直接对dword进行判断以查找字符串末尾,这是debug版的strcpy超越release版的地方。计算机的世界,算法为王。

     

     

     

    不知道诸位有注意到的这个地方没,如果这个倒霉的bit1其它bit0呢?他也可能会误把这种情况的跟’\0’混淆的,因为代码无法判断这个1使本来就有的还是被借位出现的(本质就是0xor0 = 1    1 xor!1 = 1两种情况无法判断),在进行进一步确认的时候就出了问题,不得已重新循环反而就损失了效率。当然这是一种极端的情况。可以填充数据为80xxxxxx来验证一下我们的怀疑。

     

    针对这种方法如果我们想有所改进,单独的DWORD看来是希望不大了。可以结合cf寄存器来做下一步的工作。留一个note吧,以后思考。

    整理下最近搞的wow外挂,release些经验来吧。。。。