Sometimes we have to choose between several algorithms that have the same goal. Performance and therefore the execution time can be considered as a good criterion, although there are many others. For that the best solution would be to use a profiling tool, but if you want a quick solution we can simply calculate the difference of time between the beginning and the end of each algorithm.
In python, for ease and clarity of use, we can write this as a custom decorator and call it anywhere in our script.
To use this decorator :
- Add the following snippet to your source code
- Add the @measure() annotation above the function's signature that you want to test
def measure(iterations=1): """ This decorator allows you to print the average execution time of a function :param iterations: number of times that function will be called """ import time def decorator(fn): def wrapper(*args, **kwargs): _sum = 0.0 _result = None for i in xrange(iterations): _start = time.time() _result = fn(*args, **kwargs) _sum += time.time() - _start print '"%s" Function called %s time(s). Average execution time: %f sec' % (fn.__name__, iterations, _sum / iterations) return _result return wrapper return decorator
Example
@measure() def my_function(): ...
The tested function is called once, but we can increase the number of iterations to get a more precise value. this can be achieved by changing the value of the decorator's argument
@measure(1000) def my_function(): ...
Example of use
Let's say we have a scene containing 2000 different objects and we want to sort them (in the object manager) by type, and by name (if they have the same type).
In the following source code we test 3 sorting algorithms:
- Python default sort algorithm (Timsort)
- The Insertion sort
- The Bubble sort
import c4d def measure(iterations=1): """ This decorator allows you to print the average execution time of a function :param iterations: number of times that function will be called :return: fn """ import time def decorator(fn): def wrapper(*args, **kwargs): _sum = 0.0 _result = None for i in xrange(iterations): _start = time.time() _result = fn(*args, **kwargs) _sum += time.time() - _start print '"%s" Function called %s time(s). Average execution time: %f sec' % (fn.__name__, iterations, _sum / iterations) return _result return wrapper return decorator def gt(a, b): """ Checks that A value is greater than B """ type_a = a.GetType() type_b = b.GetType() if type_a == type_b: return a.GetName() > b.GetName() return a.GetType() > b.GetType() def insertion_sort(array): """ This function allows you to sort an array using the insertion sort algorithm """ count = len(array) for i in xrange(count): current_value = array[i] pos = i while pos > 0 and gt(array[pos - 1], current_value): array[pos] = array[pos - 1] pos = pos - 1 array[pos] = current_value return array def bubble_sort(array): """ This function allows you to sort an array using the bubble sort algorithm """ def swap(a, b): array[a], array[b] = array[b], array[a] count = len(array) swapped = True idx = -1 while swapped: swapped = False idx = idx + 1 for i in xrange(1, count - idx): if gt(array[i - 1], array[i]): swap(i - 1, i) swapped = True return array def _cmp(obj_a, obj_b): return cmp(obj_a.GetType(), obj_b.GetType()) or cmp(obj_a.GetName(), obj_b.GetName()) def update_manager(doc, object_list): """ Updates all objects position in the object manager """ doc.StartUndo() for obj in object_list: doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj) obj.Remove() doc.InsertObject(obj, None, None) doc.EndUndo() @measure() def main(): obj = doc.GetFirstObject() if not obj: return objects = [obj] obj = obj.GetNext() while obj: objects.append(obj) obj = obj.GetNext() # Default sorting objects.sort(cmp=_cmp) # Insertion sorting # objects = insertion_sort(objects) # Bubble sorting # objects = bubble_sort(objects) objects.reverse() update_manager(doc, objects) c4d.EventAdd() if __name__ == '__main__': main()