Timing of function changes after changing its position in script

Viewed 54

I have written a Python script to compare performance of different solutions to a question asked here on stackoverflow ( How to build a list of growing initials of a string with itertools? ).

What I am wondering about is that the resulting timing of a function seem to depend on the position in code in relation to the other functions. I have attached both versions of the script I am running with examples of output. The issue is not always 100% reproducible, but at least at 90% of the runs.

In the position as last in the script the timing seem to be always above 7.0

inits_lstcm_slice_regex # Claudio       :  7.1699229

where placing the function as first in the script the timing improve and is mostly below 7.0:

inits_lstcm_slice_regex # Claudio       :  6.7658726

How does it come? Can you reproduce the behavior I am speaking about? What is the reason for this strange outcome and how can I fix the code to produce more reliable timing comparisons and make the relative timing not dependent on the position at which a function is placed in the code?

Below the two versions of the same script with one of the functions at the begin and at the end of the section with functions:

def get_root_defs(filename):
    with open(filename) as f: 
        Lines = f.readlines()
    Fs = []
    for i, line in enumerate(Lines):
        if line.startswith('def inits_'):
            Fs.append( (line[4:line.index('(')], Lines[i+1][4:Lines[i+1].index('(')-1]) )
    return Fs
Fs = get_root_defs(__file__)
# print(f'{Fs=}')

from itertools import accumulate
def inits_lsmap_accum_zipsp(sep, s):
    # Kelly Bundy ( list(map())->lsmap, accumulate->accum, ), zip split->zipsp
    return list(map(sep.join, accumulate(zip(s.split(sep)))))

from itertools import accumulate
def inits_laccu_split_formt(sep, s):
    # ShadowRanger ( list(accumulate())->laccu, split, formt->format )
    return list(accumulate(s.split(sep), '{} {}'.format))

def inits_lstcm_sjoin_slice(sep, s): 
    #  dawg ( list comprehension->lstcm, slice, split )
    li = s.split(sep)
    return [sep.join(li[0:i]) for i in range(1,len(li)+1) ]

def inits_haskl_gener_fnctn(sep, s):
    # Silvio Mayolo ( haskel generator function->haskl_gener_fnctn )
    def inits(sep, LsplitS):
        # yield ()
        acc = []
        for e in LsplitS:
            acc.append(e)
            yield tuple(acc)
    return list(map(sep.join, inits(sep, s.split(sep))))
# ========================================================
import numpy as np
def inits_lstcp_numpy_slice(sep, s):
    # Claudio ( )
    I = np.nonzero(np.array([*s])==' ')[0].tolist() 
    return [ s[0:i] for i in I ] + [s]

def inits_tryex_gener_fnctn(sep, s):
    # Claudio ( )
    def get_i(sep, s):
        i,j = (0,0)
        while True:
            try:
                j = s[i:].index(sep)
                i += j
                yield i
                i += 1
            except ValueError:
                yield len(s)
                break
    return [ s[0:i] for i in get_i(sep, s) ]

def inits_chrlp_lstcp_slice(sep, s):
    # Claudio ( )
    return [ s[0:i] for i in [ i for i in range(len(s)) if s[i] == sep ] ] + [s]

def inits_chrlp_lstcp_step2(sep, s):
    # Claudio ( )
    i = 1
    len_s = len(s)
    I = []
    #sT = T()
    while i < len_s:
        # if s[i].startswith(sep):
        if s[i] == sep[0]:
            I += [i] # .append(i)           
            i += 2
        else:
            i += 1
    #eT = T(); print(f'while i < len_s : {eT-sT:10.7f}')
    #sT = T()
    S = [s]*(len(I)+1)
    #eT = T(); print(f'S=[s]*(len(I)+1): {eT-sT:10.7f}')
    #sT = T()
    for i, v in enumerate(I): 
        S[i] = S[i][0:v]
    #return [ s[0:i] for i in I ] + [s]
    #eT = T(); print(f'for i,v in enum(: {eT-sT:10.7f}')
    return S

def inits_chrlp_lstcp_enumr(sep, s):
    # Claudio ( )
    return [ s[0:i] for i in [ j for j, c in enumerate(s) if c == sep ] ] + [s]

from re import finditer as r
def inits_lstcm_slice_regex(sep, s):
    # Claudio ( regex, slice, lstcm->list comprehension )
    return [s[0:m.start()] for m in r(sep,s)]+[s] 


# I have a Python string:
s0 = "d4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7 g4 dxc4"
s1 = 600 * s0
sep = " "
# I want to split it into:
L = ['d4', 'd4 d5', 'd4 d5 c4', 'd4 d5 c4 e6', 'd4 d5 c4 e6 Nc3', 'd4 d5 c4 e6 Nc3 Be7', 'd4 d5 c4 e6 Nc3 Be7 Nf3', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7 g4', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7 g4 dxc4']

from time import perf_counter as T
tf = 100
for defName in Fs:
    if defName[0].startswith('inits'): 
    #if defName[0].startswith('inits') and defName[1].startswith('# Claudio'):
        assert eval(f"{defName[0]}(sep, s0)"), f'{defName}' == L
        exec(f"sT=T();{defName[0]}(sep, s1);eT=T();print(f'{defName[0]} {defName[1].ljust(15)} : "+"{tf*(eT-sT):10.7f}')" )

output = """
inits_lsmap_accum_zipsp # Kelly Bundy   : 87.7965652
inits_laccu_split_formt # ShadowRanger  :  7.3266540
inits_lstcm_sjoin_slice #  dawg         : 88.3612148
inits_haskl_gener_fnctn # Silvio Mayolo : 88.2759180
inits_lstcp_numpy_slice # Claudio       :  7.8032393
inits_tryex_gener_fnctn # Claudio       :  8.2205204
inits_chrlp_lstcp_slice # Claudio       :  7.3996746
inits_chrlp_lstcp_step2 # Claudio       :  7.5269613
inits_chrlp_lstcp_enumr # Claudio       :  7.4681612
inits_lstcm_slice_regex # Claudio       :  7.1699229
"""
def get_root_defs(filename):
    with open(filename) as f: 
        Lines = f.readlines()
    Fs = []
    for i, line in enumerate(Lines):
        if line.startswith('def inits_'):
            Fs.append( (line[4:line.index('(')], Lines[i+1][4:Lines[i+1].index('(')-1]) )
    return Fs
Fs = get_root_defs(__file__)
# print(f'{Fs=}')

from re import finditer as r
def inits_lstcm_slice_regex(sep, s):
    # Claudio ( regex, slice, lstcm->list comprehension )
    return [s[0:m.start()] for m in r(sep,s)]+[s] 

from itertools import accumulate
def inits_lsmap_accum_zipsp(sep, s):
    # Kelly Bundy ( list(map())->lsmap, accumulate->accum, ), zip split->zipsp
    return list(map(sep.join, accumulate(zip(s.split(sep)))))

from itertools import accumulate
def inits_laccu_split_formt(sep, s):
    # ShadowRanger ( list(accumulate())->laccu, split, formt->format )
    return list(accumulate(s.split(sep), '{} {}'.format))

def inits_lstcm_sjoin_slice(sep, s): 
    #  dawg ( list comprehension->lstcm, slice, split )
    li = s.split(sep)
    return [sep.join(li[0:i]) for i in range(1,len(li)+1) ]

def inits_haskl_gener_fnctn(sep, s):
    # Silvio Mayolo ( haskel generator function->haskl_gener_fnctn )
    def inits(sep, LsplitS):
        # yield ()
        acc = []
        for e in LsplitS:
            acc.append(e)
            yield tuple(acc)
    return list(map(sep.join, inits(sep, s.split(sep))))
# ========================================================
import numpy as np
def inits_lstcp_numpy_slice(sep, s):
    # Claudio ( )
    I = np.nonzero(np.array([*s])==' ')[0].tolist() 
    return [ s[0:i] for i in I ] + [s]

def inits_tryex_gener_fnctn(sep, s):
    # Claudio ( )
    def get_i(sep, s):
        i,j = (0,0)
        while True:
            try:
                j = s[i:].index(sep)
                i += j
                yield i
                i += 1
            except ValueError:
                yield len(s)
                break
    return [ s[0:i] for i in get_i(sep, s) ]

def inits_chrlp_lstcp_slice(sep, s):
    # Claudio ( )
    return [ s[0:i] for i in [ i for i in range(len(s)) if s[i] == sep ] ] + [s]

def inits_chrlp_lstcp_step2(sep, s):
    # Claudio ( )
    i = 1
    len_s = len(s)
    I = []
    #sT = T()
    while i < len_s:
        # if s[i].startswith(sep):
        if s[i] == sep[0]:
            I += [i] # .append(i)           
            i += 2
        else:
            i += 1
    #eT = T(); print(f'while i < len_s : {eT-sT:10.7f}')
    #sT = T()
    S = [s]*(len(I)+1)
    #eT = T(); print(f'S=[s]*(len(I)+1): {eT-sT:10.7f}')
    #sT = T()
    for i, v in enumerate(I): 
        S[i] = S[i][0:v]
    #return [ s[0:i] for i in I ] + [s]
    #eT = T(); print(f'for i,v in enum(: {eT-sT:10.7f}')
    return S

def inits_chrlp_lstcp_enumr(sep, s):
    # Claudio ( )
    return [ s[0:i] for i in [ j for j, c in enumerate(s) if c == sep ] ] + [s]


# I have a Python string:
s0 = "d4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7 g4 dxc4"
s1 = 600 * s0
sep = " "
# I want to split it into:
L = ['d4', 'd4 d5', 'd4 d5 c4', 'd4 d5 c4 e6', 'd4 d5 c4 e6 Nc3', 'd4 d5 c4 e6 Nc3 Be7', 'd4 d5 c4 e6 Nc3 Be7 Nf3', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7 g4', 'd4 d5 c4 e6 Nc3 Be7 Nf3 Nf6 Bg5 h6 Bf4 0-0 e3 Nbd7 g4 dxc4']

from time import perf_counter as T
tf = 100
for defName in Fs:
    if defName[0].startswith('inits'): 
    #if defName[0].startswith('inits') and defName[1].startswith('# Claudio'):
        assert eval(f"{defName[0]}(sep, s0)"), f'{defName}' == L
        exec(f"sT=T();{defName[0]}(sep, s1);eT=T();print(f'{defName[0]} {defName[1].ljust(15)} : "+"{tf*(eT-sT):10.7f}')" )

output = """
inits_lstcm_slice_regex # Claudio       :  6.7658726
inits_lsmap_accum_zipsp # Kelly Bundy   : 86.7529857
inits_laccu_split_formt # ShadowRanger  :  7.7288590
inits_lstcm_sjoin_slice #  dawg         : 87.9976579
inits_haskl_gener_fnctn # Silvio Mayolo : 88.7987813
inits_lstcp_numpy_slice # Claudio       :  7.7087794
inits_tryex_gener_fnctn # Claudio       :  8.3114856
inits_chrlp_lstcp_slice # Claudio       :  7.2907403
inits_chrlp_lstcp_step2 # Claudio       :  7.4924239
inits_chrlp_lstcp_enumr # Claudio       :  7.2369144
"""
0 Answers
Related