• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Dict.CN 在線詞典, 英語學(xué)習(xí), 在線翻譯

            學(xué)海苦作舟,書山勤為徑

            留下點回憶

            常用鏈接

            統(tǒng)計

            積分與排名

            Denoise

            English study

            Web技術(shù)

            數(shù)據(jù)壓縮

            一些連接

            最新評論

            一段代碼優(yōu)化的討論

             優(yōu)化很多時候是必要的,特別對于瓶頸程序。這里討論一段代碼的優(yōu)化過程,從而演示一段簡單的代碼優(yōu)化過程,并希望得到一些建議。

            先描述一下需求:

            一個16位數(shù)的序列,將其奇數(shù)位置放到一個序列,偶數(shù)問題放到另外一個序列。注意奇數(shù)和偶數(shù)序列的長度不一定相同。

            最簡單的代碼:

             

             1void CTestClass::SplitToEvenOddSeqs(short *sp,short *dp1,short *dp2,int evenLen,int oddLen)
             2
             3{
             4
             5            int i;
             6
             7            for(i = 0;i<oddLen;i++,sp+=2,dp1++,dp2++)
             8
             9            {
            10
            11                        dp1[0= sp[0];
            12
            13                        dp2[0= sp[1];
            14
            15            }

            16
            17            if(i != evenLen)
            18
            19                        dp1[0= sp[0];
            20
            21}

            22
            23

            這段代碼可以達(dá)到必要的功能,但他肯定不是優(yōu)化的。

            1.循環(huán)中,每次需要訪問5個變量。

            2.每次循環(huán)需要一個判斷,4個加法

            3.最后的不等式判斷也

            考慮到dp1dp2總是同時訪問,于是定義一個結(jié)構(gòu)體:

             

            typedef struct tagDstData

            {

                        
            short dp1;

                        
            short dp2;

            }
            TagDstData;

            現(xiàn)在的算法為:

             

            void CTestClass::SplitToEvenOddSeqs(short *sp,TagDstData *dp,int evenLen,int oddLen)

            {

                        
            int i;

                        
            for(i = 0;i<oddLen;i++,sp+=2,dp++){

                                    dp
            ->dp1 = sp[0];

                                    dp
            ->dp2 = sp[1];

                        }


                        
            if(i < evenLen)

                                    dp
            ->dp1 = sp[0];

            }


            這樣做以后CPU每次讀取dp只需要一次。循環(huán)條件少了一次加法。

            上面代碼每次復(fù)制一個16bit的值,總共四個字節(jié)要復(fù)制兩次,考慮把這個地方優(yōu)化一下。優(yōu)化后的代碼如下:

             

            void CTestClass::SplitToEvenOddSeqs2(short *sp,TagDstData *dp,int evenLen,int oddLen)

            {

                        
            long *lSp = (long *)sp;

                        
            long *spEnd = (long *)(sp + (oddLen<<1));

                        
            long *lDp = (long *)dp;

                        

                        
            while(lSp < spEnd){

                                    
            *lDp++ = *lSp++;

                        }


                        
            if(oddLen < evenLen)

                                    dp[evenLen].dp1 
            = *((short *)lSp);

            }


            這里先不考慮字節(jié)序的問題。

            這樣優(yōu)化后和前面比較起來有那些改進(jìn)?

            1.循環(huán)體內(nèi)只有一個指令;對于++運(yùn)算,很多處理器都能很好處理。

            2.循環(huán)條件檢查只有一條比較指令

            其實這里的檢查的比較指令還可以優(yōu)化一下,因為比較指令比較長,看一下下面的改進(jìn):

            反正是四個字節(jié)的復(fù)制,不如下計算好復(fù)制的4個字節(jié)數(shù)量;再循環(huán)。

            void CTestClass::SplitToEvenOddSeqs3(short *sp,TagDstData *dp,int evenLen,int oddLen)

            {

                        
            long *lSp = (long *)sp;

                        
            long *spEnd = (long *)(sp + (oddLen<<1));

                        
            long *lDp = (long *)dp;

                        
            long nDWORDs = oddLen>>1;

                        

                        
            while(nDWORDs–){

                                    
            *lDp++ = *lSp++;

                        }


                        
            if(oddLen - evenLen)

                                    dp[evenLen].dp1 
            = *((short *)lSp);

            }



            寫好上面四段代碼,拿VS2005編譯一下發(fā)現(xiàn),測試代碼如下:

             

            void CompareData(TagDstData *spDst,short *pSrcTest)

            {

                        
            for(int i = 0;i<10240;i++)

                        
            {

                                    
            //if(spDst[0].dp1 )//if we access spDst here, the time will be longer

                                    
            if(spDst[0].dp1 > pSrcTest[0]|| spDst[0].dp2 >pSrcTest[1])

                                    
            {

                                                printf(”
            Split arithmetic 
            is not right!\n”);

                                                
            break;

                                    }


                                    pSrcTest 
            +=2;

                        }


                                    printf(”
            Split arithmetic 
            is right!\n”);

            }


            int _tmain(int argc, _TCHAR* argv[])

            {

                        
            int i,f ;

                        
            int now;

                        CTestClass testClass;

                        
            short spSrc[20480];     

                        
            short spDst1[10240];

                        
            short spDst2[10240];

                        TagDstData spDst[
            10240];

                        
            short *pSrcTest = spSrc;

                        memset(spSrc,
            2,20480<<1);

                        memset(spDst1,
            0,10240<<1);

                        memset(spDst2,
            0,10240<<1);

                        memset(spDst,
            0,20480<<1);

                        CStopWatch stop1;

                        stop1.Start();

                        
            for(i = 0;i<100000; i++)

                                    testClass.SplitToEvenOddSeqs(spSrc,spDst1,spDst2,
            10240,10240);

                        now 
            = stop1.Now();

                        printf(”time2 
            =%d\n”,now);

                        stop1.Start();

                        
            for(i = 0;i<100000; i++)

                                    testClass.SplitToEvenOddSeqs(spSrc,spDst,
            10240,10240);

                        now 
            = stop1.Now();

                        printf(”time3 
            =%d\n”,now);

                        
            for(i=0;i<20480;i++)

                                    spSrc[i] 
            = i;

                        memset(spDst,
            0,10240<<1);

                        CStopWatch stop2;

                        
            for(f = 0;f<100000; f++)

                                    testClass.SplitToEvenOddSeqs2(spSrc,spDst,
            10240,10240);

                        now 
            = stop2.Now();

                        printf(”time4 
            =%d\n”,now);

                        CompareData(spDst,pSrcTest);

                        memset(spDst,
            0,10240<<1);

                        CStopWatch stop3;

                        
            for(f = 0;f<100000; f++)

                                    testClass.SplitToEvenOddSeqs3(spSrc,spDst,
            10240,10240);

                        now 
            = stop3.Now();

                        printf(”time5 
            =%d\n”,now);

                        CompareData(spDst,pSrcTest);

                        
            return 0;

            }


            注:其中CStopWatch是我寫的用來計算時間的類。

            如果把CompareData中訪問spDst的代碼注釋掉,運(yùn)行的結(jié)果:

            Intel® Core™2 CPU 6400 2.13Ghz 1GB

            time2 =753945 us

            time3 =494852 us

            time4 =0 us

            time5 =0 us

            Intel® Core™2 Duo CPU T7250 @2.00GHz 2.00 GHz 2GB

            Time2 = 847431 us

            Time3=523269 us

            Time4=1 us

            Time5 =1 us

            Pentium® 4 CPU 2.6 GHz  512MB

            Time2 = 613622 us

            Time3=616545 us

            Time4=1 us

            Time5 =1 us

            如果使用VC6編譯,各種運(yùn)行結(jié)果如下:

            Intel® Core™2 CPU 6400 2.13Ghz 1GB

            time2 =2041530 us

            time3 =1352753 us

            time4 =930849 us

            time5 =501492 us

            Intel® Core™2 Duo CPU T7250 @2.00GHz 2.00 GHz 2GB

            time2 =1878766 ustime3 =1380009 ustime4 =959918 us

            time5 =523022 us

            Pentium® 4 CPU 2.6 GHz  512MB

            time2 =2098438 us

            time3 =1855219 us

            time4 =1068678 us

            time5 =610458 us

            再把CompareData還原,在VC2005中編譯,執(zhí)行結(jié)果如下:

            Intel® Core™2 CPU 6400 2.13Ghz 1GB

            time2 =1007759 us

            time3 =1364986 us

            time4 =876046 us

            time5 =437623 us

            Intel® Core™2 Duo CPU T7250 @2.00GHz 2.00 GHz 2GB

            time2 =1103970 ustime3 =1403941 ustime4 =630279 ustime5 =313330 us 

            Pentium® 4 CPU 2.6 GHz  512MB

            time2 =1218860 ustime3 =1743361 ustime4 =478785 us

            time5 =241885 us

            使用VC6重新編譯:

            Intel® Core™2 CPU 6400 2.13Ghz 1GB

            time2 =2026392 us

            time3 =1359155 us

            time4 =946604 us

            time5 =511307 us

            Intel® Core™2 Duo CPU T7250 @2.00GHz 2.00 GHz 2GB

            time2 =1921379 ustime3 =1410035 ustime4 =967616 ustime5 =528601 us 

            Pentium® 4 CPU 2.6 GHz  512MB

            time2 =2089173 ustime3 =1849719 ustime4 =1062956 ustime5 =610357 us 

            當(dāng)然這里有重復(fù)運(yùn)算對算法的運(yùn)行時間的影響;但考慮所有的算法都是對同樣的內(nèi)存操作,不考慮。那么我們發(fā)現(xiàn)的就是算法的效率提高是明顯的。算法運(yùn)行時間縮短為原來的1/31/4 

            另外有幾個問題需要在這里討論一下:

            1.演示了時間問題的同時,還看到一個奇怪的問題就是如果注釋了CompareData,在VC2005上得到的后面兩個算法的時間幾乎為0。為什么?而VC6的編譯沒有這樣的現(xiàn)象?

            2.VC6上編譯得到的結(jié)果與VC2005編譯得到的結(jié)果相比,VC2005結(jié)果更好,為什么?(這個很弱智了)

            3.我覺得程序還可以再優(yōu)化,怎么樣做?

            歡迎大家就這個簡單的優(yōu)化問題,提出討論。

            posted on 2007-12-07 06:11 笨笨 閱讀(2469) 評論(19)  編輯 收藏 引用 所屬分類: 編碼

            評論

            # re: 一段代碼優(yōu)化的討論[未登錄] 2007-12-07 08:28 cppexplore

            根本原因要看編譯器 優(yōu)化 后的匯編代碼
            重構(gòu)代碼的出發(fā)點是可讀性 可維護(hù)性 不是優(yōu)化
            系統(tǒng)性能依賴于設(shè)計階段 之后就是關(guān)鍵算法 性能工具檢測的性能瓶頸處了  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 08:43 搞笑

            搞笑,樓主連一點cache優(yōu)化的意識都沒有,卻在加幾次上面死磕,看來又是一個工作了3-4年的落伍人士,呵呵。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 09:08 笨笨

            @搞笑
            說的很好!看來你沒有把文章看完。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 09:10 笨笨

            @cppexplore
            你的出發(fā)點我很贊同,就是算法的優(yōu)劣比優(yōu)化要好前百倍。
            我這里的例子你也看到了,是一個非常簡單的問題,好比memcpy或strcpy
            所以這里算法的改進(jìn),以我的笨眼來看,應(yīng)該沒有什么余地了。
            這里只是討論一點優(yōu)化的知識;我首先告訴大家,我是初學(xué)這個方面的人。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 09:16 笨笨

            我希望大家能夠給出一些真正的意見和想法,或者是代碼來讓這段代碼運(yùn)行的更快。這也是我的發(fā)這個貼的目的。

            也希望大家可以了解到有的應(yīng)該可以跑的比我們想像的快。
            我不想得到一些無聊的建議或話。
            謝謝!  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 09:17 LouixG

            你應(yīng)該看看自己優(yōu)化代碼的匯編,你在文中說了一些讀和寫的計數(shù),不知你是否是看了匯編后得出的結(jié)論。不同的編譯器優(yōu)化的結(jié)果也不一樣,你用Intel的編譯器試試,效果應(yīng)該會更好。
            我想到一些優(yōu)化辦法:
            1,循環(huán)展開;
            2,多線程;//這兩個是高級語言代碼優(yōu)化,下面是面向特定機(jī)器的指令優(yōu)化。
            3,利用更強(qiáng)的單指令數(shù)據(jù)操縱能力:如果目標(biāo)機(jī)器是32位,可以用浮點指令fld和fstp裝載64位數(shù)據(jù);如果目標(biāo)機(jī)器是64位的那么可以直接使用64位寄存器;
            4,利用SSE指令,SSE指令集對應(yīng)的寄存器提供最多128位數(shù)據(jù)寬度;
            5,如果你的目標(biāo)機(jī)器不是兼容機(jī)而是PowerPC、SPARC或者嵌入式的架構(gòu),那推薦你查閱對應(yīng)的CPU手冊,在指令層次上尋找突破。

              回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 09:50 笨笨

            @LouixG
            謝謝。應(yīng)該看出來是這方面的高手,希望多指點。
            想得到一點建議就是:程序性能優(yōu)化如何才能入門?  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 11:46 夢在天涯

            看了大家的回復(fù),深有感觸啊,

            非常高興可以看到C++ Blog這里聚集了越來越多的高手!

              回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論[未登錄] 2007-12-07 12:35 cppexplore

            @搞笑
            這樣說就不對了 文章寫出來 大家share下 目的是互補(bǔ)長短 互相交流 互相進(jìn)步
            @笨笨
            從本文中例子來說 這種“優(yōu)化”,可讀性更好,更好維護(hù)。的確是正確的。這種差異不是算法的造成的,是開始設(shè)計的不合理。另,我的建議真的不是無聊的建議 :)。
            @LouixG
            (1)展開循環(huán)的點滴性能不是問題
            (2)多線程模型的目的一般系統(tǒng)設(shè)計層面的吧,主要是提高系統(tǒng)的吞吐能力,這種問題上的性能遠(yuǎn)遠(yuǎn)談不上
            (3)后面的問題帶來移植性的問題,非底層的關(guān)鍵算法 也不會有人去做這種優(yōu)化
            @夢在天涯
            blog里的文章真是多啊

            @me
            廢話真多。。。。。。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 13:57 LouixG

            @cppexplore
            每種優(yōu)化可能只提高很小的性能,但為了達(dá)到極致把這些方法綜合就能產(chǎn)生質(zhì)的飛躍。優(yōu)化也分算法優(yōu)化和代碼優(yōu)化,我的想法就是在樓主算法確定的情況下最大可能的優(yōu)化實現(xiàn)代碼。
            提到多線程主要是為了利用多核,但小數(shù)據(jù)量不推薦多線程,提到多核就多說點,在多核上跑得快的代碼不一定在單核上跑的好,請看代碼:
            int * src1 = src;
            int * src2 = src + 1;
            int * dst1 = dst;
            int * dst2 = dst + 1;
            while ( ... )
            {
            *dst1 = *src1;
            *dst2 = *src2;
            dst1 += 2;
            dst2 += 2;
            src1 += 2;
            src2 += 2;
            };
            這個代碼可以肯定在單核上跑得很爛,但是在多核上dst1和dst2被流水線確定為無關(guān)變量,不相互影響就會被雙核同時執(zhí)行,這樣就比單核上跑得快。

            在指令層面上思考優(yōu)化不一定帶來移植性問題,例如這段代碼:
            double * src_opt = ( ( double * ) src ) - 1;
            double * dst_opt = ( ( double * ) dst ) - 1;
            int src_ind = 0;
            int dst_ind = 0;
            int loop_time = ( src_len >> 1 ) + 1;
            while ( --loop_time )
            {
            dst_opt[ ++dst_ind ] = src_opt[ ++src_ind ];
            }
            Intel編譯器產(chǎn)生的while循環(huán)的匯編:
            $B68$3:
            add edx, 1
            fld QWORD PTR [edi+edx*8-8]
            add eax, 1
            fstp QWORD PTR [esi+eax*8-8]
            add ecx, -1
            jne $B68$3
            這樣的代碼可以在兼容機(jī)上編譯+運(yùn)行無阻,且不說前兩個add可被替換inc獲得一定效率提升,我相信這段代碼要比樓主的代碼快。

            只有有沒有人做這樣的工作就仁者見仁了,肯定有需要這種工作的時候。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 14:22 金慶

            怎么會 0 us? 計時有問題吧?
            time4 =0 us
            time5 =0 us

            看代碼
            printf(”time5 =%d\n”,now);
            沒有打印"us". 應(yīng)該保持代碼與結(jié)果的一致.

            即然輸出與輸出的內(nèi)存結(jié)構(gòu)一致,直接用memcpy就行了,不必循環(huán)賦值.  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論[未登錄] 2007-12-07 14:55 cppexplore

            @LouixG
            (1)這個問題不敢妄言,多核下編程重來沒有接觸過。不知道這種各個cpu的分配,是在編譯期間由編譯器完成的,還是在運(yùn)行期間由額外的硬件決定把指令分配給某個cpu的?這種多核下的編程,值得探討的問題就多了,尤其這種在一個線程內(nèi)的數(shù)據(jù)被分配到多個cpu,如果真有這種情況,估計以后會有語言層面的東西支持。不同線程分配不同的cpu,這個到還好。
            (2)這個例子的高性能我絲毫不懷疑,通過字節(jié)對齊提高性能。其實標(biāo)準(zhǔn)庫函數(shù)也是這么做的。不過這個例子沒意義啊,直接使用庫函數(shù)就好。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 16:22 笨笨

            感謝大家的回復(fù)
            我沒有說cppexplore說的是無聊的啊。

            大家說的都很好。
            我這段代碼只是我自己在隨便看了兩片文章后對我以前一段代碼進(jìn)行的優(yōu)化;這里面僅僅是用到了幾點:
            1.合并同時訪問的兩個數(shù)組
            2.使用多字節(jié)復(fù)制
            3.替換比較而用減法
            4.減少指令的數(shù)目

            實際上我正學(xué)習(xí)這方面的編程,沒有想到這里竟然一下子碰到兩個高手啊,這么不見你們的BLOG?

            另外,我看到一個比系統(tǒng)的memcpy寫的還要快的內(nèi)存復(fù)制函數(shù),大家不妨看一下:
            http://www.vik.cc/daniel/portfolio/memcpy.htm  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 16:24 笨笨

            碰到二位強(qiáng)人真是我的幸運(yùn),希望能得到多多指點。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 17:04 haskell

            如果是為優(yōu)化而優(yōu)化,那就管不了那么多了
            void haha(short *sp,short *dp1,short *dp2,int count)
            {
            switch (count % 8) {
            case 0: do { *dp1++ = *sp++;
            case 7: *dp2++ = *sp++;
            case 6: *dp1++ = *sp++;
            case 5: *dp2++ = *sp++;
            case 4: *dp1++ = *sp++;
            case 3: *dp2++ = *sp++;
            case 2: *dp1++ = *sp++;
            case 1: *dp2++ = *sp++;
            } while ((count -= 8) > 0);
            }
            }
            可對for語句優(yōu)化,看有幫助沒^_^能編譯的  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-07 19:20 空明流轉(zhuǎn)

            最近在寫一個軟件渲染器,流水線還沒通,哪管他優(yōu)不優(yōu)化。。。

            大量的std::vector。。。等流水通了把Shader Register改成boost::array,再加上一個pool應(yīng)該會快不少吧。反正debug下渲染一個512 * 512的要好幾秒時間。。。  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-08 01:39 笨笨

            @haskell
            你這個這么多的條件分支,CPU命中率不是很低???  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-08 01:59 haskell

            忘了這個方法叫什么了?
            不會慢的  回復(fù)  更多評論   

            # re: 一段代碼優(yōu)化的討論 2007-12-12 11:14 no name

            haskell的方法叫 達(dá)夫設(shè)備Duff's Device,google可找到更多解釋。注意其中每個case都特意少了break,使用了switch的fall though特性。  回復(fù)  更多評論   

            精品久久久久久成人AV| 亚洲中文字幕无码久久2020 | 青草国产精品久久久久久| 精品无码人妻久久久久久| 久久精品国产亚洲Aⅴ蜜臀色欲| 亚洲国产成人久久精品99| 久久婷婷色综合一区二区| 久久久精品国产sm调教网站| AV狠狠色丁香婷婷综合久久| 国产一区二区三精品久久久无广告| 久久精品无码免费不卡| 中文字幕无码免费久久| 久久久久四虎国产精品| 久久亚洲国产成人影院| 久久人人爽人人精品视频| 久久久www免费人成精品| 香蕉久久一区二区不卡无毒影院| 久久久久九国产精品| 久久天天躁狠狠躁夜夜网站| 久久精品国产精品亚洲| 久久人人爽爽爽人久久久| 青青久久精品国产免费看| 国产精品久久久久…| 久久午夜夜伦鲁鲁片免费无码影视| 亚洲精品tv久久久久| 成人久久综合网| 久久亚洲中文字幕精品一区| 99久久免费只有精品国产| 精品国产乱码久久久久久人妻| 日本精品久久久久中文字幕| 久久久www免费人成精品| 久久无码国产| 91精品久久久久久无码| 久久久久亚洲AV成人片| 偷偷做久久久久网站| 久久久久国产日韩精品网站| 久久天堂电影网| 俺来也俺去啦久久综合网| 99精品国产综合久久久久五月天| 久久久久久极精品久久久| 91精品国产91热久久久久福利|