1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 16 авг. 2018 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #ifndef TEST_PTEST_H_
23 #define TEST_PTEST_H_
24 
25 #include <stddef.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <time.h>
29 #include <core/types.h>
30 #include <core/stdlib/stdio.h>
31 #include <data/cstorage.h>
32 #include <test/test.h>
33 
34 #define PTEST_BEGIN(group, name, time, iterations) \
35         namespace ptest { \
36         namespace { \
37             \
38             using namespace ::test; \
39             \
40             class ptest_ ## name: public PerformanceTest { \
41                 public: \
42                     typedef ptest_ ## name test_type_t; \
43                 \
44                 public: \
45                     explicit ptest_ ## name() : PerformanceTest(group, #name, time, iterations) {} \
46                     \
47                     virtual ~ptest_ ## name() {}
48 
49 #define PTEST_IGNORE \
50         virtual bool ignore() const { return true; }
51 
52 #define PTEST_MAIN \
53         virtual void execute(int argc, const char **argv)
54 
55 #define PTEST_SUPPORTED(ptr)        TEST_SUPPORTED(ptr)
56 
57 #define PTEST_LOOP(__key, ...) { \
58         double __start = clock(); \
59         double __time = 0.0f; \
60         wsize_t __iterations = 0; \
61         \
62         do { \
63             for (size_t __i=0; __i<__test_iterations; ++__i) { \
64                 __VA_ARGS__; \
65             } \
66             /* Calculate statistics */ \
67             __iterations   += __test_iterations; \
68             __time          = (clock() - __start) / CLOCKS_PER_SEC; \
69         } while (__time < __test_time); \
70         \
71         gather_stats(__key, __time, __iterations); \
72         if (__verbose) { \
73             printf("  time [s]:                 %.2f/%.2f\n", __time, __test_time); \
74             printf("  iterations:               %ld/%ld\n", long(__iterations), long((__iterations * __test_time) / __time)); \
75             printf("  performance [i/s]:        %.2f\n", __iterations / __time); \
76             printf("  iteration time [us/i]:    %.4f\n\n", (1000000.0 * __time) / __iterations); \
77         } \
78     }
79 
80 #define PTEST_KLOOP(__key, __mul, ...) { \
81         double __start = clock(); \
82         double __time = 0.0f; \
83         wsize_t __iterations = 0; \
84         wsize_t __k_iterations = __test_iterations; \
85         \
86         do { \
87             for (size_t __i=0; __i<__k_iterations; ++__i) { \
88                 __VA_ARGS__; \
89             } \
90             /* Calculate statistics */ \
91             __iterations   += __k_iterations; \
92             __time          = (clock() - __start) / CLOCKS_PER_SEC; \
93         } while (__time < __test_time); \
94         \
95         gather_stats(__key, __time, __iterations); \
96         if (__verbose) { \
97             printf("  time [s]:                 %.2f/%.2f\n", __time, __test_time); \
98             printf("  iterations:               %ld/%ld\n", long(__iterations), long((__iterations * __test_time) / __time)); \
99             printf("  performance [i/s]:        %.2f\n", __iterations / __time); \
100             printf("  iteration time [us/i]:    %.4f\n\n", (1000000.0 * __time) / __iterations); \
101         } \
102     }
103 
104 #define PTEST_SEPARATOR \
105         gather_stats(NULL, 0.0f, 0); \
106         printf("\n");
107 
108 #define PTEST_SEPARATOR2 \
109         gather_stats(NULL, 0.0f, 1); \
110         printf("\n");
111 
112 #define PTEST_FAIL_MSG(message, ...) {  \
113             fprintf(stderr, "Performance test '%s.%s' has failed at file %s, line %d with message:\n  " message  "\n", \
114                     __test_group, __test_name, __FILE__, __LINE__, ## __VA_ARGS__); \
115             exit(1); \
116         }
117 
118 #define PTEST_FAIL() {\
119             fprintf(stderr, "Performance test '%s.%s' has failed at file %s, line %d\n", \
120                     __test_group, __test_name, __FILE__, __LINE__); \
121             exit(1); \
122         }
123 
124 #define PTEST_END \
125         } performance_test;  /* ptest class */ \
126         } /* anonymous namespace */ \
127         } /* namespace ptest */
128 
129 namespace test
130 {
131     class PerformanceTest: public Test
132     {
133         private:
134             friend PerformanceTest *ptest_init();
135 
136         private:
137             static PerformanceTest    *__root;
138             PerformanceTest           *__next;
139 
140         protected:
141             typedef struct stats_t
142             {
143                 char       *key;            /* The loop indicator */
144                 char       *time;           /* Actual time [seconds] */
145                 char       *n_time;         /* Normalized time [seconds] */
146                 char       *iterations;     /* Number of iterations */
147                 char       *n_iterations;   /* Normalized number of iterations */
148                 char       *performance;    /* The performance of test [iterations per second] */
149                 char       *time_cost;      /* The amount of time spent per iteration [milliseconds per iteration] */
150                 char       *rel;            /* The relative speed */
151                 double      cost;           /* The overall cost */
152             } stats_t;
153 
154         protected:
155             size_t                              __test_iterations;
156             double                              __test_time;
157             mutable lsp::cstorage<stats_t>      __test_stats;
158 
159         protected:
160             void gather_stats(const char *key, double time, wsize_t iterations);
161             static void destroy_stats(stats_t *stats);
162             static void estimate(size_t *len, const char *text);
163             static void out_text(FILE *out, size_t length, const char *text, int align, const char *padding, const char *tail);
164 
165         public:
166             int             printf(const char *fmt, ...);
167 
168         public:
169             explicit PerformanceTest(const char *group, const char *name, float time, size_t iterations);
170             virtual ~PerformanceTest();
171 
172         public:
next()173             inline PerformanceTest *next()          { return __next; }
next_test()174             virtual Test *next_test() const         { return const_cast<PerformanceTest *>(__next); };
175 
176             void dump_stats(FILE *out) const;
177             void free_stats();
178     };
179 
180 
181     /**
182      * Initialize set of performance tests (validate duplicates, etc)
183      * @return valid set of performance tests
184      */
185     PerformanceTest *ptest_init();
186 }
187 
188 #endif /* TEST_PTEST_H_ */
189