# mPy - profiling persistent function parameters
# Copyright by Anthony Vogelaar, Perth Scotland
# rev 0.10  2026-09-03  initial release

from	time	import	ticks_us, ticks_diff

# ========================= results ============================

# parameter             87 uSec
# global variable       38 uSec		<--
# persistent parameter  60 uSec
# class                124 uSec
# closure               34 uSec
# generator            104 uSec
# buffer (dict)			55 uSec		<--
# stream			   725 uSec

# ========================= utility ============================

# decorator to print duration of function f_test
def printDuration(f_test):

    def fut():
        t1 = ticks_us()
        result = f_test()
        t2 = ticks_us()
        print(f'{ticks_diff(t2, t1):5d} uSec: ', end='')
        return result
    
    
    return fut

# ========================= error ===========================

# demonstrating that n is not persistent
def f_error():
    try   : n + 1
    except: n = 10
    return  n


# error, not persistent parameter
def test0():
    for _ in range(5):
        x = f_error()						# <--
        print(x)


#test0()

# ======================== parameter ===========================

# persistent n due to external storage
def fParameter(n):
    n += 1
    return n


def test1():
    n = 10
    for _ in range(5):
        t1  = ticks_us()
        n = fParameter(n)					#  <--
        t2  = ticks_us()
        print(f'{ticks_diff(t2, t1):5d} uSec: {n}')
    

#test1()
        
# ======================== global ===========================

# persistent n due to exteral global storage
n = 10										# module global

@printDuration
def fGlobal():
    global n

    n += 1
    return n


def test2():
    for _ in range(5):
        print(fGlobal())
        
        
#test2()
        
# ===================== persistent parameter =================

# persistent n due to storage in mutable parameter
@printDuration
def fParam(static={'n':10}):
    static['n'] += 1
    return static['n']


# persistent parameter 92 uSec
def test3():
    for _ in range(5):
        print(fParam())


#test3()

# ========================== class ===========================

# persistent n due to storage in class derived object
class C:
    
    def __init__(self):
        self.n = 10
        
        
    def fObj(self):
        self.n += 1
        return self.n


c = C()

@printDuration
def doC():
    return c.fObj()    
    
    
def test4():
    for _ in range(5):
        print(doC())


#test4()

# ========================== closure =========================

# persistent n due to storage in a closure
def fClosure(n=10):
    
    @printDuration
    def fc():
        nonlocal n
        n += 1
        return n

    return fc


f = fClosure()


def test5():
    for _ in range(5):
        print(f())


# test5()

# ========================= generator ========================

# persistent n due to storage in a generator
def fGenerator():
    n = 10
    while True:
        n += 1
        yield n
        

f = fGenerator()

@printDuration
def doFGenerator():
    return next(f)


def test6():
    for _ in range(5):
        print(doFGenerator())


#test6()

# ========================== dictionary container =====================

buffer = dict(n0=0, n1=0, n2=0, n3=0, n4=0, n5=0, n6=0, n7=0, n8=0, n9=0)
buffer['n1'] = 10


# dict 55 uSec
def test7():
    
    @printDuration
    def incrI():
         buffer['n1'] += 1
         return buffer['n1']
        
        
    for _ in range(5):
        i = incrI()
        print(i)


#test7()

# ============================= file io stream ========================

def test8():
    
    @printDuration
    def incrI():
        data = bytearray(4)
        f.seek(4); f.readinto(data)
        i = int.from_bytes(data, 'little')
        i += 1
        data = i.to_bytes(4, 'little') 
        f.seek(4); f.write(data)
        return i


    with open('data.bin', 'wb') as f:
        f.seek(4)
        f.write(b'\x0A\x00\x00\x00')
        for _ in range(5):
            i = incrI()
            print(i)
            
            
#test8()

# =================================== end =============================
