莊周夢(mèng)蝶

          生活、程序、未來(lái)
             :: 首頁(yè) ::  ::  :: 聚合  :: 管理

               摘要: 簡(jiǎn)單的web server性能測(cè)試,一般多線程方式與采用jdk5線程池的比較。  閱讀全文

          posted @ 2007-08-29 18:10 dennis 閱讀(4735) | 評(píng)論 (6)編輯 收藏

              linux/unix系統(tǒng)的I/O也就是一般所說(shuō)的低級(jí)I/O——操作系統(tǒng)提供的基本IO服務(wù),與os綁定,特定于*nix平臺(tái)。而標(biāo)準(zhǔn)I/O是ANSI C建立的一個(gè)標(biāo)準(zhǔn)I/O模型,是一個(gè)標(biāo)準(zhǔn)函數(shù)包和stdio.h頭文件中的定義,具有一定的可移植性。兩者一個(gè)顯著的不同點(diǎn)在于,標(biāo)準(zhǔn)I/O默認(rèn)采用了緩沖機(jī)制,比如調(diào)用fopen函數(shù),不僅打開(kāi)一個(gè)文件,而且建立了一個(gè)緩沖區(qū)(讀寫(xiě)模式下將建立兩個(gè)緩沖區(qū)),還創(chuàng)建了一個(gè)包含文件和緩沖區(qū)相關(guān)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。低級(jí)I/O一般沒(méi)有采用緩沖,需要自己創(chuàng)建緩沖區(qū),不過(guò)其實(shí)在*nix系統(tǒng)中,都是有使用稱(chēng)為內(nèi)核緩沖的技術(shù)用于提高效率,讀寫(xiě)調(diào)用是在內(nèi)核緩沖區(qū)和進(jìn)程緩沖區(qū)之間進(jìn)行的數(shù)據(jù)復(fù)制。

          1.fopen與open
          標(biāo)準(zhǔn)I/O使用fopen函數(shù)打開(kāi)一個(gè)文件
          FILE* fp=fopen(const char* path,const char *mod)

          其中path是文件名,mod用于指定文件打開(kāi)的模式的字符串,比如"r","w","w+","a"等等,可以加上字母b用以指定以二進(jìn)制模式打開(kāi)(對(duì)于*nix系統(tǒng),只有一種文件類(lèi)型,因此沒(méi)有區(qū)別),如果成功打開(kāi),返回一個(gè)FILE文件指針,如果失敗返回NULL,這里的文件指針并不是指向?qū)嶋H的文件,而是一個(gè)關(guān)于文件信息的數(shù)據(jù)包,其中包括文件使用的緩沖區(qū)信息。

          *nix系統(tǒng)使用open函數(shù)用于打開(kāi)一個(gè)文件
          int fd=open(char *name,int how);
          與fopen類(lèi)似,name表示文件名字符串,而how指定打開(kāi)的模式:O_RDONLY(只讀),O_WRONLY(只寫(xiě)),O_RDWR (可讀可寫(xiě)),還有其他模式請(qǐng)man 2 open。成功返回一個(gè)正整數(shù)稱(chēng)為文件描述符,這與標(biāo)準(zhǔn)I/O顯著不同,失敗的話返回-1,與標(biāo)準(zhǔn)I/O返回NULL也是不同的。

          2.fclose與close
          與打開(kāi)文件相對(duì)的,標(biāo)準(zhǔn)I/O使用fclose關(guān)閉文件,將文件指針傳入即可,如果成功關(guān)閉,返回0,否則返回EOF
          比如:
          if(fclose(fp)!=0)  
            printf(
          "Error in closing file");

          而*nix使用close用于關(guān)閉open打開(kāi)的文件,與fclose類(lèi)似,只不過(guò)當(dāng)錯(cuò)誤發(fā)生時(shí)返回的是-1,而不是EOF,成功關(guān)閉同樣是返回0。C語(yǔ)言用error code來(lái)進(jìn)行錯(cuò)誤處理的傳統(tǒng)做法。

          3.讀文件,getc,fscanf,fgets和read
          標(biāo)準(zhǔn)I/O中進(jìn)行文件讀取可以使用getc,一個(gè)字符一個(gè)字符的讀取,也可以使用gets(讀取標(biāo)準(zhǔn)io讀入的)、fgets以字符串單位進(jìn)行讀取(讀到遇到的第一個(gè)換行字符的后面),gets(接受一個(gè)參數(shù),文件指針)不判斷目標(biāo)數(shù)組是否能夠容納讀入的字符,可能導(dǎo)致存儲(chǔ)溢出(不建議使用),而fgets使用三個(gè)參數(shù):
           char * fgets(char *s, int size, FILE *stream);
          第一個(gè)參數(shù)和gets一樣,用于存儲(chǔ)輸入的地址,第二個(gè)參數(shù)為整數(shù),表示輸入字符串的最大長(zhǎng)度,最后一個(gè)參數(shù)就是文件指針,指向要讀取的文件。最后是fscanf,與scanf類(lèi)似,只不過(guò)增加了一個(gè)參數(shù)用于指定操作的文件,比如fscanf(fp,"%s",words)

          *nix系統(tǒng)中使用read函數(shù)用于讀取open函數(shù)打開(kāi)的文件,函數(shù)原型如下:
          ssize_t numread=read(int fd,void *buf,size_t qty);

          其中fd就是open返回的文件描述符,buf用于存儲(chǔ)數(shù)據(jù)的目的緩沖區(qū),而qty指定要讀取的字節(jié)數(shù)。如果成功讀取,就返回讀取的字節(jié)數(shù)目(小于等于qty)。

          4.判斷文件結(jié)尾,如果嘗試讀取達(dá)到文件結(jié)尾,標(biāo)準(zhǔn)IO的getc會(huì)返回特殊值EOF,而fgets碰到EOF會(huì)返回NULL,而對(duì)于*nix的read函數(shù),情況有所不同。read讀取qty指定的字節(jié)數(shù),最終讀取的數(shù)據(jù)可能沒(méi)有你所要求的那么多(qty),而當(dāng)讀到結(jié)尾再要讀的話,read函數(shù)將返回0.

          5.寫(xiě)文件:putc,fputs,fprintf和write

          與讀文件相對(duì)應(yīng)的,標(biāo)準(zhǔn)C語(yǔ)言I/O使用putc寫(xiě)入字符,比如:
          putc(ch,fp);
          第一個(gè)參數(shù)是字符,第二個(gè)是文件指針。而fputs與此類(lèi)似:
          fputs(buf,fp);
          僅僅是第一個(gè)參數(shù)換成了字符串地址。而fprintf與printf類(lèi)似,增加了一個(gè)參數(shù)用于指定寫(xiě)入的文件,比如:
          fprintf(stdout,"Hello %s.\n","dennis");
          切記fscanf和fprintf將FILE指針作為第一個(gè)參數(shù),而putc,fputs則是作為第二個(gè)參數(shù)。

          在*nix系統(tǒng)中提供write函數(shù)用于寫(xiě)入文件,原型與read類(lèi)似:
          ssize_t result=write(int fd,void *buf ,size_t amt);

          fd是文件描述符,buf是將要寫(xiě)入的內(nèi)存數(shù)據(jù),amt是要寫(xiě)的字節(jié)數(shù)。如果寫(xiě)入成功返回寫(xiě)入的字節(jié)數(shù),通過(guò)result與amt的比較可以判斷是否寫(xiě)入正常,如果寫(xiě)入失敗返回-1。write函數(shù)僅僅是將數(shù)據(jù)寫(xiě)入了緩沖區(qū),何時(shí)寫(xiě)入磁盤(pán)由內(nèi)核決定,如果要強(qiáng)制寫(xiě)入硬盤(pán),那么在open的時(shí)候選擇O_SYNC選項(xiàng),或者調(diào)用fsync函數(shù)

          6.隨機(jī)存取:fseek()、ftell()和lseek()
          標(biāo)準(zhǔn)I/O使用fseek和ftell用于文件的隨機(jī)存取,先看看fseek函數(shù)原型
          int fseek(FILE *stream, long offset, int whence);
          第一個(gè)參數(shù)是文件指針,第二個(gè)參數(shù)是一個(gè)long類(lèi)型的偏移量(offset),表示從起始點(diǎn)開(kāi)始移動(dòng)的距離。第三個(gè)參數(shù)就是用于指定起始點(diǎn)的模式,stdio.h指定了下列模式常量:
          SEEK_SET            文件開(kāi)始處
          SEEK_CUR            當(dāng)前位置
          SEEK_END            文件結(jié)尾處
          看幾個(gè)調(diào)用例子:
          fseek(fp,0L,SEEK_SET);  //找到文件的開(kāi)始處
          fseek(fp,0L,SEEK_END);  //定位到文件結(jié)尾處
          fseek(fp,2L,SEEK_CUR);  //文件當(dāng)前位置向前移動(dòng)2個(gè)字節(jié)數(shù)

          而ftell函數(shù)用于返回文件的當(dāng)前位置,返回類(lèi)型是一個(gè)long類(lèi)型,比如下面的調(diào)用:
          fseek(fp,0L,SEEK_END);//定位到結(jié)尾
          long last=ftell(fp);  //返回當(dāng)前位置
          那么此時(shí)的last就是文件指針fp指向的文件的字節(jié)數(shù)。

          與標(biāo)準(zhǔn)I/O類(lèi)似,*nix系統(tǒng)提供了lseek來(lái)完成fseek的功能,原型如下:
          off_t lseek(int fildes, off_t offset, int whence);

          fildes是文件描述符,而offset也是偏移量,whence同樣是指定起始點(diǎn)模式,唯一的不同是lseek有返回值,如果成功就返回指針變化前的位置,否則返回-1。因此可以通過(guò)下列方法模擬ftell函數(shù)來(lái)返回當(dāng)前偏移量:
          off_t    currpos;
          currpos = lseek(fd, 0, SEEK_CUR);
          whence的取值與fseek相同:SEEK_SET,SEEK_CUR,SEEK_END,但也可以用整數(shù)0,1,2相應(yīng)代替。

          最后,以一個(gè)例子結(jié)尾,通過(guò)c語(yǔ)言編寫(xiě)linux系統(tǒng)的cp指令,先看看使用標(biāo)準(zhǔn)I/O版本的:
          #include<stdio.h>
          #include
          <stdlib.h>
          void oops(char *,char *);
          int main(int ac,char *av[])
          {
            FILE 
          *in,*out;
            
          int ch;

            
          if(ac!=3){
             fprintf(stderr,
          "Useage:%s source-file target-file.\n",av[0]);
             exit(
          1);
            }
            
          if((in=fopen(av[1],"r"))==NULL)
             oops(
          "can not open ",av[1]);
            
          if((out=fopen(av[2],"w"))==NULL)
             oops(
          "can not open ",av[2]);
            
          while((ch=getc(in))!=EOF)
              putc(ch,out);
            
          if(fclose(in)!=0||fclose(out)!=0)
              oops(
          "can not close files.\n"," ");
            
          return 0;
          }
          void oops(char *s1,char* s2)
          {
            fprintf(stderr,
          "Error:%s %s\n",s1,s2);
            exit(
          1);
          }

          再看一個(gè)使用unix io的版本:
          #include<unistd.h>
          #include
          <stdio.h>
          #include
          <fcntl.h>

          #define BUFFERSIZE 
          4096
          #define COPYMODE 
          0644
          void oops(char *,char *);

          int main(int ac,char *av[])
          {
            
          int in_fd,out_fd,n_chars;
            
          char buf[BUFFERSIZE];
            
          if(ac!=3){
              fprintf(stderr,
          "useage:%s source-file target-file.\n",av[0]);
              exit(
          1);
            }

            
          if((in_fd=open(av[1],O_RDONLY))==-1)
               oops(
          "Can't open ",av[1]);
            
          if((out_fd=creat(av[2],COPYMODE))==-1)
              oops(
          "Can't open ",av[2]);
            
          while((n_chars=read(in_fd,buf,BUFFERSIZE))>0)
                
          if(write(out_fd,buf,n_chars)!=n_chars)
                     oops(
          "Write error to ",av[2]);
            
          if(n_chars==-1)
                oops(
          "Read error from ",av[1]);
            
          if(close(in_fd)==-1||close(out_fd)==-1)
              oops(
          "Error closing files","");
            
          return 0;
          }
          void oops(char *s1,char *s2)
          {
            fprintf(stderr,
          "Error:%s",s1);
            perror(s2);
            exit(
          1);
          }

          顯然,在使用unix i/o的時(shí)候,你要更多地關(guān)注緩沖問(wèn)題以提高效率,而stdio則不需要考慮。






          posted @ 2007-08-22 16:47 dennis 閱讀(1333) | 評(píng)論 (0)編輯 收藏

              這一周一口氣開(kāi)發(fā)了4個(gè)新功能,修正了十幾個(gè)客戶提出的新要求,設(shè)計(jì)了新的績(jī)效評(píng)價(jià)算法,將《Programming Erlang》讀到第11章,《C primer plus》讀到14章,開(kāi)始讀《unix/linux編程實(shí)踐》,寫(xiě)了數(shù)十道C習(xí)題,累,而又充實(shí)。今天下午,前幾天聯(lián)系我的一家美國(guó)公司聘請(qǐng)ROR兼職的在MSN面試了一下,一開(kāi)始沒(méi)有意識(shí)到是面試,對(duì)自己的表現(xiàn)很不滿意,先問(wèn)下了我對(duì)自己優(yōu)勢(shì)和弱點(diǎn)的看法,然后就針對(duì)這些開(kāi)始提問(wèn),問(wèn)了sql語(yǔ)句,一般的db設(shè)計(jì)應(yīng)該注意的問(wèn)題,數(shù)據(jù)結(jié)構(gòu)中queue和stack區(qū)別,最后發(fā)了封email,一個(gè)比較長(zhǎng)的需求分析,考察我的E文理解能力和需求分析、設(shè)計(jì)能力。對(duì)自己的回答并不滿意吧,我覺(jué)的我沒(méi)有完全展現(xiàn)出自己的能力,也是太累了,回答完剛好下班,躺在沙發(fā)上竟然睡著了。能不能成也要看運(yùn)氣了,做ROR兼職真是項(xiàng)有趣的挑戰(zhàn),我很希望試試。

          posted @ 2007-08-10 18:40 dennis 閱讀(386) | 評(píng)論 (1)編輯 收藏

               摘要:     這是最近在項(xiàng)目中的一個(gè)需求,已知a=3,求字符串"a<=2"的值,也就是應(yīng)該返回false。這個(gè)問(wèn)題可大可小,就我們的應(yīng)用場(chǎng)景也就是用來(lái)讓用戶自定義變量區(qū)間,比如類(lèi)似下面這樣的規(guī)則:a<=2    返回積分系數(shù)1.02<a<=5  返回積分系數(shù)1.1a>5   ...  閱讀全文

          posted @ 2007-08-06 12:37 dennis 閱讀(7976) | 評(píng)論 (7)編輯 收藏

              這是Programming Erlang第8章節(jié)的一個(gè)練習(xí),創(chuàng)建N個(gè)process,連接成一個(gè)圈,然后在這個(gè)圈子里發(fā)送消息M次,看看時(shí)間是多少,然后用另一門(mén)語(yǔ)言寫(xiě)同樣的程序,看看時(shí)間是多少。我自己寫(xiě)的版本在處理3000個(gè)進(jìn)程,1000次消息循環(huán)(也就是300萬(wàn)次消息傳遞)時(shí)花了5秒多,后來(lái)去google別人寫(xiě)的版本,竟然讓我找到一個(gè)98年做的benchmark:Erlang vs. java,也是同樣的的問(wèn)題。測(cè)試的結(jié)果是Erlang性能遠(yuǎn)遠(yuǎn)大于java,這也是顯然的結(jié)果,Erlang的process是輕量級(jí)、無(wú)共享的,而java的線程是os級(jí)別的,兩者創(chuàng)建的cost不可同日而語(yǔ)。詳細(xì)的比較請(qǐng)看這里
              不過(guò)我分析了這個(gè)測(cè)試?yán)锏腅rlang代碼,存在問(wèn)題,并沒(méi)有完成所有的循環(huán),進(jìn)程就結(jié)束了,這對(duì)比較結(jié)果有較大的影響。原始代碼如下:

          -module(zog).

          %% This is a test program that first creates N processes (that are
          %% "connected" in a ring) and then sends M messages in that ring.
          %%
          %% - September 1998
          %% - roland


          -export([start/0, start/1, start/2]).

          -export([run/2, process/1]). % Local exports - ouch

          start() -> start(16000).

          start(N) -> start(N, 1000000).

          start(N, M) -> spawn(?MODULE, run, [N, M]).


          run(N, M) when N < 1 ->
          io:format("Must be at least 1 process~n", []),
          0.0;
          run(N, M) ->
          statistics(wall_clock),

          Pid = setup(N-1, self()),

          {_,T1} = statistics(wall_clock),
          io:format("Setup : ~w s", [T1/1000]),
          case N of
          1 -> io:format(" (0 spawns)~n", []);
          _ -> io:format(" (~w us per spawn) (~w spawns)~n",
          [1000*T1/(N-1), N-1])
          end,
          statistics(wall_clock),

          Pid ! M,
          K = process(Pid),

          {_,T2} = statistics(wall_clock),
          Time = 1000*T2/(M+K),
          io:format("Run : ~w s (~w us per msg) (~w msgs)~n",
          [T2/1000, Time, (M+K)]),

          Time.

          setup(0, OldPid) ->
          OldPid;
          setup(N, OldPid) ->
          NewPid = spawn(?MODULE, process, [OldPid]),
          setup(N-1, NewPid).


          process(Pid) ->
          receive
          M ->
          Pid ! M-1,
          if
          M < 0 -> -M;
          true -> process(Pid)
          end
          end.
            我將process修改一下
          process(Pid) ->
          receive
          M ->
          Pid ! M-1,
          io:format("form ~w to ~w~n",[self(),Pid]),
          if
          M < 0 -> -M;
          true -> process(Pid)
          end
          end.
          然后執(zhí)行下zog:run(3,3),你將發(fā)現(xiàn)消息繞了兩圈就結(jié)束了,第三圈根本沒(méi)有進(jìn)行,不知道測(cè)試者是什么用意。依照現(xiàn)在的執(zhí)行300萬(wàn)次消息傳送竟然只需要3毫秒!我修改了下了下代碼如下:
          -module(zog).

          %% This is a test program that first creates N processes (that are
          %% "connected" in a ring) and then sends M messages in that ring.
          %%
          %% - September 1998
          %% - roland
          -export([start/0, start/1, start/2]).

          -export([run/2, process/2]).                    % Local exports - ouch

          start() -> start(16000).

          start(N) -> start(N, 1000000).

          start(N, M) -> spawn(?MODULE, run, [N, M]).


          run(N, M) when N < 1 ->
              io:format("Must be at least 1 process~n", []),
              0.0;
          run(N, M) ->
              statistics(wall_clock),
              Limit=N-N*M+1+M,
              Pid = setup(N-1,Limit,self()),

              {_,T1} = statistics(wall_clock),
              io:format("Setup : ~w s", [T1/1000]),
              case N of
                  1 -> io:format(" (0 spawns)~n", []);
                  _ -> io:format(" (~w us per spawn) (~w spawns)~n",
                                 [1000*T1/(N-1), N-1])
              end,
              statistics(wall_clock),
            %  io:format("run's Pid=~w~n",[Pid]),
              Pid ! M,
              K = process(Pid,Limit),
            %  io:format("run's K=~w~n",[K]),

              {_,T2} = statistics(wall_clock),
              Time = 1000*T2/(M+K),
              io:format("Run   : ~w s (~w us per msg) (~w msgs)~n",
                        [T2/1000, Time, (M+K)]),
           T2/1000.

          setup(0,Limit, OldPid) ->
              OldPid;
          setup(N,Limit, OldPid) ->
              NewPid = spawn(?MODULE, process, [OldPid,Limit]),
              setup(N-1, Limit,NewPid).


          process(Pid,Limit) ->
              receive
                  M ->
                      Pid ! M-1,
                   %   io:format("from ~w to ~w and M=~w~n",[self(),Pid,M]),
                      if
                          M <Limit  -> -M;
                          true   -> process(Pid,Limit)
                      end
              end.
          修改之后,執(zhí)行zog:run(3000,1000),也就是3000個(gè)進(jìn)程,1000次消息循環(huán),總共300萬(wàn)次消息傳遞,結(jié)果在2.5秒左右,這也是相當(dāng)驚人的結(jié)果。有人用haskell和scheme各實(shí)現(xiàn)了一個(gè)版本,有興趣的看看這里這里


          posted @ 2007-08-04 17:46 dennis 閱讀(1531) | 評(píng)論 (0)編輯 收藏

              《C Primer Plus》讀到12章,我的C語(yǔ)言復(fù)習(xí)進(jìn)展的挺不錯(cuò)。這一章介紹存儲(chǔ)類(lèi)、連接和內(nèi)存管理,可以說(shuō)是重中之重。
          C的5種存儲(chǔ)類(lèi):
          自動(dòng)——在一個(gè)代碼塊內(nèi)(或在一個(gè)函數(shù)頭部作為參量)聲明的變量,無(wú)論有沒(méi)有存儲(chǔ)類(lèi)修飾符auton,都屬于自動(dòng)存儲(chǔ)類(lèi)。該類(lèi)具有自動(dòng)存儲(chǔ)時(shí)期、代碼塊的作用域和空鏈接(no linkage),如未初始化,它的值是不確定的(java要求局部變量必須初始化)

          寄存器——在一個(gè)代碼塊內(nèi)(或在一個(gè)函數(shù)頭部作為參量)使用修飾符register聲明的變量屬于寄存器存儲(chǔ)類(lèi)。該類(lèi)與自動(dòng)存儲(chǔ)類(lèi)相似,具有自動(dòng)存儲(chǔ)時(shí)期、代碼塊作用域和空連接,聲明為register僅僅是一個(gè)請(qǐng)求,而非命令,因此變量仍然可能是普通的自動(dòng)變量,但是仍然無(wú)法獲取地址。。如果沒(méi)有被初始化,它的值也是未定的。

          靜態(tài)、空鏈接——在一個(gè)代碼塊內(nèi)使用存儲(chǔ)類(lèi)修飾符static聲明的局部變量屬于靜態(tài)空連接存儲(chǔ)類(lèi)。該類(lèi)具有靜態(tài)存儲(chǔ)時(shí)期、代碼塊作用域和空鏈接,僅在編譯時(shí)初始化一次。如未明確初始化,它的字節(jié)將被設(shè)定為0.

          靜態(tài)、外部鏈接——在所有函數(shù)外部定義、未使用static修飾的變量屬于靜態(tài)、外部鏈接存儲(chǔ)類(lèi)。改類(lèi)具有靜態(tài)存儲(chǔ)時(shí)期、文件作用域和外部鏈接,僅在編譯時(shí)初始化一次。如未明確初始化,它的字節(jié)也被設(shè)定為0.

          靜態(tài)、內(nèi)部鏈接——與靜態(tài)、外部鏈接存儲(chǔ)類(lèi)不同的是,它使用static聲明,也定義在所有函數(shù)外部,但是具有內(nèi)部鏈接(僅能被與它在同一個(gè)文件的函數(shù)使用),僅在編譯時(shí)初始化一次。如未明確初始化,它的字節(jié)也被設(shè)定為0.

          兩個(gè)關(guān)鍵字:volatile和restrict,兩者都是為了方便編譯器的優(yōu)化。

          volatile告訴編譯器該被變量除了可被程序修改意外還可能被其他代理修改,因此,當(dāng)要求使用volatile 聲明的變量的值的時(shí)候,系統(tǒng)總是重新從它所在的內(nèi)存讀取數(shù)據(jù),而不是使用寄存器中的緩存。比如
          val1=x;
          val2=x;
          如果沒(méi)有聲明volatile,系統(tǒng)在給val2賦值的時(shí)候可能直接從寄存器讀取x(假定聰明的編譯器優(yōu)化了),而不是從內(nèi)存的初始位置,那么在兩次賦值之間,x完全有可能被被某些編譯器未知的因素更改(比如:操作系統(tǒng)、硬件或者其它線程等)。如果聲明為volatile,編譯器將不使用緩存,而是每次都從內(nèi)存重新讀取x。

          而restrict是c99引入的,它只可以用于限定指針,并表明指針是訪問(wèn)一個(gè)數(shù)據(jù)對(duì)象的唯一且初始的方式,考慮下面的例子:
          int ar[10];
          int * restrict restar=(int *)malloc(10*sizeof(int));
          int *par=ar;

          這里說(shuō)明restar是訪問(wèn)由malloc()分配的內(nèi)存的唯一且初始的方式。par就不是了。
          那么:
          for(n=0;n<10;n++)
          {
             par[n]+=5;
             restar[n]+=5;
             ar[n]*=2;
             par[n]+=3;
             restar[n]+=3;
          }
          因?yàn)閞estar是訪問(wèn)分配的內(nèi)存的唯一且初始的方式,那么編譯器可以將上述對(duì)restar的操作進(jìn)行優(yōu)化:
             restar[n]+=8;

          而par并不是訪問(wèn)數(shù)組ar的唯一方式,因此并不能進(jìn)行下面的優(yōu)化:
             par[n]+=8;
          因?yàn)樵趐ar[n]+=3前,ar[n]*=2進(jìn)行了改變。使用了關(guān)鍵字restric,編譯器就可以放心地進(jìn)行優(yōu)化了。這個(gè)關(guān)鍵字據(jù)說(shuō)來(lái)源于古老的FORTRAN。有興趣的看看這個(gè)

          posted @ 2007-08-04 15:34 dennis 閱讀(9482) | 評(píng)論 (1)編輯 收藏

              昨天在讀到《Programming Erlang》第8章,開(kāi)篇點(diǎn)出Erlang是一門(mén)純粹的消息傳遞風(fēng)格語(yǔ)言(message passing),我才算是領(lǐng)悟了消息傳遞。為了這個(gè)問(wèn)題,我還冒昧地去問(wèn)javaeye上的T1,對(duì)這個(gè)問(wèn)題的興趣是因?yàn)镾ICP第二章以及《失蹤的鏈環(huán)》上的介紹。T1給我解答如下:智能能對(duì)象只是消息傳遞的一種具體應(yīng)用.消息傳遞說(shuō)的更為清晰一些就是一種映射關(guān)系或者說(shuō)映射規(guī)則.f:a->b;這個(gè)規(guī)則可以是任意的。我一直將procedural representations of data,也就是sicp中聲稱(chēng)intelligent data objects (智能對(duì)象)等價(jià)于消息傳遞, 而其實(shí)智能對(duì)象僅僅是消息傳遞的一種具體應(yīng)用罷了。
          消息傳遞機(jī)制通俗地來(lái)講就是類(lèi)似于馬路上到處投遞小廣告的投遞者,它采取的是Send and Pray策略,既不關(guān)心消息是否能精確的傳送到真正需要消息的接收者,而是以廣播的方式把消息發(fā)送給所有人,然后通過(guò)回饋來(lái)確定消息接收者的類(lèi)型(引自《失蹤的鏈環(huán)》)。因此,動(dòng)態(tài)語(yǔ)言的duct typing是消息傳遞風(fēng)格,智能對(duì)象是消息傳遞風(fēng)格,顯然,Erlang的process間的通信機(jī)制同樣是消息傳遞風(fēng)格(Process之間完全通過(guò)send message來(lái)進(jìn)行控制和指示,不確定接收方是否具有處理消息的能力 ,異步的,接收的確認(rèn)也要等待reply)。
              再來(lái)說(shuō)說(shuō)lambda算子理論,推薦下g9老大的lambda算子系列文章,這是開(kāi)篇《lambda算子簡(jiǎn)介1.a》,以及另外一篇《康托爾、哥德?tīng)枴D靈——永恒的金色對(duì)角線(rev#2)》。lambda算子理論是函數(shù)式編程的理論基礎(chǔ),通過(guò)9條公理就可以推到出一個(gè)圖靈完備的形式系統(tǒng),其中的Y combinator的推導(dǎo)簡(jiǎn)直是魔法(為了表示遞歸),再次領(lǐng)略了計(jì)算理論的魅力。另外,最近讀sicp第三章《模塊化、對(duì)象和狀態(tài)》,也理解了最初的面向?qū)ο笏枷雭?lái)自何處,在引入了內(nèi)部狀態(tài)模擬時(shí)間變化之后,對(duì)象的最初思想也產(chǎn)生了,同時(shí)也帶來(lái)了賦值導(dǎo)致的Side-Effect,而其實(shí)這正是動(dòng)態(tài)OO語(yǔ)言中的對(duì)象的理念,通過(guò)消息來(lái)決定對(duì)象的type(ducktyping)。可現(xiàn)代的靜態(tài)OO語(yǔ)言,在type和clas
          之間畫(huà)上了等號(hào),java里面說(shuō)一切都是object,其實(shí)他想表達(dá)的卻是一切都是class,通過(guò)type以及函數(shù)簽名等來(lái)決定消息的分派(message dispatch),導(dǎo)致更多的代碼集中在消息分派,而不是真正的計(jì)算任務(wù)上,可以說(shuō)靜態(tài)OO已經(jīng)偏離原始的對(duì)象模型很遠(yuǎn)。
              一點(diǎn)胡思亂想吧,我沒(méi)有科班經(jīng)歷,所有的東西都是自己在學(xué),在摸索,如有理論和常識(shí)上的謬誤,請(qǐng)不吝賜教。

          posted @ 2007-08-03 09:23 dennis 閱讀(1266) | 評(píng)論 (0)編輯 收藏

          習(xí)題3.6,我的實(shí)現(xiàn)如下:
          (define rand
            (let ((x 3))
               (lambda(arg)
                (cond((eq? arg 'generate)
                      ((lambda()(set! x (rand-update x)) x)))
                     ((eq? arg 'reset)
                      (lambda(init) (set! x init) (set! x (rand-update x)) x))
                     (else
                       error "Unkonown OP")))))
          簡(jiǎn)單解釋下,當(dāng)參數(shù)是generate時(shí),直接調(diào)用匿名lambda函數(shù)(lambda()(set! x (rand-update x)) x) ,最后返回隨機(jī)值。而當(dāng)?shù)谝粋€(gè)參數(shù)是reset時(shí),返回匿名函數(shù)(lambda(init) (set! x init) (set! x (rand-update x)) x),這個(gè)匿名函數(shù)接受一個(gè)新的初始值,并賦值給x,然后調(diào)用rand-update

          習(xí)題3.7,引入賦值的代價(jià)就是引入了副作用以及相應(yīng)的復(fù)雜性,3.3小節(jié)提出了命令式語(yǔ)言與函數(shù)式語(yǔ)言的基本差別。這一題,首先修改習(xí)題3.3(參見(jiàn)《sicp 3.1小結(jié)習(xí)題嘗試解答》),增加一個(gè)檢查密碼是否正確的功能,用以檢查輸入的原始帳戶密碼是否正確,make-account修改一下
          ;習(xí)題3.3
          (define (make-account2 balance passwd)
            (define (checkpwd pwd)
              (eq? pwd passwd))
            (define (withdraw amount)
              (if (>= balance amount)
                  (begin (set! balance (- balance amount)) balance)
                  "余額不足"))
            (define (deposit amount)
              (set! balance (+ balance amount))
              balance)
            (define (dispatch pwd m)
              (if (eq? pwd passwd)
                  (cond ((eq? m 'withdraw) withdraw)
                        ((eq? m 'deposit) deposit)
                        ((eq? m 'checkpwd) checkpwd)
                      (else
                         (error "Unknow request--MAKE-ACCOUNT" m)))
                  (lambda(x) "Incorrect password")))
                  
            dispatch)

          那么,make-joint可以寫(xiě)為:
          (define (make-joint account-name account-pass new-pass)
            (if (account-name 'checkpwd account-pass)
                (lambda (passwd m)
                (if (eq? new-pass passwd)
                    (account-name account-pass m)
                     (error "Incorrect password")))
                (error
                   "Incorrect password to the original account")))

          首先是檢查原始帳戶的密碼是否正確,正確返回匿名函數(shù)(兩個(gè)參數(shù)passwd m),此匿名函數(shù)檢查密碼以及調(diào)用原始帳戶操作;如果不正確就提示消息。
          測(cè)試一下:
          > (define dennis-acc (make-account2 100 '123))
          > (define zane-acc (make-joint dennis-acc '123 'abc))
          > ((dennis-acc '123 'withdraw) 10)
          90
          >  ((zane-acc 'abc 'withdraw) 10)
          80


          習(xí)題3.8,這一題比較簡(jiǎn)單了,在內(nèi)部維持一個(gè)狀態(tài)變量即可
          (define f
            (let ((y 1))
              (lambda(x) (set! y (* x y)) y)))
          測(cè)試可知,在(f 1) (f 0)執(zhí)行的順序不同時(shí),返回的值不同。


          posted @ 2007-08-01 15:45 dennis 閱讀(405) | 評(píng)論 (0)編輯 收藏

              知道這個(gè)模式還是通過(guò)《重構(gòu)》,這個(gè)模式的出現(xiàn)還是了為了解決代碼重復(fù)的壞味道。在項(xiàng)目中很經(jīng)常見(jiàn)到類(lèi)似下面這樣的代碼:
          if(prj.getProjectId==null)
              plan.setCost(
          0.0);
          else
              plan.setCost(prj.getCost());

             我們?cè)诤芏嗟胤接蓄?lèi)似的檢查對(duì)象是否為null,如果為null,需要一個(gè)默認(rèn)值等等這樣的場(chǎng)景。顯然,代碼重復(fù)是壞味道,怎么消除這個(gè)壞味道呢?答案就是使用NullObject替代之,Null Object繼承原對(duì)象。
          class NullProject extends Project{
             
          public boolean isNull(){
                
          return true;
             }
          }
          class Project{
             
          private double cost;
             
          private String projectId;
             .
             
          public boolean isNull(){
                  
          return false;
             }
          }

          那么,原來(lái)的代碼可以改寫(xiě)為:
          if(prj.isNull())
              plan.setCost(
          0.0);
          else
              plan.setCost(prj.getCost());

              如果Null Object的引入僅僅是帶來(lái)這個(gè)好處,似乎沒(méi)有理由讓我們多敲這么多鍵盤(pán)。問(wèn)題的關(guān)鍵是類(lèi)似上面這樣的判斷也許出現(xiàn)在很多處,那么有價(jià)值的技巧出現(xiàn)了,我們?cè)贜ullObject覆寫(xiě)getCost,提供缺省值:
          class NullProject extends Project{
             
          public boolean isNull(){
                
          return true;
             }
             
          public double getCost(){
                
          return 0.0;      
             }
          }
              因此,檢查對(duì)象是否為null的代碼可以去掉if...else了:
          plan.setCost(prj.getCost());

              請(qǐng)注意,只有那些大多數(shù)客戶端代碼都要求null object做出相同響應(yīng)時(shí),這樣的行為才有意義。比如我們這里當(dāng)工程id為null,很多地方要求費(fèi)用就默認(rèn)為0.0。 特殊的行為我們?nèi)匀皇褂胕sNull進(jìn)行判斷。
              當(dāng)然,另外在需要返回NullObject的地方,你應(yīng)該創(chuàng)建一個(gè)null object以替代一般的對(duì)象,我們可以建立一個(gè)工廠方法:

          class Project{
             
          private double cost;
             
          private String projectId;
             .
             
          public boolean isNull(){
                  
          return false;
             }
             
          public Project createNullProject(){
                  
          return new NullProject();
             }
          }

             Null Object模式帶來(lái)的好處:減少了檢查對(duì)象是否為null的代碼重復(fù),提高了代碼的可讀性,通常這些Null Object也可以為單元測(cè)試帶來(lái)簡(jiǎn)便。

          posted @ 2007-07-31 17:48 dennis 閱讀(5187) | 評(píng)論 (7)編輯 收藏

              亞洲杯我沒(méi)怎么關(guān)注,昨天本來(lái)是想找找看能不能看到酋長(zhǎng)杯的轉(zhuǎn)播,可這地方收不到廣東體育臺(tái),于是我就看亞洲杯決賽了,沒(méi)想到還真值了,非常精彩,好久沒(méi)有這樣忘情地看一場(chǎng)比賽了。誰(shuí)也沒(méi)有想到國(guó)內(nèi)沒(méi)有職業(yè)聯(lián)賽,自己的國(guó)家還在戰(zhàn)火中煎熬,隊(duì)員的親人近幾日在恐怖襲擊中喪生,教練是剛接手球隊(duì)兩個(gè)月,一開(kāi)始就沒(méi)有人看好他們的伊拉克隊(duì)闖進(jìn)了亞洲杯的決賽,他們已經(jīng)創(chuàng)造歷史,他們已經(jīng)親手締造了奇跡,不管決賽的結(jié)果如何。相比于他們,中國(guó)男子足球隊(duì)?wèi)?yīng)該找塊豆腐撞死,不,應(yīng)該解散他們。
              還是回到比賽,比賽中我一個(gè)細(xì)節(jié)讓我印象深刻,伊拉克的一個(gè)后防隊(duì)員奮不顧身地用頭去解圍一個(gè)低空球,沙特的隊(duì)員的腳堪堪從他的耳朵旁大力射門(mén)劃過(guò),解說(shuō)員說(shuō)伊拉克人真是拼了,連受傷都不怕了,那時(shí)我想,對(duì)于伊拉克隊(duì)員們來(lái)說(shuō),死亡都不可怕,更何況是區(qū)區(qū)的受傷。他們是在用靈魂在戰(zhàn)斗,他們希望用勝利來(lái)告慰苦難中的祖國(guó)和逝去的生命,這已經(jīng)不是一場(chǎng)普通的球賽,這也不是一場(chǎng)戰(zhàn)爭(zhēng),讓我想到的詞語(yǔ)竟然是悲劇,一幕讓人激昂而又悲哀的悲劇。

          posted @ 2007-07-30 13:10 dennis 閱讀(228) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共56頁(yè): First 上一頁(yè) 35 36 37 38 39 40 41 42 43 下一頁(yè) Last 
          主站蜘蛛池模板: 高陵县| 清涧县| 嵩明县| 奎屯市| 怀安县| 扶风县| 甘孜| 盱眙县| 资阳市| 嘉荫县| 怀宁县| 通海县| 东乡族自治县| 江北区| 尚义县| 静宁县| 涿州市| 河曲县| 文安县| 沙雅县| 九寨沟县| 冕宁县| 五河县| 共和县| 楚雄市| 田阳县| 六安市| 汉阴县| 昭平县| 牙克石市| 武胜县| 五大连池市| 元氏县| 赤壁市| 江城| 张家港市| 抚州市| 无锡市| 常州市| 石台县| 绍兴县|