1#!/usr/bin/env python3
2
3try:
4    frozendict
5    print("builtin")
6except NameError:
7    from frozendict import coold
8
9class F(coold):
10    def __new__(cls, *args, **kwargs):
11            return super().__new__(cls, *args, **kwargs)
12
13frozendict = F
14
15def main(number):
16    import timeit
17    import uuid
18    from time import time
19    from math import sqrt
20
21    def mindev(data, xbar = None):
22        if not data:
23            raise ValueError("No data")
24
25        if xbar == None:
26            xbar = min(data)
27
28        sigma2 = 0
29
30        for x in data:
31            sigma2 += (x - xbar) ** 2
32
33        N = len(data) - 1
34
35        if N < 1:
36            N = 1
37
38        return sqrt(sigma2 / N)
39
40    def autorange(stmt, setup="pass", globals=None, ratio=1000, bench_time=10, number=None):
41        if setup == None:
42            setup = "pass"
43
44        t = timeit.Timer(stmt=stmt, setup=setup, globals=globals)
45        break_immediately = False
46
47        if number == None:
48            a = t.autorange()
49
50            num = a[0]
51            number = int(num / ratio)
52
53            if number < 1:
54                number = 1
55
56            repeat = int(num / number)
57
58            if repeat < 1:
59                repeat = 1
60
61        else:
62            repeat = 1
63            break_immediately = True
64
65        results = []
66
67        data_tmp = t.repeat(number=number, repeat=repeat)
68        min_value = min(data_tmp)
69        data_min = [min_value]
70
71        bench_start = time()
72
73        while 1:
74            data_min.extend(t.repeat(number=number, repeat=repeat))
75
76            if break_immediately or time() - bench_start > bench_time:
77                break
78
79        data_min.sort()
80        xbar = data_min[0]
81        i = 0
82
83        while i < len(data_min):
84            i = len(data_min)
85            sigma = mindev(data_min, xbar=xbar)
86
87            for i in range(2, len(data_min)):
88                if data_min[i] - xbar > 3 * sigma:
89                    break
90
91            k = i
92
93            if i < 5:
94                k = 5
95
96            del data_min[k:]
97
98        return (min(data_min) / number, mindev(data_min, xbar=xbar) / number)
99
100
101    def getUuid():
102        return str(uuid.uuid4())
103
104
105    dictionary_sizes = (5, 1000)
106
107    print_tpl = "Name: {name: <25} Size: {size: >4}; Keys: {keys: >3}; Type: {type: >10}; Time: {time:.2e}; Sigma: {sigma:.0e}"
108    str_key = '12323f29-c31f-478c-9b15-e7acc5354df9'
109    int_key = dictionary_sizes[0] - 2
110
111    if int_key < 0:
112        int_key = 0
113
114    benchmarks = (
115        {"name": "constructor(d)", "code": "klass(d)", "setup": "klass = type(o)", },
116        {"name": "constructor(kwargs)", "code": "klass(**d)", "setup": "klass = type(o)", },
117        {"name": "constructor(seq2)", "code": "klass(v)", "setup": "klass = type(o); v = tuple(d.items())", },
118        {"name": "constructor(o)", "code": "klass(o)", "setup": "klass = type(o)", },
119        {"name": "o.copy()", "code": "o.copy()", "setup": "pass", },
120        {"name": "o == o", "code": "o == o", "setup": "pass", },
121        {"name": "for x in o", "code": "for _ in o: pass", "setup": "pass", },
122        {"name": "for x in o.values()", "code": "for _ in values: pass", "setup": "values = o.values()", },
123        {"name": "for x in o.items()", "code": "for _ in items: pass", "setup": "items = o.items()", },
124        {"name": "pickle.dumps(o)", "code": "dumps(o, protocol=-1)", "setup": "from pickle import dumps", },
125        {"name": "pickle.loads(dump)", "code": "loads(dump)", "setup": "from pickle import loads, dumps; dump = dumps(o, protocol=-1)", },
126        {"name": "class.fromkeys()", "code": "fromkeys(keys)", "setup": "fromkeys = type(o).fromkeys; keys = o.keys()", },
127        {"name": "for x in o.keys()", "code": "for _ in keys: pass", "setup": "keys = o.keys()", },
128        {"name": "for x in iter(o)", "code": "for _ in iter(o): pass", "setup": "pass", },
129        {"name": "o == d", "code": "o == d", "setup": "pass", },
130        {"name": "o.get(key)", "code": "get(key)", "setup": "key = getUuid(); get = o.get", },
131        {"name": "o[key]", "code": "o[one_key]","setup": "pass", },
132        {"name": "key in o", "code": "key in o", "setup": "key = getUuid()", },
133        {"name": "key not in o", "code": "key not in o", "setup": "key = getUuid()", },
134        {"name": "hash(o)", "code": "hash(o)", "setup": "pass", },
135        {"name": "len(o)", "code": "len(o)", "setup": "pass", },
136        {"name": "o.keys()", "code": "keys()", "setup": "keys = o.keys", },
137        {"name": "o.values()", "code": "values()", "setup": "values = o.values", },
138        {"name": "o.items()", "code": "items()", "setup": "items = o.items", },
139        {"name": "iter(o)", "code": "iter(o)", "setup": "pass", },
140        {"name": "repr(o)", "code": "repr(o)", "setup": "pass", },
141        {"name": "str(o)", "code": "str(o)", "setup": "pass", },
142        {"name": "set", "code": None, "setup": "val = getUuid()", },
143    )
144
145    dict_collection = []
146
147    for n in dictionary_sizes:
148        d1 = {}
149        d2 = {}
150
151        for i in range(n-1):
152            d1[getUuid()] = getUuid()
153            d2[i] = i
154
155        d1[str_key] = getUuid()
156        d2[999] = 999
157
158        fd1 = frozendict(d1)
159        fd2 = frozendict(d2)
160
161        dict_collection.append({"str": ((d1, fd1), str_key), "int": ((d2, fd2), int_key)})
162
163    for benchmark in benchmarks:
164        print("################################################################################")
165
166        for dict_collection_entry in dict_collection:
167            for (dict_keys, (dicts, one_key)) in dict_collection_entry.items():
168
169                if benchmark["name"] == "constructor(kwargs)" and dict_keys == "int":
170                    continue
171
172                print("////////////////////////////////////////////////////////////////////////////////")
173
174                for o in dicts:
175                    if benchmark["name"] == "hash(o)" and type(o) == dict:
176                        continue
177
178                    if benchmark["name"] == "set":
179                        if type(o) == dict:
180                            benchmark["code"] = "o[one_key] = val"
181                        else:
182                            benchmark["code"] = "o.set(one_key, val)"
183
184                    d = dicts[0]
185
186                    bench_res = autorange(
187                        stmt = benchmark["code"],
188                        setup = benchmark["setup"],
189                        globals = {"o": o.copy(), "getUuid": getUuid, "d": d.copy(), "one_key": one_key},
190                        number = number,
191                    )
192
193                    print(print_tpl.format(
194                        name = "`{}`;".format(benchmark["name"]),
195                        keys = dict_keys,
196                        size = len(o),
197                        type = type(o).__name__,
198                        time = bench_res[0],
199                        sigma = bench_res[1],
200                    ))
201
202    print("################################################################################")
203
204if __name__ == "__main__":
205    import sys
206
207    number = None
208    argv = sys.argv
209    len_argv = len(argv)
210    max_positional_args = 1
211    max_len_argv = max_positional_args + 1
212
213    if len_argv > max_len_argv:
214        raise ValueError(
215            ("{name} must not accept more than {nargs} positional " +
216            "command-line parameters").format(name=__name__, nargs=max_positional_args)
217        )
218
219    number_arg_pos = 1
220
221    if len_argv == number_arg_pos + 1:
222        number = int(argv[number_arg_pos])
223
224    main(number)
225