| Fig. 1) Source code for the
optimised predicate "conclist/2"
(written in April 2005): ; ======================= source-file: _conclist.asm ========================== ; ; Purpose: Concatenates a list of strings into a single string, with or without ; a separator-character (arg 2). If the separator is 0 (ascii), then there will ; be no separator-chars in the output string (arg 3) (concatenation). IDEAL P586 MODEL FLAT DATASEG ALIGN 4 vdstr dd voidstr voidstr db 0,0,0,0 CODESEG ALIGN 4 public _conclist include "macros.inc" PROC _conclist near push ebp ; mov ebp,esp ; push edi ; push ebx ; cld ; forwards (just in case!) mov ecx,[dword ptr ebp+8] ;input-list........ARG-1(i) mov eax,[ecx] ; load flag list-element dec al ; is list-element = end? jnz @@vx1 ; if so, special processing to give empty_string ; ------------------- mov ebx,8 ; xor edx,edx ; mov al,[byte ptr ebp+12] ; separator........ARG-2(i) or al,al ; is the separator = 0? jz @@vd1 ; if so, special processing (to NOT write it!) ; ------------------- jmp short @@ok2 ; ; ------------------- @@LP0: mov eax,[ecx] ; load flag list-element dec al ; is list-element NORMAL? (=1)? jnz @@XX ; if not so, exit ; ------------------- @@ok2: push ebx ; push ecx ; mov ecx,[ecx+4] ; 1st string_pointer in ECX xor eax,eax ; @@p1: mov bl,[ecx+eax]; inc eax ; or bl,bl ; jnz short @@p1 ; ; ------------------- dec eax ; add edx,eax ; add the specific str_len to the total_len inc edx ; also, add 1 (for separator) pop ecx ; pop ebx ; add ecx,ebx ; advance list_ptr by 8 mov ecx,[ecx] ; address of next element... jmp @@LP0 ; repeat loop for all elements ; =================== @@XX: inc edx ; push edx ; call _MEM_AllocGStack ; allocate memory for this size of string mov edi,eax ; pop edx ; mov ecx,[dword ptr ebp+8] ; input-list........ARG-1(i) push edi ; mov ah,[byte ptr ebp+12] ; separator........ARG-2(i) mov ebx,8 ; ; -------------------- @@Lq0: mov al,[byte ptr ecx] ; load flag list-element dec al ; is list-element NORMAL? (=1)? jnz @@ZZ ; if not so, exit ; ==================== push ecx ; mov ecx,[ecx+4] ; 1st string_pointer in ECX xor edx,edx ; @@q1: mov al,[ecx+edx]; stosb ; write the byte inc edx ; or al,al ; jnz short @@q1 ; ; -------------------- mov [byte ptr edi-1],ah ; write the separator pop ecx ; add ecx,ebx ; advance list_ptr by 8 mov ecx,[ecx] ; address of next element... jmp @@Lq0 ; repeat loop for all elements ; ==================== @@ZZ: xor al,al ; mov [byte ptr edi-1],al ; pop eax ; return the new_string in EAX pop ebx ; pop edi ; pop ebp ; ret ; ; ==================== @@vx1: mov eax,[vdstr] ; pop ebx ; pop edi ; pop ebp ; ret ; ; ====================================================================== ; ==================== CASE of zero-ascii Separator: =================== ; ====================================================================== @@M1: mov eax,[ecx] ; load flag list-element dec al ; is list-element NORMAL? (=1)? jnz @@h2 ; if not so, end this phaser ; --------------------- @@vd1: push ebx ; push ecx ; mov ecx,[ecx+4] ; 1st string_pointer in ECX xor eax,eax ; @@w1: mov bl,[ecx+eax] ; inc eax ; or bl,bl ; jnz short @@w1 ; ; --------------------- dec eax ; add edx,eax ; add the specific str_len to the total_len pop ecx ; pop ebx ; add ecx,ebx ; advance list_ptr by 8 mov ecx,[ecx] ; address of next element... jmp @@M1 ; repeat loop for all elements ; ===================== @@h2: inc edx ; push edx ; call _MEM_AllocGStack ; allocate memory for this size of string mov edi,eax ; pop edx ; mov ecx,[dword ptr ebp+8] ; input-list........ARG-1(i) push edi ; ; --------------------- @@j0: mov al,[byte ptr ecx] ; load flag list-element dec al ; is list-element NORMAL? (=1)? jnz @@Z3 ; if not so, exit ; ========================== push ecx ; mov ecx,[ecx+4] ; 1st string_pointer in ECX xor edx,edx ; @@f1: mov al,[ecx+edx] ; stosb ; write the byte inc edx ; or al,al ; jnz short @@f1 ; ; -------------------------- dec edi ; pop ecx ; add ecx,ebx ; advance list_ptr by 8 mov ecx,[ecx] ; address of next element... jmp @@j0 ; repeat loop for all elements ; ================== @@Z3: xor al,al ; mov [byte ptr edi],al ; pop eax ; return the new_string in EAX pop ebx ; pop edi ; pop ebp ; ret ENDP _conclist end |
| Fig.
2) Source code for the ("non-optimised") predicate "concatlist/2"
(written in 2002): ; ====================== source-file: _concatlist.asm ========================= ; ; Purpose: Concatenates a list of strings into a single string, with or without ; a separator-character (arg 2). If the separator is 0 (ascii), then there will ; be no separator-chars in the output string (arg 3) (concatenation). IDEAL P586 MODEL FLAT DATASEG ALIGN 4 vdstr dd voidstr voidstr db 0,0,0,0 CODESEG ALIGN 4 public _concatlist include "macros.inc" PROC _concatlist near ARG slist:dword, sepch:byte push ebp ; mov ebp,esp ; push esi ; push edi ; push ebx ; cld ; forwards (just in case!) mov esi,[slist] ; make ESI = first argument (list) mov al,[byte ptr esi] ; load flag cmp al,2 ; is it two? jnz @@ok1 ; if not so, proceed as usual... ; ------------------- mov eax,[vdstr] ; else, enter with void_string pop3 ; ; =================== @@ok1: mov dl,[sepch] ; make DL = second argument (char) mov bl,2 ; make bl = end-element flag or dl,dl ; zero sepchar? jz @@n1 ; if so, skip next... @@n0: push esi ; xor edx,edx ; use EDX as size_counter, initialize it to 0 @@L0: lodsd ; load list-flag cmp al,bl ; is it 2 (end of list)? jz @@Lx ; if so, exit this loop lodsd ; string_pointer in EAX mov edi,eax ; xor eax,eax ; mov ecx,eax ; not ecx ; repne scasb ; find length of string_element (including zero at the end) not ecx ; now ECX = length of string element + 1 add edx,ecx ; add str_length to size_counter inc edx ; increment it by one (to account for separator) lodsd ; load next list-element pointer into EAX mov esi,eax ; use next list-element pointer, as current list pointer jmp @@L0 ; repeat big loop ; =================== @@n1: push esi ; xor edx,edx ; use EDX as size_counter, initialize it to 0 @@L1: lodsd ; load list-flag cmp al,bl ; is it 2 (end of list)? jz @@Lx ; if so, exit this loop lodsd ; string_pointer in EAX mov edi,eax ; xor eax,eax ; mov ecx,eax ; not ecx ; repne scasb ; find length of string_element (including zero at the end) not ecx ; now ECX = length of string element + 1 add edx,ecx ; add str_length to size_counter lodsd ; load next list-element pointer into EAX mov esi,eax ; use next list-element pointer, as current list pointer jmp @@L1 ; repeat big loop ; =================== @@Lx: mov ecx,edx ; return ECX = list_size pop esi ; push esi ; push ecx ; call _MEM_AllocGStack ; allocate memory for the result-string pop ecx ; pop esi ; mov edi,eax ; edi is the new string pointer push edi ; store it in the stack mov dl,[sepch]; make DL = second argument (char) mov dh,2 ; make DH = end-element flag jmp @@L3 ; ; ================== @@L3a: lodsd ; load next-element-pointer mov esi,eax ; turn it into the current list-pointer @@L3: lodsd ; load flag-element cmp al,dh ; is AL = end-element flag jz @@XXX ; if so, end this loop lodsd ; load string-pointer push esi ; mov esi,eax ; make ESI = string @@M0: lodsb ; load a byte from the string or al,al ; is it a zero? jz @@M1 ; if so, stop copying bytes... stosb ; else, write it to the target-string jmp @@M0 ; and repeat this loop ; ================== @@M1: pop esi ; recover list pointer from the stack or dl,dl ; zero sepchar? jz @@L3a ; if so, skip next mov al,dl ; else, make AL = sepchar stosb ; and write the sepchar jmp @@L3a ; @@XXX: dec al ; dec al ; now AL = 0 or dl,dl ; zero sepchar? jz @@Z3 ; if so, skip next dec edi ; @@Z3: stosb ; end the string with a zero pop eax ; restore and return start of new string in EAX pop ebx ; pop edi ; pop esi ; pop ebp ; ret ENDP _concatlist end |
| Fig. 3) Visual Prolog Test program (compiled as an "EasyWin" 32-bit
executable): GLOBAL DOMAINS SLIST = STRING* GLOBAL PREDICATES STRING conclist(SLIST,CHAR) -(i,i) language c %CONCLIST.asm (2002) STRING concatlist(SLIST,CHAR) -(i,i) language c %CONCATLIST.asm (2005) CONSTANTS % test_concatlist = 1 % <-- activate this constant for the first test test_concatlist_benchmark = 2 % /** a "traditional recursive" Prolog implementation: */ PREDICATES prolog_conclist(SLIST,CHAR,STRING,STRING) CLAUSES prolog_conclist([S],_,Old,Sx):- concat(Old,S,Sx), !. prolog_conclist([S|SL],'\0',Old,Sx):- concat(Old,S,New), !, prolog_conclist(SL,'\0',New,Sx). prolog_conclist([S|SL],Char,Old,Sx):- str_char(Sch,Char), concat(S,Sch,S2), concat(Old,S2,New), !, prolog_conclist(SL,Char,New,Sx). ifdef test_concatlist GOAL repeat, write("give a sentence with commas:\n> "), readln(S), str2slist(S,',',SL), % another ASM predicate, converting a string-with-separators to a string-list %NOTE: % Use the call "term_str(slist,SL,S)", if you wish to avoid "str2slist/3", % but... then your sentence must have brackets and double-quotes! % write("Give an output_separator (ASCII-number): "), readint(ASC), char_int(ChSep,ASC), prolog_conclist(SL,ChSep,"",X), write("\n(prolog_conclist/3) result:\n\"",X,"\"\n\n"), Y = concatlist(SL,ChSep), write("(concatlist/2) result:\n\"",Y,"\"\n\n"), Z = conclist(SL,ChSep), write("(conclist/2) result:\n\"",Z,"\"\n\n"), readchar(Cx), Cx='\27', !. enddef ifdef test_concatlist_benchmark GOAL FileTypeFilters = ["Text files","*.1_x", "Special large Test-files","*.1_x", "All Prolog files","*.pro;*.pre;*.con;*.dom;*.inc;*.rc", "All files","*.*"], F = dlg_GetFileName("*.txt",FileTypeFilters,"Open a file to test",[],"",_,_), file_str(F,Txt), str2slist(Txt,'\n',SL), repeat, T1a = readclock(), X1 = conclist(SL,'\n'), T1b = readclock(), % NOTE: "readclock" is another ASM predicate giving the CPU's clock ticks T2a = readclock(), X2 = concatlist(SL,'\n'), T2b = readclock(), % NOTE: "readclock" is another ASM predicate giving the CPU's clock ticks T3a = readclock(), prolog_conclist(SL,'\n',"",X3), T3b = readclock(), % NOTE: "readclock" is another ASM predicate giving the CPU's clock ticks X1=X2, write("X1 = X2,\n"), X1=X3, write("X1 = X3,\n"), Tdif1 = T1b-T1a, Tdif2 = T2b-T2a, Tdif3 = T3b-T3a, nl, writef("Timings:\n(1)%16 <-- conclist/2\n(2)%16 <-- concatlist/2\n(3)%16 <-- prolog_conclist/4\n", Tdif1,Tdif2,Tdif3), Rdif21 = (Tdif2 / Tdif1), Rdif31 = (Tdif3 / Tdif1), writef("Execution times' ratio, Naive ASM compared to 'Optimal ASM': %12.2f\n",Rdif21), writef("Execution times' ratio, Visual Prolog compared to Optimal ASM: %12.2f\n",Rdif31), readchar(Cx), Cx='\27', !. enddef /* Benchmark results (1): (by cutting and pasting from the Visual Prolog executable) - Small file case (): Timings: (1) 45252 <-- conclist/2 (2) 71456 <-- concatlist/2 (3) 1034448 <-- prolog_conclist/4 Execution times' ratio, 'Naive' ASM(2) compared to Optimal ASM(1): 1.58 Execution times' ratio, Visual Prolog(3) compared to Optimal ASM(1): 22.86 X1 = X2, X1 = X3, Timings: (1) 44524 <-- conclist/2 (2) 71344 <-- concatlist/2 (3) 1031984 <-- prolog_conclist/4 Execution times' ratio, 'Naive' ASM(2) compared to Optimal ASM(1): 1.60 Execution times' ratio, Visual Prolog(3) compared to Optimal ASM(1): 23.18 */ /* Benchmark results (1): (by cutting and pasting from the Visual Prolog executable) - Large file case (14.3Mb): Any call to the predicate "prolog_conclist/4" produces the following... ugly message: PROGRAM ERROR. Module:DICT_FUN.PRO Pos:18647 Message:1001 Gstack overflow. Not enough memory or an endless loop Press any key ... */ However, by commenting out the prolog implementation, we obtained the following results (for the same 14.3 Megabyte file, converted to a list and then re-assembled as a single string, using ASM): X1 = X2, Timings: (1) 78069876 <-- conclist/2 (2) 135285196 <-- concatlist/2 (3) Execution times' ratio, Naive ASM compared to 'Optimal ASM': 1.73 X1 = X2, Timings: (1) 70840152 <-- conclist/2 (2) 118534664 <-- concatlist/2 (3) Execution times' ratio, Naive ASM compared to 'Optimal ASM': 1.67 X1 = X2, Timings: (1) 70717452 <-- conclist/2 (2) 121604524 <-- concatlist/2 (3) Execution times' ratio, Naive ASM compared to 'Optimal ASM': 1.72 X1 = X2, Timings: (1) 69697228 <-- conclist/2 (2) 118369980 <-- concatlist/2 (3) Execution times' ratio, Naive ASM compared to 'Optimal ASM': 1.70 */ |