MAJ terminée. Nous sommes passés en version 14.6.2 . Pour consulter les "releases notes" associées c'est ici :

https://about.gitlab.com/releases/2022/01/11/security-release-gitlab-14-6-2-released/
https://about.gitlab.com/releases/2022/01/04/gitlab-14-6-1-released/

TestDriver.hpp 22.1 KB
Newer Older
Quentin Khan's avatar
Quentin Khan committed
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

#ifndef TRAITSMANAGEMENT_HPP
#define TRAITSMANAGEMENT_HPP

#include <memory>
#include <vector>

#include "FPerfTestParams.hpp"
#include "Descriptors.hpp"

namespace scalfmm  {

    namespace sfinae {

        /**
         * \brief Algorithm / Kernel descriptor enabled type trait
         *
         * Inactive algorithm and kernel descriptors define a 'disabled' value
         * convertible to bool.
         */
        template<typename T>
        struct is_enabled {
            /// c++17 will define this for SFINAE convenience
            template<class ...> using void_t = void;

            /// Disabled type trait definition
            template<class U, class = void_t<> >
            struct disabled: std::false_type {};

Quentin Khan's avatar
Quentin Khan committed
30
            /// Disabled type trait specialisation
Quentin Khan's avatar
Quentin Khan committed
31
32
            template<class U>
            struct disabled<U, void_t<decltype(U::disabled)> >
33
            { enum { value = U::disabled }; };
Quentin Khan's avatar
Quentin Khan committed
34
35

            /// Type trait value, true if the T is enabled
36
            enum {value = !disabled<T>::value};
Quentin Khan's avatar
Quentin Khan committed
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
        };

        /**
         * \brief Algorithm and kernel descriptors compatibility type trait
         *
         * Several inner type traits are defined to check individual properties.
         *
         *
         * \tparam kernel_desc Kernel descriptor
         * \tparam algo_desc   Algorithm descriptor
         */
        template<typename kernel_desc, typename algo_desc>
        struct compatible {

            /// c++17 will define this for SFINAE convenience
            template<typename... Ts> using void_t = void;

            /** \brief Tree descriptor */
            using tree_desc = typename algo_desc::tree_desc;

            /**
             * \brief Tree factory type trait definition
             *
             * #tree_desc should declare a tree_factory template.
             *
             * \tparam T Dependent name, use with #tree_desc
             */
            template<typename T, typename = void_t<> >
            struct has_tree_factory : std::false_type {};

            /**
             * \brief Tree factory type trait success specialisation
             *
             * #tree_desc should declare a tree_factory template.
             *
             * \tparam T Dependent name, use with #tree_desc
             */
            template<typename T>
            struct has_tree_factory<T, void_t<typename T::template
                                              tree_factory<algo_desc, kernel_desc>
                                              > >
                : std::true_type {};


            /**
             * \brief Loader factory definition type trait definition
             *
             * #algo_desc should declare a loader_factory template.
             *
             * \tparam T Dependent name, use with #algo_desc
             */
            template<typename T, typename = void_t<> >
            struct has_loader_factory : std::false_type {};

            /**
             * \brief Loader factory type trait success specialisation
             *
             * #algo_desc should declare a loader_descriptor::loader_factory
             * type.
             *
             * \tparam T Dependent name, use with #algo_desc
             */
            template<typename T>
            struct has_loader_factory<T, void_t<typename T::loader_descriptor::
                                                loader_factory> >
                : std::true_type {};


            /**
             * \brief Kernel factory type trait definition
             *
             * #kernel_desc should declare a kernel_factory template.
             *
             * \tparam T Dependent name, use with #kernel_desc
             */
            template<typename T, typename = void_t<> >
            struct has_kernel_factory : std::false_type {};

            /**
             * \brief Kernel factory type trait success specialisation
             *
             * #kernel_desc should declare a kernel_factory template.
             *
             * \tparam T Dependent name, use with #kernel_desc
             */
            template<typename T>
            struct has_kernel_factory<
                T, void_t<typename T::template
                          kernel_factory<typename algo_desc::tree_desc> > >
                : std::true_type {};


            /**
             * \brief Algorithm factory type trait definition
             *
             * #algo_desc should declare an algo_factory template.
             *
             * \tparam T Dependent name, use with #algo_desc
             */
            template<typename T, typename = void_t<> >
            struct has_algo_factory : std::false_type {};

            /**
             * \brief Algorithm factory type trait succes specialisation
             *
             * #algo_desc should declare an algo_factory template.
             *
             * \tparam T Dependent name, use with #algo_desc
             */
            template<typename T>
            struct has_algo_factory<T, void_t< typename T::template
                                               algo_factory<kernel_desc> > >
                : std::true_type {};


            /// Shorthand for the tree factory type trait value
153
154
            enum { has_tree_factory_v =
                   has_tree_factory<tree_desc>::value };
Quentin Khan's avatar
Quentin Khan committed
155
156

            /// Shorthand for the loader factory type trait value
157
158
            enum { has_loader_factory_v =
                   has_loader_factory<algo_desc>::value };
Quentin Khan's avatar
Quentin Khan committed
159
160

            /// Shorthand for the kernel factory type trait value
161
162
            enum { has_kernel_factory_v =
                   has_kernel_factory<kernel_desc>::value };
Quentin Khan's avatar
Quentin Khan committed
163
164

            /// Shorthand for the algorithm factory type trait value
165
166
            enum { has_algo_factory_v =
                   has_algo_factory<algo_desc>::value };
Quentin Khan's avatar
Quentin Khan committed
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


            /**
             * \brief Algorithm / Kernel compatibility type trait definition
             *
             * A given algorithm and kernel may not be compatible and make
             * compilation fail. To avoid this, this type trait tries the
             * `build` definition of the #algo_desc.
             *
             * The actual check is done by an internal structure. This one
             * checks the build expression prerequsites.
             *
             * \warning The adequate build method may exist in the #algo_desc,
             * however if it fails to compile after its definition, this will
             * not catch the error. The algorithm definition must check kernel
             * compatibility (using SFINAE).
             *
             * \tparam T unused parameter, may be anything
             * \tparam unnamed SFINAE checks for build expression validity
             */
            template<typename T, bool =
                     has_tree_factory_v
                     && has_kernel_factory_v
                     && has_algo_factory_v
                     >
            struct can_build_algo {
                /// Tree type shorthand
                using tree_t = typename tree_desc::
                    template tree_factory<algo_desc, kernel_desc>::tree_t;
                /// Kernel factory shorthand
                using kernel_t = typename kernel_desc::
                    template kernel_factory<tree_desc>::kernel_t;
                /// Algorithm factory shorthand
                using algo_factory = typename algo_desc::
                    template algo_factory<kernel_desc>;

                /**
                 * \brief Build expression type trait definition
                 *
                 * \tparam algo_t Algorithm factory type
                 */
                template<typename algo_f, typename = void_t<> >
                struct valid_build_expression : std::false_type {};

                /**
                 * \brief Build expression type trait success specialisation
                 *
                 * \tparam algo_t Algorithm factory type
                 */
                template<typename algo_f>
                struct valid_build_expression<
                    algo_f, void_t<
                                decltype(std::declval<algo_f>()
                                         .build(std::declval<FPerfTestParams>(),
                                                std::declval<tree_t&>(),
                                                std::declval<kernel_t&>() ))
                            > >
                    : std::true_type {};

                /// Type trait value, true if the build expression is declared
227
                enum {value = valid_build_expression<algo_factory>::value};
Quentin Khan's avatar
Quentin Khan committed
228
229
230
231
232
233
234
235
236
237
238
            };

            /**
             * \brief Algorithm / Kernel compatibility type trait failure
             * specialisation
             */
            template<typename T>
            struct can_build_algo<T, false> : std::false_type {};


            /// Shorthand for the build compatibility type trait value
239
            enum { can_build_algo_v = can_build_algo<void>::value };
Quentin Khan's avatar
Quentin Khan committed
240
241

            /// Type trait value, true if the algorithm & kernel seem compatible
242
            enum {
Quentin Khan's avatar
Quentin Khan committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
                value = has_tree_factory_v
                    && has_loader_factory_v
                    && has_kernel_factory_v
                    && has_algo_factory_v
                    && can_build_algo_v
            };
        };

    }



    namespace details {

        namespace {
            enum {max_descriptor_count = 15};
        }

        /**
         * \brief Compile time type list
         *
Quentin Khan's avatar
Quentin Khan committed
264
         * Used to pass type lists using template argument deduction.
Quentin Khan's avatar
Quentin Khan committed
265
         */
Quentin Khan's avatar
Quentin Khan committed
266
267
268
269
270
        template<typename ... Ts>
        struct type_list {
            template<typename T>
            using append = type_list<Ts..., T>;
        };
Quentin Khan's avatar
Quentin Khan committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292


        /**
         * \brief Last step, setup everything and run if descriptors are compatible
         *
         * Called once all descriptors have been found. A compile check is done to
         * ensure algorithm and kernel compatibility.
         *
         * \tparam kernel_desc Kernel descriptor
         * \tparam algo_desc   Algorithm descriptor
         */
        template<typename kernel_desc, typename algo_desc,
                 bool = sfinae::compatible<kernel_desc, algo_desc>::value>
        struct setup_step {
            /// Tree descriptor
            using tree_desc   = typename algo_desc::tree_desc;

            using tree_factory   = typename tree_desc::template tree_factory<algo_desc, kernel_desc>;
            using loader_factory = typename algo_desc::loader_descriptor::loader_factory;
            using kernel_factory = typename kernel_desc::template kernel_factory<tree_desc>;
            using algo_factory   = typename algo_desc::template algo_factory<kernel_desc>;

Quentin Khan's avatar
Quentin Khan committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
            /**
             * \brief Detect existence of static `info` method in class
             *
             * \tparam K Class to check
             * \tparam Args Types of `K::info` arguments if it exists
             */
            template<typename K, typename ... Args>
            struct has_info {
                /// Void alias used by SFINAE
                template<typename...> using void_t = void;
                /// Default SFINAE `check`, equals false
                template<typename k, typename = void_t<> >
                struct check : std::false_type {};
                /// `check` specialisation in case the `info` method exists
                template<typename k>
                struct check<k, void_t<decltype(k::info(std::declval<Args>()...))> >
                    : std::true_type {};
                /// True if `K::info(Args...)` exists, false otherwise
                constexpr static const bool value = check<K>::value;

                static void print() {
                    auto l = {0, (std::cerr << typeid(Args).name() << ' ', 0)...};
                    (void)l;
                    std::cerr << '\n';
                }
            };


            /**
             * \brief Call static `info` method if it exists
             *
             * \note This method is disabled through SFINAE if the method does
             * not exist.
             *
             * \tparam T Class that may offer an info static method
             * \tparam Args Parameter type pack for T::info arguments
             *
             * \param args Arguments to forward to T::info
             */
            template<typename T, typename... Args,
                     typename std::enable_if<has_info<T, Args...>::value>::type* = nullptr >
            static void call_info(Args&&... args) {
                T::info(std::forward<Args>(args)...);
            }

            /**
             * \brief Ignore inexistent `info` call
             */
            template<typename T, typename... Args>
            static void call_info(const Args&..., ...) {
                // Uncomment to debug
                // std::cerr << T::name << ": ";
                // has_info<T, Args...>::print();
            }

Quentin Khan's avatar
Quentin Khan committed
348
349
350
351
352
353
            /**
             * \brief Build everything and run algorithm
             *
             * \param params The program parameters
             */
            static void run(FPerfTestParams& params) {
Quentin Khan's avatar
Quentin Khan committed
354
355
                FTimerMap& timers = params.timers;

Quentin Khan's avatar
Quentin Khan committed
356
                // The factories return unique_ptr specializations
Quentin Khan's avatar
Quentin Khan committed
357
358
359
360
361
                tree_factory tree_f;
                loader_factory loader_f;
                kernel_factory kernel_f;
                algo_factory algo_f;

Quentin Khan's avatar
Quentin Khan committed
362
                // Build tree and load it
Quentin Khan's avatar
Quentin Khan committed
363
364
                auto loader = loader_f.build(params);
                auto tree   = tree_f.build(params, *loader);
Quentin Khan's avatar
Quentin Khan committed
365
                timers["load"].tic();
Quentin Khan's avatar
Quentin Khan committed
366
                loader->load(*tree);
Quentin Khan's avatar
Quentin Khan committed
367
                timers["load"].tac();
Quentin Khan's avatar
Quentin Khan committed
368

Quentin Khan's avatar
Quentin Khan committed
369
                // Build kernel and algorithm
Quentin Khan's avatar
Quentin Khan committed
370
371
                auto kernel = kernel_f.build(params, *tree);
                auto algo   = algo_f.build(params, *tree, *kernel);
Quentin Khan's avatar
Quentin Khan committed
372
                std::cout << algo->name() << ": ";
Quentin Khan's avatar
Quentin Khan committed
373
                std::cout << algo->description() << '\n';
Quentin Khan's avatar
Quentin Khan committed
374
375

                // Run the algorithm
Quentin Khan's avatar
Quentin Khan committed
376
                timers["algo"].tic();
Quentin Khan's avatar
Quentin Khan committed
377
                algo->execute();
Quentin Khan's avatar
Quentin Khan committed
378
                timers["algo"].tac();
Quentin Khan's avatar
Quentin Khan committed
379

Quentin Khan's avatar
Quentin Khan committed
380
381
382
383
384
385
386
387
                // Gather general data
                params.json["config"]["algo"]["name"] = params.algo;
                params.json["config"]["algo"]["threads"] = params.nbThreads;
                params.json["config"]["particle files"] =
                    {params.filename.substr(params.filename.find_last_of('/')+1)};
                params.json["config"]["kernel"]["name"] = params.kernel;

                // Gather timing data
Quentin Khan's avatar
Quentin Khan committed
388
                for(auto t : algo->Timers) {
389
                    params.json["time"]["operator"][t.first] = t.second.cumulated();
Quentin Khan's avatar
Quentin Khan committed
390
391
                }
                for(auto t : params.timers) {
392
                    params.json["time"][t.first] = t.second.cumulated();
Quentin Khan's avatar
Quentin Khan committed
393
                }
Quentin Khan's avatar
Quentin Khan committed
394

Quentin Khan's avatar
Quentin Khan committed
395
396
397
                // Gather descriptor specific data
                setup_step::call_info<tree_desc>(params, *tree);
                setup_step::call_info<kernel_desc>(params, *kernel);
Quentin Khan's avatar
Quentin Khan committed
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
            }
        };

        /**
         * \brief Last step, abort run if descriptors are not compatible
         *
         * \tparam kernel_desc Kernel descriptor
         * \tparam algo_desc   Algorithm descriptor
         */
        template<typename kernel_desc, typename algo_desc>
        struct setup_step<kernel_desc, algo_desc, false> {
            static void run(FPerfTestParams&) {
                std::cerr << "Kernel '" << kernel_desc::name << "' is incompatible"
                          << " with algorithm '" << algo_desc::name << "'" << '\n';
                using concept = sfinae::compatible<kernel_desc, algo_desc>;
                std::cerr << "tree fact: " << concept::has_tree_factory_v << '\n';
                std::cerr << "loader fact: " << concept::has_loader_factory_v << '\n';
                std::cerr << "kernel fact: " << concept::has_kernel_factory_v << '\n';
                std::cerr << "algo fact: " << concept::has_algo_factory_v << '\n';
                std::cerr << "can build algo: " << concept::can_build_algo_v << '\n';
            }
        };

        /**
         * \brief Kernel lookup
         *
         * Recursive template class to find whether the kernel name given in
         * `params` matches a kernel descriptor. If so, calls the final step.
         *
         * \tparam FReal Floating point type
         * \tparam I Current kernel index
         */
        template<typename FReal, std::size_t I = 0,
                 bool = sfinae::is_enabled<kernel_descriptor<FReal, I> >::value,
432
                 bool = (I < max_descriptor_count)
Quentin Khan's avatar
Quentin Khan committed
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
                 >
        struct kernel_lookup {
            /// `I`th kernel descriptor type
            using kernel_desc = kernel_descriptor<FReal, I>;
            /**
             * \brief Kernel lookup method
             *
             * Checks whether `I`th kernel descriptor name matches the given
             * kernel. If so, calls the final step, otherwise checks next kernel
             * desriptor.
             *
             * \tparam other_descs Descriptors found in previous steps
             *
             * \param params Program parameters
             */
            template<typename... other_descs>
            static void check(FPerfTestParams& params, type_list<other_descs...>) {
                if(params.kernel ==  kernel_desc::name) {
                    setup_step<kernel_desc, other_descs...>::run(params);
                } else {
                    kernel_lookup<FReal, I+1>::check(params, type_list<other_descs...>{});
                }
            }
        };

Quentin Khan's avatar
Quentin Khan committed
458
459
460
461
462
463
464
465
466
467
468
        /**
         * \brief Kernel lookup disabled case
         *
         * Recursive template class to find whether the kernel name given in
         * `params` matches a kernel descriptor. If so, calls the final step.
         *
         * This specialization skips *disabled* kernel descriptors.
         *
         * \tparam FReal Floating point type
         * \tparam I Current kernel index
         */
Quentin Khan's avatar
Quentin Khan committed
469
470
471
472
473
474
475
476
477
478
479
480
481
482
        template<typename FReal, std::size_t I>
        struct kernel_lookup<FReal, I, false, true> {
            template<typename T>
            static void check(const FPerfTestParams& params, T) {
                kernel_lookup<FReal, I+1>::check(params, T{});
            }
        };

        /**
         * \brief Kernel lookup recursion end
         *
         * Recursive template class to find whether the kernel name given in
         * `params` matches a kernel descriptor. If so, calls the final step.
         *
Quentin Khan's avatar
Quentin Khan committed
483
484
485
         * This specialization ends the kernel lookup, no fitting kernel was
         * found.
         *
Quentin Khan's avatar
Quentin Khan committed
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
         * \tparam FReal Floating point type
         * \tparam I Current kernel index
         */
        template<typename FReal, std::size_t I>
        struct kernel_lookup<FReal, I, false, false> {
            template<typename T>
            static void check(const FPerfTestParams& params, T) {
                std::cerr << "Kernel " << params.kernel << " does not exist.\n";
            }
        };


        /**
         * \brief Algorithm lookup
         *
         * Recursive template class to find whether the algorithm name given in
         * `params` matches an algorithm descriptor. If so, calls the final step.
         *
         * \tparam FReal Floating point type
         * \tparam I Current kernel index
         */
        template<typename FReal, std::size_t I = 0,
                 bool = sfinae::is_enabled<algo_descriptor<FReal, I> >::value,
509
                 bool = (I < max_descriptor_count) >
Quentin Khan's avatar
Quentin Khan committed
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
        struct algorithm_lookup {
            /// `I`th algorithm descriptor
            using algo_desc = algo_descriptor<FReal, I>;
            /**
             * \brief Algorithm lookup method
             *
             * Checks whether `I`th algorithm descriptor name matches the given
             * algorithm. If so, calls the final step, otherwise checks next
             * algorithm desriptor.
             *
             * \tparam other_descs Descriptors found in previous steps
             *
             * \param params Program parameters
             */
            template<typename... other_descs>
            static void check(FPerfTestParams& params, type_list<other_descs...>) {
                if(params.algo ==  algo_desc::name) {
                    kernel_lookup<FReal>
                        ::check(params, type_list<algo_desc, other_descs...>{});
                } else {
                    algorithm_lookup<FReal, I+1>::check(params, type_list<other_descs...>{});
                }
            }
        };

        template<typename FReal, std::size_t I>
        struct algorithm_lookup<FReal, I, false, true> {
            template<typename T>
            static void check(const FPerfTestParams& params, T) {
                algorithm_lookup<FReal, I+1>::check(params, T{});
            }
        };


        /** \brief Algorithm lookup recursion end */
        template<typename FReal, std::size_t I>
        struct algorithm_lookup<FReal, I, false, false> {
            template<typename T>
            static void check(const FPerfTestParams& params, T) {
                std::cerr << "Algorithm " << params.algo << " does not exist.\n";
            }
        };
    }


    /**
     * \brief Look for algorithm and kernel asked in parameter and run
     *
     * \param params Program paramters
     */
    template<typename FReal>
    void run(FPerfTestParams& params) {
        details::algorithm_lookup<FReal>::check(params, details::type_list<>{});
    }


    template<typename FReal, template<typename, std::size_t> class descriptor, std::size_t... Is>
    void descriptor_list(std::index_sequence<Is...>) {
        std::vector<std::string> names;
        auto l = {(names.push_back(descriptor<FReal, Is>::name), 0)..., 0};
        (void)l;

        std::ostream_iterator<std::string> it(std::cout, ", ");
        names.erase(std::remove(names.begin(), names.end(), ""), names.end());
        std::copy(names.begin(), names.end()-1, it);
        std::cout << names.back() << '\n';

    }

    template<typename FReal>
    void kernel_list() {
        descriptor_list<FReal, kernel_descriptor>(
            std::make_index_sequence<details::max_descriptor_count>());
    }

    template<typename FReal>
    void algo_list() {
        descriptor_list<FReal, algo_descriptor>(
            std::make_index_sequence<details::max_descriptor_count>());
    }


}



#endif /* TRAITSMANAGEMENT_HPP */