aboutsummaryrefslogtreecommitdiff
path: root/measurements/tolatex.py
blob: 998da1ec64939adceb7040075350438719b24684 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# Generates benchmark graphs in LaTex (following format from the pgfplots
# package)
import sys, math

USAGE = "USAGE: python tolatex.py [input file] [num bits]"

SETUPS = {
        "gmpvar32": "color=red,mark=o", 
        "gmpxx32": "color=red,mark=x", 
        "gmpsec32" : "color=red,mark=*",
        "gmpvar64": "color=red,mark=o", 
        "gmpxx64": "color=red,mark=x", 
        "gmpsec64" : "color=red,mark=*",
        "fiat_montgomery32": "color=blue,mark=triangle*", 
        "fiat_montgomery64": "color=blue,mark=triangle*", 
        "fiat_solinas32": "color=blue,mark=triangle", 
        "fiat_solinas64": "color=blue,mark=triangle"
        }

# setups to combine and functions to combine them
COMBINE = [
        ("fiat_montgomery32", "fiat_solinas32", min),
        ("fiat_montgomery64", "fiat_solinas64", min)
        ]

# setups to exclude
EXCLUDE_32 = [
        "fiat_montgomery64",
        "fiat_solinas64",
        "gmpvar64",
        "gmpsec64",
        "gmpxx64",
        "gmpxx32"
        ]
EXCLUDE_64 = [
        "fiat_montgomery32",
        "fiat_solinas32",
        "gmpvar32",
        "gmpsec32",
        "gmpxx64",
        "gmpxx32"
        ]

LEGEND = {
        "fiat_montgomery32": "this paper", 
        "fiat_montgomery64": "this paper", 
        "fiat_solinas32": "this paper", 
        "fiat_solinas64": "this paper", 
        "gmpvar32": "GMP mpn API", 
        "gmpxx32": "GMP C++ API", 
        "gmpsec32" : "GMP mpn_sec API",
        "gmpvar64": "GMP mpn API",
        "gmpxx64": "GMP C++ API", 
        "gmpsec64" : "GMP mpn_sec API"
        }

class ParseException(Exception): pass

def parse_line(line):
    data = line.strip().split("\t")
    if len(data) != 3 or (data[1] not in SETUPS) or ("2e" not in data[0]) :
        raise ParseException("Could not parse line %s" %line.strip())
    return { 
            "prime" : data[0],
            "setup" : data[1],
            "time" : data[2] }

# given a string representing one term or "tap" in a prime, returns a pair of
# integers representing the weight and coefficient of that tap
#    "2 ^ y" -> [1, y]
#    "x * 2 ^ y" -> [x, y]
#    "x * y" -> [x*y,0]
#    "x" -> [x,0]
def parse_term(t) :
    if "*" not in t and "^" not in t:
        return [int(t),0]

    if "*" in t:
        if len(t.split("*")) > 2: # this occurs when e.g. [w - x * y] has been turned into [w + -1 * x * y]
            a1,a2,b = t.split("*")
            a = int(a1) * int(a2)
        else:
            a,b = t.split("*")
        if "^" not in b:
            return [int(a) * int(b),0]
    else:
        a,b = (1,t)

    b,e = b.split("^")
    if int(b) != 2:
        raise NonBase2Exception("Could not parse term, power with base other than 2: %s" %t)
    return [int(a),int(e)]

# expects prime to be a string and expressed as sum/difference of products of
# two with small coefficients (e.g. '2^448 - 2^224 - 1', '2^255 - 19')
def parse_prime(prime):
    prime = prime.replace("-", "+ -").replace(' ', '').replace('+-2^', '+-1*2^')
    terms = prime.split("+")
    return list(map(parse_term, terms))

# remove duplicates, reorganize, and parse primes
def clean_data(parsed_lines):
    out = {s:{} for s in SETUPS}
    for ln in parsed_lines:
        prime2 = ln["prime"].replace("e", "^").replace("m", "-").replace("p","+").replace("x","*")
        p = sum([(x * (2**e)) for x,e in parse_prime(prime2)])
        # if some measurement is duplicated, ignore the repeats
        if p not in out[ln["setup"]]:
            out[ln["setup"]][p] = ln["time"]
    # combine setups according to COMBINE list
    for s1, s2, f in COMBINE:
        all_primes = list(out[s1].keys())
        all_primes.extend(out[s2].keys())
        for p in set(all_primes):
            if p in out[s1] and p in out[s2]:
                out[s1][p] = f(out[s1][p], out[s2][p])
            elif p in out[s2]:
                out[s1][p] = out[s2][p]
    return out

def makeplot(data, bits):
    out = """
 \\begin{figure*}
 \\begin{tikzpicture}
 \t\\begin{axis}[
 \t\theight=3.4cm,
 \t\ttitle style={font=\small},
 \t\ttitle=%s-bit Field Arithmetic Benchmarks,
 \t\twidth=\\textwidth,
 \t\tlegend pos= north west,
 \t\txtick distance=64,
 \t\tlegend style={font=\\tiny},
 \t\tlabel style={font=\\footnotesize},
 \t\txlabel style={at={(0.5,0.1)}, anchor=north},
 \t\tlegend columns=2,
 \t\ttick label style={font=\\footnotesize},
 \t\tgrid=major,
 \t\tymin=0,
 \t\txlabel=log2(prime),
 \t\tylabel=Time (seconds)]\n""" %bits
    for s in SETUPS:
        if (bits == 32 and s in EXCLUDE_32) or (bits == 64 and s in EXCLUDE_64):
            continue
        if any([x[1]==s for x in COMBINE]):
            continue # in this case, the setup has been combined into some other one
        out +="\t\t\\addplot[%s,mark size=2pt] coordinates {\n" %SETUPS[s]
        for p,t in sorted(data[s].items()):
            out += "\t\t\t(%s, %s) \n" %(math.log2(p), t)
        out += "\t\t};\n"
        out += "\t\t\\addlegendentry{%s}\n\n" %LEGEND[s].replace("_", "\_")
    out += "\t\end{axis}\n\\end{tikzpicture}\n\\end{figure*}"
    return out

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print(USAGE)
        sys.exit()
    bits = int(sys.argv[2])
    f = open(sys.argv[1])
    parsed_lines = []
    for line in f:
        try:
            parsed_lines.append(parse_line(line))
        except ParseException:
            print("WARNING: Could not parse line %s, skipping" %line.strip().split("\t"))
    f.close()
    print(makeplot(clean_data(parsed_lines), bits))