Kokkos Core Kernels Package Version of the Day
Loading...
Searching...
No Matches
Kokkos_Tuners.hpp
1//@HEADER
2// ************************************************************************
3//
4// Kokkos v. 4.0
5// Copyright (2022) National Technology & Engineering
6// Solutions of Sandia, LLC (NTESS).
7//
8// Under the terms of Contract DE-NA0003525 with NTESS,
9// the U.S. Government retains certain rights in this software.
10//
11// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12// See https://kokkos.org/LICENSE for license information.
13// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14//
15//@HEADER
16
17#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
18#include <Kokkos_Macros.hpp>
19static_assert(false,
20 "Including non-public Kokkos header files is not allowed.");
21#endif
22#ifndef KOKKOS_KOKKOS_TUNERS_HPP
23#define KOKKOS_KOKKOS_TUNERS_HPP
24
25#include <Kokkos_Macros.hpp>
26#include <Kokkos_Core_fwd.hpp>
27#include <Kokkos_ExecPolicy.hpp>
28#include <KokkosExp_MDRangePolicy.hpp>
29#include <impl/Kokkos_Profiling_Interface.hpp>
30
31#include <array>
32#include <utility>
33#include <tuple>
34#include <string>
35#include <vector>
36#include <map>
37#include <cassert>
38
39namespace Kokkos {
40namespace Tools {
41
42namespace Experimental {
43
44// forward declarations
45SetOrRange make_candidate_set(size_t size, int64_t* data);
46bool have_tuning_tool();
47size_t declare_output_type(const std::string&,
48 Kokkos::Tools::Experimental::VariableInfo);
49void request_output_values(size_t, size_t,
50 Kokkos::Tools::Experimental::VariableValue*);
51VariableValue make_variable_value(size_t, int64_t);
52VariableValue make_variable_value(size_t, double);
53SetOrRange make_candidate_range(double lower, double upper, double step,
54 bool openLower, bool openUpper);
55SetOrRange make_candidate_range(int64_t lower, int64_t upper, int64_t step,
56 bool openLower, bool openUpper);
57size_t get_new_context_id();
58void begin_context(size_t context_id);
59void end_context(size_t context_id);
60namespace Impl {
61
67template <typename ValueType, typename ContainedType>
69
70template <typename ValueType, typename ContainedType>
72 std::vector<ValueType> root_values;
73 std::vector<ContainedType> sub_values;
74 void add_root_value(const ValueType& in) noexcept {
75 root_values.push_back(in);
76 }
77 void add_sub_container(const ContainedType& in) { sub_values.push_back(in); }
78 const ValueType& get_root_value(const size_t index) const {
79 return root_values[index];
80 }
81 const ContainedType& get_sub_value(const size_t index) const {
82 return sub_values[index];
83 }
84};
85
86template <typename ValueType>
87struct ValueHierarchyNode<ValueType, void> {
88 std::vector<ValueType> root_values;
89 explicit ValueHierarchyNode(std::vector<ValueType> rv)
90 : root_values(std::move(rv)) {}
91 void add_root_value(const ValueType& in) noexcept {
92 root_values.push_back(in);
93 }
94 const ValueType& get_root_value(const size_t index) const {
95 return root_values[index];
96 }
97};
98
103
104template <class NestedMap>
106
107// Vectors are our lowest-level, no nested values
108template <class T>
109struct MapTypeConverter<std::vector<T>> {
110 using type = ValueHierarchyNode<T, void>;
111};
112
113// Maps contain both the "root" types and sub-vectors
114template <class K, class V>
115struct MapTypeConverter<std::map<K, V>> {
117};
118
123
124template <class NestedMap>
126
127// Vectors are our lowest-level, no nested values. Just fill in the fundamental
128// values
129template <class T>
130struct ValueHierarchyConstructor<std::vector<T>> {
131 using return_type = typename MapTypeConverter<std::vector<T>>::type;
132 static return_type build(const std::vector<T>& in) { return return_type{in}; }
133};
134
135// For maps, we need to fill in the fundamental values, and construct child
136// nodes
137template <class K, class V>
138struct ValueHierarchyConstructor<std::map<K, V>> {
139 using return_type = typename MapTypeConverter<std::map<K, V>>::type;
140 static return_type build(const std::map<K, V>& in) {
141 return_type node_to_build;
142 for (auto& entry : in) {
143 node_to_build.add_root_value(entry.first);
144 node_to_build.add_sub_container(
145 ValueHierarchyConstructor<V>::build(entry.second));
146 }
147 return node_to_build;
148 }
149};
150
159template <class InspectForDepth>
161
162// The dimensionality of a vector is 1
163template <class T>
164struct get_space_dimensionality<std::vector<T>> {
165 static constexpr int value = 1;
166};
167
168// The dimensionality of a map is 1 (the map) plus the dimensionality
169// of the map's value type
170template <class K, class V>
171struct get_space_dimensionality<std::map<K, V>> {
172 static constexpr int value = 1 + get_space_dimensionality<V>::value;
173};
174
175template <class T, int N>
176struct n_dimensional_sparse_structure;
177
178template <class T>
179struct n_dimensional_sparse_structure<T, 1> {
180 using type = std::vector<T>;
181};
182
183template <class T, int N>
184struct n_dimensional_sparse_structure {
185 using type =
186 std::map<T, typename n_dimensional_sparse_structure<T, N - 1>::type>;
187};
188
194
195// First, a helper to get the value in one dimension
196template <class Container>
198
199// At any given level, just return your value at that level
200template <class RootType, class Subtype>
201struct DimensionValueExtractor<ValueHierarchyNode<RootType, Subtype>> {
202 static RootType get(const ValueHierarchyNode<RootType, Subtype>& dimension,
203 double fraction_to_traverse) {
204 size_t index = dimension.root_values.size() * fraction_to_traverse;
205 return dimension.get_root_value(index);
206 }
207};
208
213
214// At the bottom level, we have one double and a base-level ValueHierarchyNode
215
216template <class HierarchyNode, class... InterpolationIndices>
218
219template <class ValueType>
220struct GetMultidimensionalPoint<ValueHierarchyNode<ValueType, void>, double> {
221 using node_type = ValueHierarchyNode<ValueType, void>;
222 using return_type = std::tuple<ValueType>;
223 static return_type build(const node_type& in, double index) {
224 return std::make_tuple(DimensionValueExtractor<node_type>::get(in, index));
225 }
226};
227
228// At levels above the bottom, we tuple_cat the result of our child on the end
229// of our own tuple
230template <class ValueType, class Subtype, class... Indices>
231struct GetMultidimensionalPoint<ValueHierarchyNode<ValueType, Subtype>, double,
232 Indices...> {
233 using node_type = ValueHierarchyNode<ValueType, Subtype>;
234 using sub_tuple =
235 typename GetMultidimensionalPoint<Subtype, Indices...>::return_type;
236 using return_type = decltype(std::tuple_cat(
237 std::declval<std::tuple<ValueType>>(), std::declval<sub_tuple>()));
238 static return_type build(const node_type& in, double fraction_to_traverse,
239 Indices... indices) {
240 size_t index = in.sub_values.size() * fraction_to_traverse;
241 auto dimension_value = std::make_tuple(
242 DimensionValueExtractor<node_type>::get(in, fraction_to_traverse));
243 return std::tuple_cat(dimension_value,
244 GetMultidimensionalPoint<Subtype, Indices...>::build(
245 in.get_sub_value(index), indices...));
246 }
247};
248
249template <typename PointType, class ArrayType, size_t... Is>
250auto get_point_helper(const PointType& in, const ArrayType& indices,
251 std::index_sequence<Is...>) {
252 using helper = GetMultidimensionalPoint<
253 PointType,
254 decltype(std::get<Is>(std::declval<ArrayType>()).value.double_value)...>;
255 return helper::build(in, std::get<Is>(indices).value.double_value...);
256}
257
258template <typename PointType, typename ArrayType>
259struct GetPoint;
260
261template <typename PointType, size_t ArraySize>
262struct GetPoint<
263 PointType,
264 std::array<Kokkos::Tools::Experimental::VariableValue, ArraySize>> {
265 using index_set_type =
266 std::array<Kokkos::Tools::Experimental::VariableValue, ArraySize>;
267 static auto build(const PointType& in, const index_set_type& indices) {
268 return get_point_helper(in, indices, std::make_index_sequence<ArraySize>{});
269 }
270};
271
272template <typename PointType, typename ArrayType>
273auto get_point(const PointType& point, const ArrayType& indices) {
274 return GetPoint<PointType, ArrayType>::build(point, indices);
275}
276
277} // namespace Impl
278
279template <template <class...> class Container, size_t MaxDimensionSize = 100,
280 class... TemplateArguments>
281class MultidimensionalSparseTuningProblem {
282 public:
283 using ProblemSpaceInput = Container<TemplateArguments...>;
284 static constexpr int space_dimensionality =
285 Impl::get_space_dimensionality<ProblemSpaceInput>::value;
286 static constexpr size_t max_space_dimension_size = MaxDimensionSize;
287 static constexpr double tuning_min = 0.0;
288 static constexpr double tuning_max = 0.999;
289
290 // Not declared as static constexpr to work around the following compiler bug
291 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96862
292 // where a floating-point expression cannot be constexpr under -frounding-math
293 double tuning_step = tuning_max / max_space_dimension_size;
294
295 using StoredProblemSpace =
296 typename Impl::MapTypeConverter<ProblemSpaceInput>::type;
297 using HierarchyConstructor =
298 typename Impl::ValueHierarchyConstructor<Container<TemplateArguments...>>;
299
300 using ValueArray = std::array<Kokkos::Tools::Experimental::VariableValue,
301 space_dimensionality>;
302 template <class Key, class Value>
303 using extended_map = std::map<Key, Value>;
304 template <typename Key>
305 using extended_problem =
306 MultidimensionalSparseTuningProblem<extended_map, MaxDimensionSize, Key,
307 ProblemSpaceInput>;
308 template <typename Key, typename Value>
309 using ExtendedProblemSpace =
310 typename Impl::MapTypeConverter<extended_map<Key, Value>>::type;
311
312 template <typename Key>
313 auto extend(const std::string& axis_name,
314 const std::vector<Key>& new_tuning_axis) const
315 -> extended_problem<Key> {
316 ExtendedProblemSpace<Key, ProblemSpaceInput> extended_space;
317 for (auto& key : new_tuning_axis) {
318 extended_space.add_root_value(key);
319 extended_space.add_sub_container(m_space);
320 }
321 std::vector<std::string> extended_names;
322 extended_names.reserve(m_variable_names.size() + 1);
323 extended_names.push_back(axis_name);
324 extended_names.insert(extended_names.end(), m_variable_names.begin(),
325 m_variable_names.end());
326 return extended_problem<Key>(extended_space, extended_names);
327 }
328
329 private:
330 StoredProblemSpace m_space;
331 std::array<size_t, space_dimensionality> variable_ids;
332 std::vector<std::string> m_variable_names;
333 size_t context;
334
335 public:
336 MultidimensionalSparseTuningProblem() = default;
337
338 MultidimensionalSparseTuningProblem(StoredProblemSpace space,
339 const std::vector<std::string>& names)
340 : m_space(std::move(space)), m_variable_names(names) {
341 assert(names.size() == space_dimensionality);
342 for (unsigned long x = 0; x < names.size(); ++x) {
343 VariableInfo info;
344 info.type = Kokkos::Tools::Experimental::ValueType::kokkos_value_double;
345 info.category = Kokkos::Tools::Experimental::StatisticalCategory::
346 kokkos_value_interval;
347 info.valueQuantity =
348 Kokkos::Tools::Experimental::CandidateValueType::kokkos_value_range;
349 info.candidates = Kokkos::Tools::Experimental::make_candidate_range(
350 tuning_min, tuning_max, tuning_step, true, true);
351 variable_ids[x] = declare_output_type(names[x], info);
352 }
353 }
354
355 MultidimensionalSparseTuningProblem(ProblemSpaceInput space,
356 const std::vector<std::string>& names)
357 : MultidimensionalSparseTuningProblem(HierarchyConstructor::build(space),
358 names) {}
359
360 template <typename... Coordinates>
361 auto get_point(Coordinates... coordinates) {
362 using ArrayType = std::array<Kokkos::Tools::Experimental::VariableValue,
363 sizeof...(coordinates)>;
364 return Impl::get_point(
365 m_space, ArrayType({Kokkos::Tools::Experimental::make_variable_value(
366 0, static_cast<double>(coordinates))...}));
367 }
368
369 auto begin() {
370 context = Kokkos::Tools::Experimental::get_new_context_id();
371 ValueArray values;
372 for (int x = 0; x < space_dimensionality; ++x) {
373 values[x] = Kokkos::Tools::Experimental::make_variable_value(
374 variable_ids[x], 0.0);
375 }
376 begin_context(context);
377 request_output_values(context, space_dimensionality, values.data());
378 return Impl::get_point(m_space, values);
379 }
380
381 auto end() { end_context(context); }
382};
383
384template <typename Tuner>
385struct ExtendableTunerMixin {
386 template <typename Key>
387 auto combine(const std::string& axis_name,
388 const std::vector<Key>& new_axis) const {
389 const auto& sub_tuner = static_cast<const Tuner*>(this)->get_tuner();
390 return sub_tuner.extend(axis_name, new_axis);
391 }
392
393 template <typename... Coordinates>
394 auto get_point(Coordinates... coordinates) {
395 const auto& sub_tuner = static_cast<const Tuner*>(this)->get_tuner();
396 return sub_tuner.get_point(coordinates...);
397 }
398};
399
400template <size_t MaxDimensionSize = 100, template <class...> class Container,
401 class... TemplateArguments>
402auto make_multidimensional_sparse_tuning_problem(
403 const Container<TemplateArguments...>& in, std::vector<std::string> names) {
404 return MultidimensionalSparseTuningProblem<Container, MaxDimensionSize,
405 TemplateArguments...>(in, names);
406}
407
408class TeamSizeTuner : public ExtendableTunerMixin<TeamSizeTuner> {
409 private:
410 using SpaceDescription = std::map<int64_t, std::vector<int64_t>>;
411 using TunerType = decltype(make_multidimensional_sparse_tuning_problem<20>(
412 std::declval<SpaceDescription>(),
413 std::declval<std::vector<std::string>>()));
414 TunerType tuner;
415
416 public:
417 TeamSizeTuner() = default;
418 TeamSizeTuner& operator=(const TeamSizeTuner& other) = default;
419 TeamSizeTuner(const TeamSizeTuner& other) = default;
420 TeamSizeTuner& operator=(TeamSizeTuner&& other) = default;
421 TeamSizeTuner(TeamSizeTuner&& other) = default;
422 template <typename ViableConfigurationCalculator, typename Functor,
423 typename TagType, typename... Properties>
424 TeamSizeTuner(const std::string& name,
425 const Kokkos::TeamPolicy<Properties...>& policy_in,
426 const Functor& functor, const TagType& tag,
427 ViableConfigurationCalculator calc) {
428 using PolicyType = Kokkos::TeamPolicy<Properties...>;
429 PolicyType policy(policy_in);
430 auto initial_vector_length = policy.impl_vector_length();
431 if (initial_vector_length < 1) {
432 policy.impl_set_vector_length(1);
433 }
459 SpaceDescription space_description;
460
461 auto max_vector_length = PolicyType::vector_length_max();
462 std::vector<int64_t> allowed_vector_lengths;
463
464 if (policy.impl_auto_vector_length()) { // case 1 or 2
465 for (int vector_length = max_vector_length; vector_length >= 1;
466 vector_length /= 2) {
467 policy.impl_set_vector_length(vector_length);
480 auto max_team_size = calc.get_max_team_size(policy, functor, tag);
481 if ((policy.impl_auto_team_size()) ||
482 (policy.team_size() <= max_team_size)) {
483 allowed_vector_lengths.push_back(vector_length);
484 }
485 }
486 } else { // case 3, there's only one vector length to care about
487 allowed_vector_lengths.push_back(policy.impl_vector_length());
488 }
489
490 for (const auto vector_length : allowed_vector_lengths) {
491 std::vector<int64_t> allowed_team_sizes;
492 policy.impl_set_vector_length(vector_length);
493 auto max_team_size = calc.get_max_team_size(policy, functor, tag);
494 if (policy.impl_auto_team_size()) { // case 1 or 3, try all legal team
495 // sizes
496 for (int team_size = max_team_size; team_size >= 1; team_size /= 2) {
497 allowed_team_sizes.push_back(team_size);
498 }
499 } else { // case 2, just try the provided team size
500 allowed_team_sizes.push_back(policy.team_size());
501 }
502 space_description[vector_length] = allowed_team_sizes;
503 }
504 tuner = make_multidimensional_sparse_tuning_problem<20>(
505 space_description, {std::string(name + "_vector_length"),
506 std::string(name + "_team_size")});
507 policy.impl_set_vector_length(initial_vector_length);
508 }
509
510 template <typename... Properties>
511 auto tune(const Kokkos::TeamPolicy<Properties...>& policy_in) {
512 Kokkos::TeamPolicy<Properties...> policy(policy_in);
513 if (Kokkos::Tools::Experimental::have_tuning_tool()) {
514 auto configuration = tuner.begin();
515 auto team_size = std::get<1>(configuration);
516 auto vector_length = std::get<0>(configuration);
517 if (vector_length > 0) {
518 policy.impl_set_team_size(team_size);
519 policy.impl_set_vector_length(vector_length);
520 }
521 }
522 return policy;
523 }
524 void end() {
525 if (Kokkos::Tools::Experimental::have_tuning_tool()) {
526 tuner.end();
527 }
528 }
529
530 TunerType get_tuner() const { return tuner; }
531};
532namespace Impl {
533template <class T>
534struct tuning_type_for;
535
536template <>
537struct tuning_type_for<double> {
538 static constexpr Kokkos::Tools::Experimental::ValueType value =
539 Kokkos::Tools::Experimental::ValueType::kokkos_value_double;
540 static double get(
541 const Kokkos::Tools::Experimental::VariableValue& value_struct) {
542 return value_struct.value.double_value;
543 }
544};
545template <>
546struct tuning_type_for<int64_t> {
547 static constexpr Kokkos::Tools::Experimental::ValueType value =
548 Kokkos::Tools::Experimental::ValueType::kokkos_value_int64;
549 static int64_t get(
550 const Kokkos::Tools::Experimental::VariableValue& value_struct) {
551 return value_struct.value.int_value;
552 }
553};
554} // namespace Impl
555template <class Bound>
556class SingleDimensionalRangeTuner {
557 size_t id;
558 size_t context;
559 using tuning_util = Impl::tuning_type_for<Bound>;
560
561 Bound default_value;
562
563 public:
564 SingleDimensionalRangeTuner() = default;
565 SingleDimensionalRangeTuner(
566 const std::string& name,
567 Kokkos::Tools::Experimental::StatisticalCategory category,
568 Bound default_val, Bound lower, Bound upper, Bound step = (Bound)0) {
569 default_value = default_val;
570 Kokkos::Tools::Experimental::VariableInfo info;
571 info.category = category;
572 info.candidates = make_candidate_range(
573 static_cast<Bound>(lower), static_cast<Bound>(upper),
574 static_cast<Bound>(step), false, false);
575 info.valueQuantity =
576 Kokkos::Tools::Experimental::CandidateValueType::kokkos_value_range;
577 info.type = tuning_util::value;
578 id = Kokkos::Tools::Experimental::declare_output_type(name, info);
579 }
580
581 Bound begin() {
582 context = Kokkos::Tools::Experimental::get_new_context_id();
583 Kokkos::Tools::Experimental::begin_context(context);
584 auto tuned_value =
585 Kokkos::Tools::Experimental::make_variable_value(id, default_value);
586 Kokkos::Tools::Experimental::request_output_values(context, 1,
587 &tuned_value);
588 return tuning_util::get(tuned_value);
589 }
590
591 void end() { Kokkos::Tools::Experimental::end_context(context); }
592
593 template <typename Functor>
594 void with_tuned_value(Functor& func) {
595 func(begin());
596 end();
597 }
598};
599
600class RangePolicyOccupancyTuner {
601 private:
602 using TunerType = SingleDimensionalRangeTuner<int64_t>;
603 TunerType tuner;
604
605 public:
606 RangePolicyOccupancyTuner() = default;
607 template <typename ViableConfigurationCalculator, typename Functor,
608 typename TagType, typename... Properties>
609 RangePolicyOccupancyTuner(const std::string& name,
610 const Kokkos::RangePolicy<Properties...>&,
611 const Functor&, const TagType&,
612 ViableConfigurationCalculator)
613 : tuner(TunerType(name,
614 Kokkos::Tools::Experimental::StatisticalCategory::
615 kokkos_value_ratio,
616 100, 5, 100, 5)) {}
617
618 template <typename... Properties>
619 auto tune(const Kokkos::RangePolicy<Properties...>& policy_in) {
620 Kokkos::RangePolicy<Properties...> policy(policy_in);
621 if (Kokkos::Tools::Experimental::have_tuning_tool()) {
622 auto occupancy = tuner.begin();
623 policy.impl_set_desired_occupancy(
624 Kokkos::Experimental::DesiredOccupancy{static_cast<int>(occupancy)});
625 }
626 return policy;
627 }
628 void end() {
629 if (Kokkos::Tools::Experimental::have_tuning_tool()) {
630 tuner.end();
631 }
632 }
633
634 TunerType get_tuner() const { return tuner; }
635};
636
637namespace Impl {
638
639template <typename T>
640void fill_tile(std::vector<T>& cont, int tile_size) {
641 for (int x = 1; x < tile_size; x *= 2) {
642 cont.push_back(x);
643 }
644}
645template <typename T, typename Mapped>
646void fill_tile(std::map<T, Mapped>& cont, int tile_size) {
647 for (int x = 1; x < tile_size; x *= 2) {
648 fill_tile(cont[x], tile_size / x);
649 }
650}
651} // namespace Impl
652
653template <int MDRangeRank>
654struct MDRangeTuner : public ExtendableTunerMixin<MDRangeTuner<MDRangeRank>> {
655 private:
656 static constexpr int rank = MDRangeRank;
657 static constexpr int max_slices = 15;
658 using SpaceDescription =
659 typename Impl::n_dimensional_sparse_structure<int, rank>::type;
660 using TunerType =
661 decltype(make_multidimensional_sparse_tuning_problem<max_slices>(
662 std::declval<SpaceDescription>(),
663 std::declval<std::vector<std::string>>()));
664 TunerType tuner;
665
666 public:
667 MDRangeTuner() = default;
668 template <typename Functor, typename TagType, typename Calculator,
669 typename... Properties>
670 MDRangeTuner(const std::string& name,
671 const Kokkos::MDRangePolicy<Properties...>& policy,
672 const Functor& functor, const TagType& tag, Calculator calc) {
673 SpaceDescription desc;
674 int max_tile_size =
675 calc.get_mdrange_max_tile_size_product(policy, functor, tag);
676 Impl::fill_tile(desc, max_tile_size);
677 std::vector<std::string> feature_names;
678 for (int x = 0; x < rank; ++x) {
679 feature_names.push_back(name + "_tile_size_" + std::to_string(x));
680 }
681 tuner = make_multidimensional_sparse_tuning_problem<max_slices>(
682 desc, feature_names);
683 }
684 template <typename Policy, typename Tuple, size_t... Indices>
685 void set_policy_tile(Policy& policy, const Tuple& tuple,
686 const std::index_sequence<Indices...>&) {
687 policy.impl_change_tile_size({std::get<Indices>(tuple)...});
688 }
689 template <typename... Properties>
690 auto tune(const Kokkos::MDRangePolicy<Properties...>& policy_in) {
691 Kokkos::MDRangePolicy<Properties...> policy(policy_in);
692 if (Kokkos::Tools::Experimental::have_tuning_tool()) {
693 auto configuration = tuner.begin();
694 set_policy_tile(policy, configuration, std::make_index_sequence<rank>{});
695 }
696 return policy;
697 }
698 void end() {
699 if (Kokkos::Tools::Experimental::have_tuning_tool()) {
700 tuner.end();
701 }
702 }
703
704 TunerType get_tuner() const { return tuner; }
705};
706
707template <class Choice>
708struct CategoricalTuner {
709 using choice_list = std::vector<Choice>;
710 choice_list choices;
711 size_t context;
712 size_t tuning_variable_id;
713 CategoricalTuner(std::string name, choice_list m_choices)
714 : choices(m_choices) {
715 std::vector<int64_t> indices;
716 for (typename decltype(choices)::size_type x = 0; x < choices.size(); ++x) {
717 indices.push_back(x);
718 }
719 VariableInfo info;
720 info.category = StatisticalCategory::kokkos_value_categorical;
721 info.valueQuantity = CandidateValueType::kokkos_value_set;
722 info.type = ValueType::kokkos_value_int64;
723 info.candidates = make_candidate_set(indices.size(), indices.data());
724 tuning_variable_id = declare_output_type(name, info);
725 }
726 const Choice& begin() {
727 context = get_new_context_id();
728 begin_context(context);
729 VariableValue value = make_variable_value(tuning_variable_id, int64_t(0));
730 request_output_values(context, 1, &value);
731 return choices[value.value.int_value];
732 }
733 void end() { end_context(context); }
734};
735
736template <typename Choice>
737auto make_categorical_tuner(std::string name, std::vector<Choice> choices)
738 -> CategoricalTuner<Choice> {
739 return CategoricalTuner<Choice>(name, choices);
740}
741
742} // namespace Experimental
743} // namespace Tools
744} // namespace Kokkos
745
746#endif