aboutsummaryrefslogtreecommitdiffstats
path: root/util/string/benchmark/float_to_string/main.cpp
blob: b52a5e5ab1909d900733ea2c69ceea34fae409c1 (plain) (blame)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#include <library/cpp/testing/benchmark/bench.h>

#include <util/generic/singleton.h>
#include <util/generic/vector.h>
#include <util/generic/xrange.h>
#include <util/generic/ymath.h>
#include <util/random/fast.h>
#include <util/string/cast.h>
#include <util/string/printf.h>

#include <limits>

#include <cmath>

/* Please be careful before making any decisions based on this benchmark.
 *
 * Only `Sprintf("%.<decimals>f", x)` and `FloatToString(x, PREC_POINT_DIGITS, decimals` produce
 * equal results in general case. However, results for cases when x \in [0, 1) must be equal for
 * both `Sprintf` and `FloatToString`.
 *
 * Read more about formatting in STL [1, 2] and Yandex Util formatting [3]
 *
 * [1] http://www.cplusplus.com/reference/cstdio/printf/
 * [2] http://en.cppreference.com/w/c/io/fprintf
 * [3] https://a.yandex-team.ru/arc/trunk/arcadia/util/string/cast.h?rev=2432660#L29
 */

namespace {
    template <typename T>
    struct TExample {
        T Value{};
        int DigitsCount{};
    };

    template <typename T, size_t N>
    struct TExamplesHolder {
        TVector<TExample<T>> Examples; 

        TExamplesHolder()
            : Examples(N)
        {
            TFastRng<ui64> prng{N * sizeof(T) * 42};
            for (auto& x : Examples) {
                x.Value = prng.GenRandReal4() + prng.Uniform(Max<ui16>());
                x.DigitsCount = prng.Uniform(std::numeric_limits<T>::max_digits10 + 1);
            }
        }
    };

    template <typename T, size_t N>
    struct TNearZeroExamplesHolder {
        TVector<TExample<T>> Examples; 

        TNearZeroExamplesHolder()
            : Examples(N)
        {
            TFastRng<ui64> prng{N * sizeof(T) * 42};
            for (auto& x : Examples) {
                x.Value = prng.GenRandReal4();
                x.DigitsCount = prng.Uniform(std::numeric_limits<T>::max_digits10 + 1);
            }
        }
    };
}

static const char* FORMAT_FIXED[] = {
    "%.0f",
    "%.1f",
    "%.2f",
    "%.3f",
    "%.4f",
    "%.5f",
    "%.6f",
    "%.7f",
    "%.8f",
    "%.9f",
    "%.10f",
    "%.11f",
    "%.12f",
    "%.13f",
    "%.14f",
    "%.15f",
    "%.16f",
    "%.17f",
};

static const char* FORMAT_SIGNIFICANT[] = {
    "%.0g",
    "%.1g",
    "%.2g",
    "%.3g",
    "%.4g",
    "%.5g",
    "%.6g",
    "%.7g",
    "%.8g",
    "%.9g",
    "%.10g",
    "%.11g",
    "%.12g",
    "%.13g",
    "%.14g",
    "%.15g",
    "%.16g",
    "%.17g",
};

#define DEFINE_BENCHMARK(type, count)                                                                \
    Y_CPU_BENCHMARK(SprintfAuto_##type##_##count, iface) {                                           \
        const auto& examples = Default<TExamplesHolder<type, count>>().Examples;                     \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                /* this is in fact equal to Sprintf("%.6f", e.Value) and that is why it is faster */ \
                /* than FloatToString(e.Value) */                                                    \
                Y_DO_NOT_OPTIMIZE_AWAY(Sprintf("%f", e.Value));                                      \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(FloatToStringAuto_##type##_##count, iface) {                                     \
        const auto& examples = Default<TExamplesHolder<type, count>>().Examples;                     \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value));                                      \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(SprintfFixed_##type##_##count, iface) {                                          \
        const auto& examples = Default<TExamplesHolder<type, count>>().Examples;                     \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_FIXED[e.DigitsCount], e.Value));               \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(FloatToStringFixed_##type##_##count, iface) {                                    \
        const auto& examples = Default<TExamplesHolder<type, count>>().Examples;                     \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_NDIGITS, e.DigitsCount));         \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(SprintfSignificant_##type##_##count, iface) {                                    \
        const auto& examples = Default<TExamplesHolder<type, count>>().Examples;                     \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_SIGNIFICANT[e.DigitsCount], e.Value));         \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(FloatToStringSignificant_##type##_##count, iface) {                              \
        const auto& examples = Default<TExamplesHolder<type, count>>().Examples;                     \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_POINT_DIGITS, e.DigitsCount));    \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(NearZeroSprintfAuto_##type##_##count, iface) {                                   \
        const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples;             \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                /* this is in fact equal to Sprintf("%.6f", e.Value) and that is why it is faster */ \
                /* than FloatToString(e.Value) */                                                    \
                Y_DO_NOT_OPTIMIZE_AWAY(Sprintf("%f", e.Value));                                      \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(NearZeroFloatToStringAuto_##type##_##count, iface) {                             \
        const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples;             \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value));                                      \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(NearZeroSprintfFixed_##type##_##count, iface) {                                  \
        const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples;             \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_FIXED[e.DigitsCount], e.Value));               \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(NearZeroFloatToStringFixed_##type##_##count, iface) {                            \
        const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples;             \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_NDIGITS, e.DigitsCount));         \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(NearZeroSprintfSignificant_##type##_##count, iface) {                            \
        const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples;             \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_SIGNIFICANT[e.DigitsCount], e.Value));         \
            }                                                                                        \
        }                                                                                            \
    }                                                                                                \
                                                                                                     \
    Y_CPU_BENCHMARK(NearZeroFloatToStringSignificant_##type##_##count, iface) {                      \
        const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples;             \
        for (const auto i : xrange(iface.Iterations())) {                                            \
            Y_UNUSED(i);                                                                             \
            for (const auto e : examples) {                                                          \
                Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_POINT_DIGITS, e.DigitsCount));    \
            }                                                                                        \
        }                                                                                            \
    }

DEFINE_BENCHMARK(float, 1);
DEFINE_BENCHMARK(float, 2);
DEFINE_BENCHMARK(float, 4);
DEFINE_BENCHMARK(float, 8);
DEFINE_BENCHMARK(float, 16);
DEFINE_BENCHMARK(float, 32);
DEFINE_BENCHMARK(float, 64);
DEFINE_BENCHMARK(float, 128);
DEFINE_BENCHMARK(float, 256);

DEFINE_BENCHMARK(double, 1);
DEFINE_BENCHMARK(double, 2);
DEFINE_BENCHMARK(double, 4);
DEFINE_BENCHMARK(double, 8);
DEFINE_BENCHMARK(double, 16);
DEFINE_BENCHMARK(double, 32);
DEFINE_BENCHMARK(double, 64);
DEFINE_BENCHMARK(double, 128);
DEFINE_BENCHMARK(double, 256);

#undef DEFINE_BENCHMARK