2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #ifndef __NNFW_CKER_HELPER_RANDOM_OP_CPU_H__
19 #define __NNFW_CKER_HELPER_RANDOM_OP_CPU_H__
21 #define EIGEN_USE_THREADS
27 #include "cker/Types.h"
28 #include "cker/Shape.h"
29 #include "cker/Utils.h"
31 #include "cker/eigen/EigenSupport.h"
33 #include "cker/operation/Helper/PhiloxRandom.h"
34 #include "cker/operation/Helper/RandomOp.h"
35 #include "cker/operation/Helper/RandomDistributions.h"
37 #if EIGEN_COMP_GNUC && __cplusplus > 199711L
38 #define DISABLE_FLOAT_EQUALITY_WARNING \
39 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
40 #define ENABLE_FLOAT_EQUALITY_WARNING _Pragma("GCC diagnostic pop")
42 #define DISABLE_FLOAT_EQUALITY_WARNING
43 #define ENABLE_FLOAT_EQUALITY_WARNING
51 typedef Eigen::ThreadPoolDevice CPUDevice;
55 using random::PhiloxRandom;
56 using random::SingleSampleAdapter;
58 // The default implementation of the functor, which should never be invoked
59 // But we still need to provide implementation for now for the linker to work,
60 // since we do not support all the distributions yet.
61 template <typename Device, class Distribution> struct FillPhiloxRandom
63 typedef typename Distribution::ResultElementType T;
67 // A class to fill a specified range of random groups
68 template <class Distribution, bool VariableSamplesPerOutput> struct FillPhiloxRandomTask;
70 // Specialization for distribution that takes a fixed number of samples for
72 template <class Distribution> struct FillPhiloxRandomTask<Distribution, false>
74 typedef typename Distribution::ResultElementType T;
75 static void Run(random::PhiloxRandom gen, T *data, int64_t size, Distribution dist)
77 const int kGroupSize = Distribution::kResultElementCount;
81 // First fill all the full-size groups
82 int64_t limit_group_full = size / kGroupSize;
83 for (int64_t index = 0; index < limit_group_full; ++index)
85 auto samples = dist(&gen);
86 std::copy(&samples[0], &samples[0] + kGroupSize, data + offset);
90 int64_t remaining_size = size - limit_group_full * kGroupSize;
92 // If there are any remaining elements that need to be filled, process them
93 if (remaining_size > 0)
95 auto samples = dist(&gen);
96 std::copy(&samples[0], &samples[0] + remaining_size, data + offset);
101 // Specialization for distribution that takes a variable number of samples for
102 // each output. This will be slower due to the generality.
103 template <class Distribution> struct FillPhiloxRandomTask<Distribution, true>
105 typedef typename Distribution::ResultElementType T;
106 static constexpr int64_t kReservedSamplesPerOutput = 256;
108 static void Run(random::PhiloxRandom base_gen, T *data, int64_t size, Distribution dist)
110 const int kGroupSize = Distribution::kResultElementCount;
111 static const int kGeneratorSkipPerOutputGroup =
112 kGroupSize * kReservedSamplesPerOutput / PhiloxRandom::kResultElementCount;
116 // First fill all the full-size groups
117 int64_t limit_group_full = size / kGroupSize;
119 for (group_index = 0; group_index < limit_group_full; ++group_index)
121 // Reset the generator to the beginning of the output group region
122 // This is necessary if we want the results to be independent of order
124 PhiloxRandom gen = base_gen;
125 gen.Skip(group_index * kGeneratorSkipPerOutputGroup);
126 SingleSampleAdapter<PhiloxRandom> single_samples(&gen);
128 auto samples = dist(&single_samples);
129 std::copy(&samples[0], &samples[0] + kGroupSize, data + offset);
130 offset += kGroupSize;
133 int64_t remaining_size = size - limit_group_full * kGroupSize;
134 // If there are any remaining elements that need to be filled, process them
135 if (remaining_size > 0)
137 PhiloxRandom gen = base_gen;
138 gen.Skip(group_index * kGeneratorSkipPerOutputGroup);
139 SingleSampleAdapter<PhiloxRandom> single_samples(&gen);
141 auto samples = dist(&single_samples);
142 std::copy(&samples[0], &samples[0] + remaining_size, data + offset);
147 // Partial specialization for CPU to fill the entire region with randoms
148 // It splits the work into several tasks and run them in parallel
149 template <class Distribution>
150 void FillPhiloxRandom<CPUDevice, Distribution>::
151 operator()(random::PhiloxRandom gen, typename Distribution::ResultElementType *data, int64_t size,
154 FillPhiloxRandomTask<Distribution, Distribution::kVariableSamplesPerOutput>::Run(gen, data, size,
158 } // namespace functor
160 } // end namespace tensorflow
163 #endif // __NNFW_CKER_HELPER_RANDOM_OP_CPU_H__