main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #include <library/cpp/testing/benchmark/bench.h>
  2. #include <util/generic/singleton.h>
  3. #include <util/generic/vector.h>
  4. #include <util/generic/xrange.h>
  5. #include <util/generic/ymath.h>
  6. #include <util/random/fast.h>
  7. #include <util/string/cast.h>
  8. #include <util/string/printf.h>
  9. #include <limits>
  10. #include <cmath>
  11. /* Please be careful before making any decisions based on this benchmark.
  12. *
  13. * Only `Sprintf("%.<decimals>f", x)` and `FloatToString(x, PREC_POINT_DIGITS, decimals` produce
  14. * equal results in general case. However, results for cases when x \in [0, 1) must be equal for
  15. * both `Sprintf` and `FloatToString`.
  16. *
  17. * Read more about formatting in STL [1, 2] and Yandex Util formatting [3]
  18. *
  19. * [1] http://www.cplusplus.com/reference/cstdio/printf/
  20. * [2] http://en.cppreference.com/w/c/io/fprintf
  21. * [3] https://a.yandex-team.ru/arc/trunk/arcadia/util/string/cast.h?rev=2432660#L29
  22. */
  23. namespace {
  24. template <typename T>
  25. struct TExample {
  26. T Value{};
  27. int DigitsCount{};
  28. };
  29. template <typename T, size_t N>
  30. struct TExamplesHolder {
  31. TVector<TExample<T>> Examples;
  32. TExamplesHolder()
  33. : Examples(N)
  34. {
  35. TFastRng<ui64> prng{N * sizeof(T) * 42};
  36. for (auto& x : Examples) {
  37. x.Value = prng.GenRandReal4() + prng.Uniform(Max<ui16>());
  38. x.DigitsCount = prng.Uniform(std::numeric_limits<T>::max_digits10 + 1);
  39. }
  40. }
  41. };
  42. template <typename T, size_t N>
  43. struct TNearZeroExamplesHolder {
  44. TVector<TExample<T>> Examples;
  45. TNearZeroExamplesHolder()
  46. : Examples(N)
  47. {
  48. TFastRng<ui64> prng{N * sizeof(T) * 42};
  49. for (auto& x : Examples) {
  50. x.Value = prng.GenRandReal4();
  51. x.DigitsCount = prng.Uniform(std::numeric_limits<T>::max_digits10 + 1);
  52. }
  53. }
  54. };
  55. }
  56. static const char* FORMAT_FIXED[] = {
  57. "%.0f",
  58. "%.1f",
  59. "%.2f",
  60. "%.3f",
  61. "%.4f",
  62. "%.5f",
  63. "%.6f",
  64. "%.7f",
  65. "%.8f",
  66. "%.9f",
  67. "%.10f",
  68. "%.11f",
  69. "%.12f",
  70. "%.13f",
  71. "%.14f",
  72. "%.15f",
  73. "%.16f",
  74. "%.17f",
  75. };
  76. static const char* FORMAT_SIGNIFICANT[] = {
  77. "%.0g",
  78. "%.1g",
  79. "%.2g",
  80. "%.3g",
  81. "%.4g",
  82. "%.5g",
  83. "%.6g",
  84. "%.7g",
  85. "%.8g",
  86. "%.9g",
  87. "%.10g",
  88. "%.11g",
  89. "%.12g",
  90. "%.13g",
  91. "%.14g",
  92. "%.15g",
  93. "%.16g",
  94. "%.17g",
  95. };
  96. #define DEFINE_BENCHMARK(type, count) \
  97. Y_CPU_BENCHMARK(SprintfAuto_##type##_##count, iface) { \
  98. const auto& examples = Default<TExamplesHolder<type, count>>().Examples; \
  99. for (const auto i : xrange(iface.Iterations())) { \
  100. Y_UNUSED(i); \
  101. for (const auto e : examples) { \
  102. /* this is in fact equal to Sprintf("%.6f", e.Value) and that is why it is faster */ \
  103. /* than FloatToString(e.Value) */ \
  104. Y_DO_NOT_OPTIMIZE_AWAY(Sprintf("%f", e.Value)); \
  105. } \
  106. } \
  107. } \
  108. \
  109. Y_CPU_BENCHMARK(FloatToStringAuto_##type##_##count, iface) { \
  110. const auto& examples = Default<TExamplesHolder<type, count>>().Examples; \
  111. for (const auto i : xrange(iface.Iterations())) { \
  112. Y_UNUSED(i); \
  113. for (const auto e : examples) { \
  114. Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value)); \
  115. } \
  116. } \
  117. } \
  118. \
  119. Y_CPU_BENCHMARK(SprintfFixed_##type##_##count, iface) { \
  120. const auto& examples = Default<TExamplesHolder<type, count>>().Examples; \
  121. for (const auto i : xrange(iface.Iterations())) { \
  122. Y_UNUSED(i); \
  123. for (const auto e : examples) { \
  124. Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_FIXED[e.DigitsCount], e.Value)); \
  125. } \
  126. } \
  127. } \
  128. \
  129. Y_CPU_BENCHMARK(FloatToStringFixed_##type##_##count, iface) { \
  130. const auto& examples = Default<TExamplesHolder<type, count>>().Examples; \
  131. for (const auto i : xrange(iface.Iterations())) { \
  132. Y_UNUSED(i); \
  133. for (const auto e : examples) { \
  134. Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_NDIGITS, e.DigitsCount)); \
  135. } \
  136. } \
  137. } \
  138. \
  139. Y_CPU_BENCHMARK(SprintfSignificant_##type##_##count, iface) { \
  140. const auto& examples = Default<TExamplesHolder<type, count>>().Examples; \
  141. for (const auto i : xrange(iface.Iterations())) { \
  142. Y_UNUSED(i); \
  143. for (const auto e : examples) { \
  144. Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_SIGNIFICANT[e.DigitsCount], e.Value)); \
  145. } \
  146. } \
  147. } \
  148. \
  149. Y_CPU_BENCHMARK(FloatToStringSignificant_##type##_##count, iface) { \
  150. const auto& examples = Default<TExamplesHolder<type, count>>().Examples; \
  151. for (const auto i : xrange(iface.Iterations())) { \
  152. Y_UNUSED(i); \
  153. for (const auto e : examples) { \
  154. Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_POINT_DIGITS, e.DigitsCount)); \
  155. } \
  156. } \
  157. } \
  158. \
  159. Y_CPU_BENCHMARK(NearZeroSprintfAuto_##type##_##count, iface) { \
  160. const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples; \
  161. for (const auto i : xrange(iface.Iterations())) { \
  162. Y_UNUSED(i); \
  163. for (const auto e : examples) { \
  164. /* this is in fact equal to Sprintf("%.6f", e.Value) and that is why it is faster */ \
  165. /* than FloatToString(e.Value) */ \
  166. Y_DO_NOT_OPTIMIZE_AWAY(Sprintf("%f", e.Value)); \
  167. } \
  168. } \
  169. } \
  170. \
  171. Y_CPU_BENCHMARK(NearZeroFloatToStringAuto_##type##_##count, iface) { \
  172. const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples; \
  173. for (const auto i : xrange(iface.Iterations())) { \
  174. Y_UNUSED(i); \
  175. for (const auto e : examples) { \
  176. Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value)); \
  177. } \
  178. } \
  179. } \
  180. \
  181. Y_CPU_BENCHMARK(NearZeroSprintfFixed_##type##_##count, iface) { \
  182. const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples; \
  183. for (const auto i : xrange(iface.Iterations())) { \
  184. Y_UNUSED(i); \
  185. for (const auto e : examples) { \
  186. Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_FIXED[e.DigitsCount], e.Value)); \
  187. } \
  188. } \
  189. } \
  190. \
  191. Y_CPU_BENCHMARK(NearZeroFloatToStringFixed_##type##_##count, iface) { \
  192. const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples; \
  193. for (const auto i : xrange(iface.Iterations())) { \
  194. Y_UNUSED(i); \
  195. for (const auto e : examples) { \
  196. Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_NDIGITS, e.DigitsCount)); \
  197. } \
  198. } \
  199. } \
  200. \
  201. Y_CPU_BENCHMARK(NearZeroSprintfSignificant_##type##_##count, iface) { \
  202. const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples; \
  203. for (const auto i : xrange(iface.Iterations())) { \
  204. Y_UNUSED(i); \
  205. for (const auto e : examples) { \
  206. Y_DO_NOT_OPTIMIZE_AWAY(Sprintf(FORMAT_SIGNIFICANT[e.DigitsCount], e.Value)); \
  207. } \
  208. } \
  209. } \
  210. \
  211. Y_CPU_BENCHMARK(NearZeroFloatToStringSignificant_##type##_##count, iface) { \
  212. const auto& examples = Default<TNearZeroExamplesHolder<type, count>>().Examples; \
  213. for (const auto i : xrange(iface.Iterations())) { \
  214. Y_UNUSED(i); \
  215. for (const auto e : examples) { \
  216. Y_DO_NOT_OPTIMIZE_AWAY(FloatToString(e.Value, PREC_POINT_DIGITS, e.DigitsCount)); \
  217. } \
  218. } \
  219. }
  220. DEFINE_BENCHMARK(float, 1);
  221. DEFINE_BENCHMARK(float, 2);
  222. DEFINE_BENCHMARK(float, 4);
  223. DEFINE_BENCHMARK(float, 8);
  224. DEFINE_BENCHMARK(float, 16);
  225. DEFINE_BENCHMARK(float, 32);
  226. DEFINE_BENCHMARK(float, 64);
  227. DEFINE_BENCHMARK(float, 128);
  228. DEFINE_BENCHMARK(float, 256);
  229. DEFINE_BENCHMARK(double, 1);
  230. DEFINE_BENCHMARK(double, 2);
  231. DEFINE_BENCHMARK(double, 4);
  232. DEFINE_BENCHMARK(double, 8);
  233. DEFINE_BENCHMARK(double, 16);
  234. DEFINE_BENCHMARK(double, 32);
  235. DEFINE_BENCHMARK(double, 64);
  236. DEFINE_BENCHMARK(double, 128);
  237. DEFINE_BENCHMARK(double, 256);
  238. #undef DEFINE_BENCHMARK