From 6e3c08af0e03f8178ceb7136d7bc8469a55a636f Mon Sep 17 00:00:00 2001 From: Delphi-Coder <47367331+Delphi-Coder@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:04:39 +0330 Subject: [PATCH 1/2] prime.asm NASM 64 bit assembly - x86 architecture - Linux OS --- prime.asm | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 prime.asm diff --git a/prime.asm b/prime.asm new file mode 100644 index 0000000..b63137e --- /dev/null +++ b/prime.asm @@ -0,0 +1,99 @@ +; This assembly code calculate the count of prime numbers in range 1~9,000,000 +; Prints the count of prime numbers and then the time (in ms) of execution +; we were interested measuring/comparing the speed and performance +; optimization of different compilers/programming languages, Thats all. + +; build steps: +; 1- sudo apt install nasm (of-course if you dont have NASM installed) +; 2- nasm -f elf64 -o prime.o prime.asm +; 3- ld -I/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 -lc -s -o prime prime.o +Bits 64 +MaxNumber EQU 8999999 +section .data + fmt db "%lld", 10, 0 + tv1 dq 0,0 + tv2 dq 0,0 + +section .text +global _start +extern printf +extern exit +; entry point +_start: + ; set start time + mov rax, 96 ; gettimeofday syscall number + mov rdi, tv1 ; struct timeval *tv + mov rsi, 0 ; struct timezone *tz + syscall + + call count_primes ; count the number of prime numbers between 1 and 8999999 + + mov rdi, fmt ; format string for printf + mov rsi, rax ; the number of prime numbers found + xor rax, rax ; clear rax for printf + call printf ; print the result + + ; set stop time + mov rax, 96 ; gettimeofday syscall number + mov rdi, tv2 ; struct timeval *tv + mov rsi, 0 ; struct timezone *tz + syscall + + ; calculate time difference in milliseconds + mov rax, [tv2 + 0] ; tv2.tv_sec + sub rax, [tv1 + 0] ; tv1.tv_sec + mov rbx, 1000000 + mul rbx + mov rcx, [tv2 + 8] ; tv2.tv_usec + sub rcx, [tv1 + 8] ; tv1.tv_usec + add rax, rcx + mov rbx, 1000 + div rbx + + ; print time duration in milliseconds + mov rdi, fmt + mov rsi, rax + xor rax, rax + call printf + + xor rdi, rdi + call exit + +; count the number of prime numbers between 1 and MaxNumber +; output: rax - the number of prime numbers found +count_primes: +; rcx = number counter to check if its prime +; rbx = divisor counter +; rbp = max number +; rdi = prime number counter + mov ecx,1 ; numbers to check starts from 1 + mov ebp,MaxNumber ; initialize max number + xor edi,edi ; no prime found yet. set counter to 0 +chkprimelp: + cmp ecx,1 + je notprime + mov ebx,2 ; divisor counter start from 2 + mov eax,ecx + cvtsi2sd xmm0,eax + sqrtsd xmm0,xmm0 + cvtsd2si eax,xmm0 + mov r8d,eax ; store max divisor in r8 register +primemlp: ; prime main loop + cmp ebx,r8d ; compare with max divisor + ja isprime ; if its below or equal jump to primemlp + mov eax,ecx ; put number in eax + xor edx,edx ; set higher 4 byte to zero(our numbers is not that big) + div ebx ; devide eax to ebx. edx will be remainder + or edx,edx ; reminder is zero? + je notprime ; if yes (it is not prime) + inc ebx ; increase divisor + jmp primemlp +isprime: + inc edi ; the number is prime for sure. inc the counter +notprime: + inc ecx ; increase number counter + cmp ecx,ebp ; compare with max number + jna chkprimelp ; if not above or equal jump to loop start + mov eax,edi + ret + From edbcf72185ab9bb49a3583eefee4fe65ff44805e Mon Sep 17 00:00:00 2001 From: Delphi-Coder <47367331+Delphi-Coder@users.noreply.github.com> Date: Sun, 23 Apr 2023 23:44:13 +0330 Subject: [PATCH 2/2] Update prime.asm Dependency to C library such as printf and exit removed. Now it use services provided by OS via syscall. --- prime.asm | 145 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 61 deletions(-) diff --git a/prime.asm b/prime.asm index b63137e..ff57f2a 100644 --- a/prime.asm +++ b/prime.asm @@ -1,37 +1,72 @@ ; This assembly code calculate the count of prime numbers in range 1~9,000,000 ; Prints the count of prime numbers and then the time (in ms) of execution -; we were interested measuring/comparing the speed and performance -; optimization of different compilers/programming languages, Thats all. + +; We were interested measuring/comparing the performance and optimization +; of different compilers/programming languages, Thats why this code exist. ; build steps: -; 1- sudo apt install nasm (of-course if you dont have NASM installed) -; 2- nasm -f elf64 -o prime.o prime.asm -; 3- ld -I/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 -lc -s -o prime prime.o + +; sudo apt install nasm (of-course if you dont have NASM installed) +; nasm -f elf64 prime.asm +; ld -s -o prime prime.o Bits 64 MaxNumber EQU 8999999 section .data - fmt db "%lld", 10, 0 - tv1 dq 0,0 - tv2 dq 0,0 + tv1 dq 0,0 + tv2 dq 0,0 + strbuf db 20 dup(0),10,13,0 section .text global _start -extern printf -extern exit -; entry point -_start: +_start: ; entry point ; set start time mov rax, 96 ; gettimeofday syscall number mov rdi, tv1 ; struct timeval *tv mov rsi, 0 ; struct timezone *tz syscall - call count_primes ; count the number of prime numbers between 1 and 8999999 +count_primes: ; count the number of prime numbers between 1 and MaxNumber + mov ecx,1 ; ecx = numbers to check, starts from 1 + mov ebp,MaxNumber ; ebp = max number to check + xor esi,esi ; esi = count of primes +chkprimelp: + cmp ecx,1 + je notprime + ;test ecx,1 ; we did this in better way than C (divisor will be added 2 on + ;jz notprime ; each loop in-order to obtain only odd numbers to be checked) + ; ****************************************************************************************************** + ; a weird thing happens when we have the above lines uncommented and increment divisor 1 by each step + ; despite the fact that it reduces our instruction count to be executed it increase total excution time + ; about 300~400 ms but leaving it active by 1 increment for loop has better performance + ; I dont know why this happening also couldn't find any reason for this searching the google for hours + ; ****************************************************************************************************** + mov ebx,3 ; ebx = divisor counter start from 3 + cvtsi2sd xmm0,ecx ; mov the number to xmm0 + sqrtsd xmm0,xmm0 ; calculate xmm0 sqrt and put the result in xmm0 + cvtsd2si eax,xmm0 ; move integer part of result to eax + mov r8d,eax ; store max divisor in r8 register +primemlp: ; prime main loop + cmp ebx,r8d ; compare with max divisor + ja isprime ; if its above jump to isprime + mov eax,ecx ; put number in eax + xor edx,edx ; set higher 4 byte to zero(our numbers is not that big so this must be zero) + div ebx ; devide eax to ebx. edx will be remainder + or edx,edx ; reminder is zero? + je notprime ; if yes (it is not prime) + inc ebx ; increase divisor + jmp primemlp ; loop to primemlp +isprime: + inc esi ; the number is prime for sure. inc the counter +notprime: + add ecx,2 + ;inc ecx ; increase number counter + ;inc ecx ; increase number counter + cmp ecx,ebp ; compare with max number + jna chkprimelp ; if not above or equal jump to loop start - mov rdi, fmt ; format string for printf - mov rsi, rax ; the number of prime numbers found - xor rax, rax ; clear rax for printf - call printf ; print the result + inc rsi ; add 1 as number 2 not checked + mov rax,rsi ; put primes count to eax + call printeax ; print the eax as result ; set stop time mov rax, 96 ; gettimeofday syscall number @@ -51,49 +86,37 @@ _start: div rbx ; print time duration in milliseconds - mov rdi, fmt - mov rsi, rax - xor rax, rax - call printf + call printeax - xor rdi, rdi - call exit - -; count the number of prime numbers between 1 and MaxNumber -; output: rax - the number of prime numbers found -count_primes: -; rcx = number counter to check if its prime -; rbx = divisor counter -; rbp = max number -; rdi = prime number counter - mov ecx,1 ; numbers to check starts from 1 - mov ebp,MaxNumber ; initialize max number - xor edi,edi ; no prime found yet. set counter to 0 -chkprimelp: - cmp ecx,1 - je notprime - mov ebx,2 ; divisor counter start from 2 - mov eax,ecx - cvtsi2sd xmm0,eax - sqrtsd xmm0,xmm0 - cvtsd2si eax,xmm0 - mov r8d,eax ; store max divisor in r8 register -primemlp: ; prime main loop - cmp ebx,r8d ; compare with max divisor - ja isprime ; if its below or equal jump to primemlp - mov eax,ecx ; put number in eax - xor edx,edx ; set higher 4 byte to zero(our numbers is not that big) - div ebx ; devide eax to ebx. edx will be remainder - or edx,edx ; reminder is zero? - je notprime ; if yes (it is not prime) - inc ebx ; increase divisor - jmp primemlp -isprime: - inc edi ; the number is prime for sure. inc the counter -notprime: - inc ecx ; increase number counter - cmp ecx,ebp ; compare with max number - jna chkprimelp ; if not above or equal jump to loop start - mov eax,edi - ret + mov rax, 3ch ; exit syscall no + mov rdi, 0 ; argument, in this case: return value + syscall +printeax: + mov rdi,strbuf ; address of strbuf to Destination Index register + add rdi,19 ; add 19 to point last byte of buffer + mov rsi,rdi ; save end of string buffer in esi + std ; set direction flag (will be moved backward) + mov ecx,10 ; we want to print the number in base 10 +devide: + xor edx,edx ; zero high value bits of number + div ecx ; div to ecx(=10) dl will be the reminder + add dl,48 ; add character '0' ascii to it to obtain ascii character + push rax ; save eax on stack + mov al,dl ; move character to al + stosb ; store byte in es:edi and decrement edi + pop rax ; restore eax value from stack + or eax,eax ; check if eax is zero + jnz devide ; if not zero goto devide +finish: + sub rsi,rdi ; calculate length of string + add rsi,2 ; add 2 to include \r\n at the end of buffer + inc rdi ; increase edi as it decreased by last stosb + mov rdx,rsi ; message length to rdx + mov rsi,rdi ; string start address to rsi + mov rcx,rdi ; and same to rcx + mov rbx,1 ; file descriptor 1 (stdout) + mov rax,1 ; function number 1 (kernel write) + mov rdi,1 ; rdi to 1 + syscall ; call write function + ret ; return to caller