1 | #!/usr/bin/env bash
|
2 | #
|
3 | # 2018 experiments on determism. There are 2017 experiments in compare.sh and
|
4 | # misc/determinism.py.
|
5 | #
|
6 | # I think I fixed the misc.Set() bug in OPy, but there still remained CPython
|
7 | # determinism. However I haven't reproduced it on a small case.
|
8 | #
|
9 | # Usage:
|
10 | # ./determinism.sh <function name>
|
11 |
|
12 | set -o nounset
|
13 | set -o pipefail
|
14 | set -o errexit
|
15 |
|
16 | # Trying to reproduce problem with pyassem.py and Block() in order_blocks, but
|
17 | # this does NOT do it.
|
18 | # I had to add sorted() to make it stable there, but here I do not? Why?
|
19 |
|
20 | # See also: https://github.com/NixOS/nixpkgs/issues/22570
|
21 | #
|
22 | # "No, the sets are built as real sets and then marshalled to .pyc files in a
|
23 | # separate step. So on CPython an essentially random order will end up in the
|
24 | # .pyc file. Even CPython 3.6 gives a deterministic order to dictionaries but
|
25 | # not sets. You could ensure sets are marshalled in a known order by changing
|
26 | # the marshalling code, e.g. to emit them in sorted order (on Python 2.x; on
|
27 | # 3.x it is more messy because different types are more often non-comparable)."
|
28 | #
|
29 | # Is that accurate? The issue here is not sets as marshalled constants; it's
|
30 | # USING sets in the compiler.
|
31 | #
|
32 | # set([1, 2, 3]) and {'a': 'b'} do not produce literal constants!
|
33 |
|
34 | dictset() {
|
35 | local n=${1:-30}
|
36 | local python=${2:-python}
|
37 |
|
38 | seq $n | $python -c '
|
39 | import sys
|
40 | class Block:
|
41 | def __init__(self, x):
|
42 | self.x = x
|
43 | def __repr__(self):
|
44 | return str(self.x)
|
45 |
|
46 | s = set()
|
47 | hashes = []
|
48 | for line in sys.stdin:
|
49 | b = Block(line.strip())
|
50 | hashes.append(hash(b))
|
51 | s.add(b)
|
52 | print s
|
53 | print hashes
|
54 | '
|
55 | }
|
56 |
|
57 | dictset2() {
|
58 | local n=${1:-30}
|
59 | local python=${2:-python}
|
60 |
|
61 | seq $n | $python -c '
|
62 | import sys
|
63 | d = {}
|
64 | s = set()
|
65 | for line in sys.stdin:
|
66 | i = line.strip()
|
67 | d[i] = 1
|
68 | s.add(i)
|
69 | print "D", " ".join(d.keys())
|
70 | print "S", " ".join(s)
|
71 | '
|
72 | }
|
73 |
|
74 | # Each iteration is stable.
|
75 | compare-iters() {
|
76 | for i in $(seq 10); do
|
77 | # Run it twice with the same seed
|
78 | dictset
|
79 | done
|
80 | }
|
81 |
|
82 | # Changing the seed changes the order.
|
83 |
|
84 | # Aha! hash(Block()) is still not deterministic with a fixed seed, because it
|
85 | # uses the address?
|
86 | #
|
87 | # https://stackoverflow.com/questions/11324271/what-is-the-default-hash-in-python
|
88 |
|
89 | compare-seed() {
|
90 | for seed in 1 2 3; do
|
91 | echo "seed = $seed"
|
92 | # Run it twice with the same seed
|
93 | PYTHONHASHSEED=$seed $0 dictset
|
94 | PYTHONHASHSEED=$seed $0 dictset
|
95 | done
|
96 | }
|
97 |
|
98 | # Hm this is stable oto.
|
99 | compare-python() {
|
100 | for i in $(seq 10); do
|
101 | dictset
|
102 | dictset '' ../_devbuild/cpython-full/python
|
103 | done
|
104 | }
|
105 |
|
106 | #
|
107 | # OPy
|
108 | #
|
109 |
|
110 | # See smoke.sh
|
111 |
|
112 | "$@"
|