/* ****************************************************************************** * * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * * SPDX-License-Identifier: Apache-2.0 ******************************************************************************/ // // @author AbdelRauf // #include #include #include #include #include #include #include #include #include #include #include #if 1 #define LOG_CALLS(X) #else #define LOG_CALLS(X) sd_printf("___%s_________%d+\n", __PRETTY_FUNCTION__, X); #endif #define USE_REDUCED_DIV 1 namespace sd { namespace ops { namespace helpers { constexpr int threadingThreshold = 4096 * 2; constexpr int vectorizationThreshold = 64; struct DeviationAggregate { double n; double mean; double M2; }; template class Deviation { public: using aggregate_type = DeviationAggregate; template static SD_INLINE typename std::enable_if::type getDeviation(const aggregate_type& a, bool biasCorrected) { if (a.n <= 1.0) { return static_cast(0.0); } double ret; if (biasCorrected) { ret = a.M2 / (a.n - 1.0); if (ret < 0.0) ret = a.M2 / a.n; } else { ret = a.M2 / a.n; } return sd::math::sd_sqrt(ret); } template static SD_INLINE typename std::enable_if::type getDeviation(const aggregate_type& a, bool biasCorrected) { if (a.n <= 1.0) { return static_cast(0.0); } double ret; if (biasCorrected) { ret = a.M2 / (a.n - 1.0); if (ret < 0.0) ret = a.M2 / a.n; } else { ret = a.M2 / a.n; } return static_cast(ret); } template static SD_INLINE void updateInnerLoop1b(const X* buffer, sd::LongType length, aggregate_type& a) { double xn = InitializeFromAggregate ? a.n : 0; double xmean = InitializeFromAggregate ? a.mean : 0; double xM2 = InitializeFromAggregate ? a.M2 : 0; for (sd::LongType i = 0; i < length; i++) { double n = xn + 1; double delta = buffer[i] - xmean; double delta2 = delta * delta; double delta_n = delta / n; xmean = xmean + delta_n; xM2 += delta2 * xn / n; xn = n; } a = {xn, xmean, xM2}; } template static SD_INLINE void updateInnerLoop1b(const X* buffer, sd::LongType length, sd::LongType stride, aggregate_type& a) { double xn = InitializeFromAggregate ? a.n : 0; double xmean = InitializeFromAggregate ? a.mean : 0; double xM2 = InitializeFromAggregate ? a.M2 : 0; for (sd::LongType i = 0; i < length; i++) { double n = xn + 1; double delta = buffer[i * stride] - xmean; double delta2 = delta * delta; double delta_n = delta / n; xmean = xmean + delta_n; xM2 += delta2 * xn / n; xn = n; } a = {xn, xmean, xM2}; } // WARNING: a.n a1.n a2.n a3.n should be equal template static SD_INLINE void updateInnerLoop4b(const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3, sd::LongType length, aggregate_type& a, aggregate_type& a1, aggregate_type& a2, aggregate_type& a3) { double xn, x0mean, x0M2, x1mean, x1M2, x2mean, x2M2, x3mean, x3M2; xn = InitializeFromAggregate ? a.n : 0; x0mean = InitializeFromAggregate ? a.mean : 0; x1mean = InitializeFromAggregate ? a1.mean : 0; x2mean = InitializeFromAggregate ? a2.mean : 0; x3mean = InitializeFromAggregate ? a3.mean : 0; x0M2 = InitializeFromAggregate ? a.M2 : 0; x1M2 = InitializeFromAggregate ? a1.M2 : 0; x2M2 = InitializeFromAggregate ? a2.M2 : 0; x3M2 = InitializeFromAggregate ? a3.M2 : 0; for (sd::LongType i = 0; i < length; i++) { double n = xn + 1; double delta0 = buffer[i] - x0mean; double delta1 = buffer1[i] - x1mean; double delta2 = buffer2[i] - x2mean; double delta3 = buffer3[i] - x3mean; #if defined(USE_REDUCED_DIV) double delta_nj0 = delta0 / n; double delta_nj1 = delta1 / n; double delta_nj2 = delta2 / n; double delta_nj3 = delta3 / n; x0mean = x0mean + delta_nj0; x1mean = x1mean + delta_nj1; x2mean = x2mean + delta_nj2; x3mean = x3mean + delta_nj3; x0M2 += delta0 * delta_nj0 * xn; x1M2 += delta1 * delta_nj1 * xn; x2M2 += delta2 * delta_nj2 * xn; x3M2 += delta3 * delta_nj3 * xn; #else double delta02 = delta0 * delta0; double delta12 = delta1 * delta1; double delta22 = delta2 * delta2; double delta32 = delta3 * delta3; x0mean = x0mean + delta0 / n; x1mean = x1mean + delta1 / n; x2mean = x2mean + delta2 / n; x3mean = x3mean + delta3 / n; x0M2 += delta02 * xn / n; x1M2 += delta12 * xn / n; x2M2 += delta22 * xn / n; x3M2 += delta32 * xn / n; #endif xn = n; } a = {xn, x0mean, x0M2}; a1 = {xn, x1mean, x1M2}; a2 = {xn, x2mean, x2M2}; a3 = {xn, x3mean, x3M2}; } // WARNING: a.n a1.n a2.n a3.n should be equal template static SD_INLINE void updateInnerLoop4b(const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3, sd::LongType length, sd::LongType stride, aggregate_type& a, aggregate_type& a1, aggregate_type& a2, aggregate_type& a3) { double xn, x0mean, x0M2, x1mean, x1M2, x2mean, x2M2, x3mean, x3M2; xn = InitializeFromAggregate ? a.n : 0; x0mean = InitializeFromAggregate ? a.mean : 0; x1mean = InitializeFromAggregate ? a1.mean : 0; x2mean = InitializeFromAggregate ? a2.mean : 0; x3mean = InitializeFromAggregate ? a3.mean : 0; x0M2 = InitializeFromAggregate ? a.M2 : 0; x1M2 = InitializeFromAggregate ? a1.M2 : 0; x2M2 = InitializeFromAggregate ? a2.M2 : 0; x3M2 = InitializeFromAggregate ? a3.M2 : 0; for (sd::LongType i = 0; i < length; i++) { double n = xn + 1; double delta0 = buffer[i * stride] - x0mean; double delta1 = buffer1[i * stride] - x1mean; double delta2 = buffer2[i * stride] - x2mean; double delta3 = buffer3[i * stride] - x3mean; #if defined(USE_REDUCED_DIV) double delta_nj0 = delta0 / n; double delta_nj1 = delta1 / n; double delta_nj2 = delta2 / n; double delta_nj3 = delta3 / n; x0mean = x0mean + delta_nj0; x1mean = x1mean + delta_nj1; x2mean = x2mean + delta_nj2; x3mean = x3mean + delta_nj3; x0M2 += delta0 * delta_nj0 * xn; x1M2 += delta1 * delta_nj1 * xn; x2M2 += delta2 * delta_nj2 * xn; x3M2 += delta3 * delta_nj3 * xn; #else double delta02 = delta0 * delta0; double delta12 = delta1 * delta1; double delta22 = delta2 * delta2; double delta32 = delta3 * delta3; x0mean = x0mean + delta0 / n; x1mean = x1mean + delta1 / n; x2mean = x2mean + delta2 / n; x3mean = x3mean + delta3 / n; x0M2 += delta02 * xn / n; x1M2 += delta12 * xn / n; x2M2 += delta22 * xn / n; x3M2 += delta32 * xn / n; #endif xn = n; } a = {xn, x0mean, x0M2}; a1 = {xn, x1mean, x1M2}; a2 = {xn, x2mean, x2M2}; a3 = {xn, x3mean, x3M2}; } // WARNING: length required to to be length/8. static SD_INLINE void updateInnerLoop1b_vec8(const X* buffer, sd::LongType length_8th, double (&xn)[8], double (&xmean)[8], double (&xM2)[8]) { double n[8] = {}; for (int i = 0; i < length_8th; i++) { const X* bufferX = &(buffer[i * 8]); #pragma omp simd for (int j = 0; j < 8; j++) { n[j] = xn[j] + 1.0; #if defined(USE_REDUCED_DIV) double delta = bufferX[j] - xmean[j]; double delta_nj = delta / n[j]; xmean[j] = xmean[j] + delta_nj; xM2[j] = xM2[j] + delta * delta_nj * xn[j]; #else double delta = bufferX[j] - xmean[j]; double delta2 = delta * delta; xmean[j] = xmean[j] + delta / n[j]; xM2[j] = xM2[j] + delta2 * xn[j] / n[j]; #endif xn[j] = n[j]; } } } // WARNING: length required to to be length/8. // WARNING: a.n a1.n a2.n a3.n should be equal static SD_INLINE void updateInnerLoop4b_vec8(const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3, sd::LongType length_8th, double (&xn)[8], double (&x0mean)[8], double (&x1mean)[8], double (&x2mean)[8], double (&x3mean)[8], double (&x0M2)[8], double (&x1M2)[8], double (&x2M2)[8], double (&x3M2)[8]) { double n[8] = {}; for (sd::LongType i = 0; i < length_8th; i++) { const X* bufferX = &(buffer[i * 8]); const X* buffer1X = &(buffer1[i * 8]); const X* buffer2X = &(buffer2[i * 8]); const X* buffer3X = &(buffer3[i * 8]); #pragma omp simd for (int j = 0; j < 8; j++) { n[j] = xn[j] + 1.0; double delta0 = bufferX[j] - x0mean[j]; double delta1 = buffer1X[j] - x1mean[j]; double delta2 = buffer2X[j] - x2mean[j]; double delta3 = buffer3X[j] - x3mean[j]; double delta0_nj = delta0 / n[j]; double delta1_nj = delta1 / n[j]; double delta2_nj = delta2 / n[j]; double delta3_nj = delta3 / n[j]; x0mean[j] = x0mean[j] + delta0_nj; x1mean[j] = x1mean[j] + delta1_nj; x2mean[j] = x2mean[j] + delta2_nj; x3mean[j] = x3mean[j] + delta3_nj; x0M2[j] = x0M2[j] + xn[j] * delta0 * delta0_nj; x1M2[j] = x1M2[j] + xn[j] * delta1 * delta1_nj; x2M2[j] = x2M2[j] + xn[j] * delta2 * delta2_nj; x3M2[j] = x3M2[j] + xn[j] * delta3 * delta3_nj; xn[j] = n[j]; } } } template static SD_INLINE void reduceConstRankLoop4b(const X* buff, const X* buff1, const X* buff2, const X* buff3, Z* output0, Z* output1, Z* output2, Z* output3, const sd::LongType* bases, const sd::LongType* strides, const sd::LongType& outerLoopCount, const sd::LongType& innerLoopCount, bool biasCorrected) { // skip 1 from the beginning or end depending the Order constexpr size_t updated_index = LastIndexFaster ? 0 : 1; constexpr size_t updated_rank = constRank - 1; sd::CoordsState cst; // we skip 1 size_t offset = sd::init_coords(cst, 0, bases + updated_index, strides + updated_index); aggregate_type agg0 = {}; aggregate_type agg1 = {}; aggregate_type agg2 = {}; aggregate_type agg3 = {}; if (innerLoopCount >= vectorizationThreshold) { LOG_CALLS(0) // use vector const sd::LongType loopCount = innerLoopCount & (-8); const sd::LongType tail = innerLoopCount & 7; const auto loopCount_8th = loopCount / 8; double xn[8] = {}; double x0mean[8] = {}; double x1mean[8] = {}; double x2mean[8] = {}; double x3mean[8] = {}; double x0M2[8] = {}; double x1M2[8] = {}; double x2M2[8] = {}; double x3M2[8] = {}; for (sd::LongType i = 0; i < outerLoopCount; i++) { const X* buffPtr0 = &(buff[offset]); const X* buffPtr1 = &(buff1[offset]); const X* buffPtr2 = &(buff2[offset]); const X* buffPtr3 = &(buff3[offset]); updateInnerLoop4b_vec8(buffPtr0, buffPtr1, buffPtr2, buffPtr3, loopCount_8th, xn, x0mean, x1mean, x2mean, x3mean, x0M2, x1M2, x2M2, x3M2); if (tail > 0) { // tails updateInnerLoop4b(&(buffPtr0[loopCount]), &(buffPtr1[loopCount]), &(buffPtr2[loopCount]), &(buffPtr3[loopCount]), tail, agg0, agg1, agg2, agg3); } offset = sd::inc_coords(cst, offset); } // merge vector and tails auto merged0 = mergeAggregates(xn[0], x0mean, x0M2); auto merged1 = mergeAggregates(xn[0], x1mean, x1M2); auto merged2 = mergeAggregates(xn[0], x2mean, x2M2); auto merged3 = mergeAggregates(xn[0], x3mean, x3M2); // tail with merged3 agg0 = mergeAggregates(merged0, agg0); agg1 = mergeAggregates(merged1, agg1); agg2 = mergeAggregates(merged2, agg2); agg3 = mergeAggregates(merged3, agg3); } else { LOG_CALLS(1) for (sd::LongType i = 0; i < outerLoopCount; i++) { updateInnerLoop4b(&(buff[offset]), &(buff1[offset]), &(buff2[offset]), &(buff3[offset]), innerLoopCount, agg0, agg1, agg2, agg3); offset = sd::inc_coords(cst, offset); } } *output0 = getDeviation(agg0, biasCorrected); *output1 = getDeviation(agg1, biasCorrected); *output2 = getDeviation(agg2, biasCorrected); *output3 = getDeviation(agg3, biasCorrected); return; } template static SD_INLINE void reduceConstRankLoop4b(const X* buff, const X* buff1, const X* buff2, const X* buff3, Z* output0, Z* output1, Z* output2, Z* output3, const sd::LongType* bases, const sd::LongType* strides, const sd::LongType& outerLoopCount, const sd::LongType& innerLoopCount, const sd::LongType& inner_stride, bool biasCorrected) { LOG_CALLS(2) // skip 1 from the beginning or end depending the Order constexpr size_t updated_index = LastIndexFaster ? 0 : 1; constexpr size_t updated_rank = constRank - 1; sd::CoordsState cst; // we skip 1 size_t offset = sd::init_coords(cst, 0, bases + updated_index, strides + updated_index); aggregate_type agg = {0.0, 0.0, 0.0}; aggregate_type agg1 = {0.0, 0.0, 0.0}; aggregate_type agg2 = {0.0, 0.0, 0.0}; aggregate_type agg3 = {0.0, 0.0, 0.0}; // LOG_CALLS(0) for (sd::LongType i = 0; i < outerLoopCount; i++) { updateInnerLoop4b(&(buff[offset]), &(buff1[offset]), &(buff2[offset]), &(buff3[offset]), innerLoopCount, inner_stride, agg, agg1, agg2, agg3); offset = sd::inc_coords(cst, offset); } *output0 = getDeviation(agg, biasCorrected); *output1 = getDeviation(agg1, biasCorrected); *output2 = getDeviation(agg2, biasCorrected); *output3 = getDeviation(agg3, biasCorrected); return; } template static SD_INLINE void reduceConstRankLoop1b(const X* buff, Z* output, const sd::LongType* bases, const sd::LongType* strides, const sd::LongType& outerLoopCount, const sd::LongType& innerLoopCount, bool biasCorrected) { // skip 1 from the beginning or end depending the Order constexpr size_t updated_index = LastIndexFaster ? 0 : 1; constexpr size_t updated_rank = constRank - 1; sd::CoordsState cst; // we skip 1 size_t offset = sd::init_coords(cst, 0, bases + updated_index, strides + updated_index); aggregate_type agg = {0.0, 0.0, 0.0}; if (innerLoopCount >= vectorizationThreshold) { LOG_CALLS(0) // use vector const sd::LongType loopCount = innerLoopCount & (-8); const sd::LongType tail = innerLoopCount & 7; const auto loopCount_8th = loopCount / 8; double xn[8] = {}; double xmean[8] = {}; double xM2[8] = {}; for (sd::LongType i = 0; i < outerLoopCount; i++) { const X* buffPtr0 = &(buff[offset]); updateInnerLoop1b_vec8(buffPtr0, loopCount_8th, xn, xmean, xM2); if (tail > 0) { updateInnerLoop1b(&(buffPtr0[loopCount]), tail, agg); } offset = sd::inc_coords(cst, offset); } // merge vector between and with the tail agg auto merged = mergeAggregates(xn[0], xmean, xM2); agg = mergeAggregates(agg, merged); } else { LOG_CALLS(1) for (sd::LongType i = 0; i < outerLoopCount; i++) { updateInnerLoop1b(&(buff[offset]), innerLoopCount, agg); offset = sd::inc_coords(cst, offset); } } *output = getDeviation(agg, biasCorrected); return; } template static SD_INLINE void reduceConstRankLoop1b(const X* buff, Z* output, const sd::LongType* bases, const sd::LongType* strides, const sd::LongType& outerLoopCount, const sd::LongType& innerLoopCount, const sd::LongType& inner_stride, bool biasCorrected) { LOG_CALLS(2) // skip 1 from the beginning or end depending the Order constexpr size_t updated_index = LastIndexFaster ? 0 : 1; constexpr size_t updated_rank = constRank - 1; sd::CoordsState cst; // we skip 1 size_t offset = sd::init_coords(cst, 0, bases + updated_index, strides + updated_index); aggregate_type agg = {0.0, 0.0, 0.0}; // LOG_CALLS(0) for (sd::LongType i = 0; i < outerLoopCount; i++) { updateInnerLoop1b(&(buff[offset]), innerLoopCount, inner_stride, agg); offset = sd::inc_coords(cst, offset); } *output = getDeviation(agg, biasCorrected); return; } template static SD_INLINE void updateGeneralLoop1b(LongType rank, const X* buff, DeviationAggregate& agg, const sd::LongType* bases, const sd::LongType* strides, const sd::LongType& outerLoopStart, const sd::LongType& outerLoopStop, const sd::LongType& innerLoopCount) { agg = {}; size_t offset = 0; sd::LongType outerLoopCount = outerLoopStop - outerLoopStart; sd::LongType coords[SD_MAX_RANK] = {}; sd::LongType* ptr_coords = (sd::LongType*)&coords; if (outerLoopStart > 0) { INDEX2COORDS(outerLoopStart, rank - 1, bases, ptr_coords); COORDS2INDEX(rank, strides, ptr_coords, offset); } if (innerLoopCount >= vectorizationThreshold) { LOG_CALLS(88) // use vector const sd::LongType loopCount = innerLoopCount & (-8); const sd::LongType tail = innerLoopCount & 7; const auto loopCount_8th = loopCount / 8; double xn[8] = {}; double x0mean[8] = {}; double x0M2[8] = {}; for (sd::LongType i = 0; i < outerLoopCount; i++) { const X* buffPtr0 = &(buff[offset]); updateInnerLoop1b_vec8(buffPtr0, loopCount_8th, xn, x0mean, x0M2); if (tail > 0) { // tails updateInnerLoop1b(&(buffPtr0[loopCount]), tail, agg); } offset = inc_coords(bases, strides, ptr_coords, offset, rank, 1); } // merge vector and tails auto merged0 = mergeAggregates(xn[0], x0mean, x0M2); // tail with merged3 agg = mergeAggregates(merged0, agg); } else { LOG_CALLS(89) for (sd::LongType i = 0; i < outerLoopCount; i++) { updateInnerLoop1b(&(buff[offset]), innerLoopCount, agg); offset = inc_coords(bases, strides, ptr_coords, offset, rank, 1); } } } template static SD_INLINE void updateGeneralLoop1b(int rank, const X* buff, DeviationAggregate& agg, const sd::LongType* bases, const sd::LongType* strides, const sd::LongType& outerLoopStart, const sd::LongType& outerLoopStop, const sd::LongType& innerLoopCount, const sd::LongType& inner_stride) { agg = {}; size_t offset = 0; sd::LongType outerLoopCount = outerLoopStop - outerLoopStart; sd::LongType coords[SD_MAX_RANK] = {}; sd::LongType* ptr_coords = (sd::LongType*)&coords; if (outerLoopStart > 0) { INDEX2COORDS(outerLoopStart, rank - 1, bases, ptr_coords); COORDS2INDEX(rank, strides, ptr_coords, offset); } LOG_CALLS(90) for (sd::LongType i = 0; i < outerLoopCount; i++) { updateInnerLoop1b(&(buff[offset]), innerLoopCount, inner_stride, agg); offset = inc_coords(bases, strides, ptr_coords, offset, rank, 1); } } static SD_INLINE aggregate_type mergeAggregates(const aggregate_type& x, const aggregate_type& y) { if ((long)x.n == 0 && (long)y.n > 0) return y; else if ((long)x.n > 0 && (long)y.n == 0) return x; double n = x.n + y.n; double delta = y.mean - x.mean; double delta2 = delta * delta; double meanD = x.mean + delta * y.n / n; double M2D = x.M2 + y.M2; M2D += delta2 * x.n * y.n / n; return {n, meanD, M2D}; } static SD_INLINE aggregate_type mergeAggregates(double xn, double (&xmean)[8], double (&xM2)[8]) { auto arg0 = mergeAggregates(xn, xn, xmean[0], xmean[1], xM2[0], xM2[1]); auto arg1 = mergeAggregates(xn, xn, xmean[2], xmean[3], xM2[2], xM2[3]); auto arg2 = mergeAggregates(xn, xn, xmean[4], xmean[5], xM2[4], xM2[5]); auto arg3 = mergeAggregates(xn, xn, xmean[6], xmean[7], xM2[6], xM2[7]); auto arg01 = mergeAggregates(arg0, arg1); auto arg23 = mergeAggregates(arg2, arg3); return mergeAggregates(arg01, arg23); } static SD_INLINE aggregate_type mergeAggregates(double (&xn)[8], double (&xmean)[8], double (&xM2)[8]) { auto arg0 = mergeAggregates(xn[0], xn[1], xmean[0], xmean[1], xM2[0], xM2[1]); auto arg1 = mergeAggregates(xn[2], xn[3], xmean[2], xmean[3], xM2[2], xM2[3]); auto arg2 = mergeAggregates(xn[4], xn[5], xmean[4], xmean[5], xM2[4], xM2[5]); auto arg3 = mergeAggregates(xn[6], xn[7], xmean[6], xmean[7], xM2[6], xM2[7]); auto arg01 = mergeAggregates(arg0, arg1); auto arg23 = mergeAggregates(arg2, arg3); return mergeAggregates(arg01, arg23); } static SD_INLINE aggregate_type mergeAggregates(double xn, double yn, double xmean, double ymean, double xM2, double yM2) { double n = xn + yn; double delta = ymean - xmean; double delta2 = delta * delta; double meanD = xmean + delta * yn / n; double M2D = xM2 + yM2; M2D += delta2 * xn * yn / n; return {n, meanD, M2D}; } }; template static void reductionCase1Scalar(const int& second_rank, const sd::LongType* inner_bases, const sd::LongType* inner_strides, const X* bufferX, Z* outputZ, bool biasCorrected) { using AggType = typename DeviationOp::aggregate_type; sd::LongType inner_total; sd::LongType inner_last = 0; int maxThreads = sd::Environment::getInstance().maxMasterThreads(); if (second_rank == 1) { inner_total = inner_bases[0]; if (inner_total < threadingThreshold) { maxThreads = 1; } else { auto gen = inner_total / threadingThreshold + 1; maxThreads = gen > maxThreads ? maxThreads : gen; } } else { inner_total = getLength(inner_bases, second_rank, 1, inner_last); if (inner_total * inner_last < threadingThreshold) { maxThreads = 1; } else { auto gen = inner_total * inner_last / threadingThreshold + 1; maxThreads = gen > maxThreads ? maxThreads : gen; } } #define BLOCKX4 1 std::unique_ptr aggs(new AggType[maxThreads]); AggType* ptrAggs = aggs.get(); auto func = [ptrAggs, inner_last, second_rank, inner_bases, inner_strides, bufferX]( uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void { // LOG_CALLS(0) const sd::LongType inner_stride = LastIndexFaster ? inner_strides[second_rank - 1] : inner_strides[0]; Z argCurrent; X current; if (second_rank == 1) { const sd::LongType loopTotal = stop - start; #if defined(BLOCKX4) if (loopTotal >= 2048) { AggType agg, agg1, agg2, agg3; if (inner_stride == 1) { // use vector version sd::LongType loopTotal4_32 = loopTotal & (-32); auto loopCount4 = loopTotal4_32 / 4; auto loopCount4_8th = loopCount4 / 8; auto tail = (loopTotal & 31); const X* buffer0 = bufferX + start * 1; const X* buffer1 = buffer0 + 1 * loopCount4; const X* buffer2 = buffer1 + 1 * loopCount4; const X* buffer3 = buffer2 + 1 * loopCount4; double xn[8] = {}; double x0mean[8] = {}; double x1mean[8] = {}; double x2mean[8] = {}; double x3mean[8] = {}; double x0M2[8] = {}; double x1M2[8] = {}; double x2M2[8] = {}; double x3M2[8] = {}; DeviationOp::updateInnerLoop4b_vec8(buffer0, buffer1, buffer2, buffer3, loopCount4_8th, xn, x0mean, x1mean, x2mean, x3mean, x0M2, x1M2, x2M2, x3M2); // merge vectors agg = DeviationOp::mergeAggregates(xn[0], x0mean, x0M2); agg1 = DeviationOp::mergeAggregates(xn[0], x1mean, x1M2); agg2 = DeviationOp::mergeAggregates(xn[0], x2mean, x2M2); agg3 = DeviationOp::mergeAggregates(xn[0], x3mean, x3M2); // tail merge to one of the aggs if (tail > 0) { DeviationOp::template updateInnerLoop1b(&(buffer3[loopCount4]), tail, agg3); } } else { auto loopCount4 = loopTotal / 4; auto tail = (loopTotal & 3); const X* buffer0 = bufferX + start * inner_stride; const X* buffer1 = buffer0 + inner_stride * loopCount4; const X* buffer2 = buffer1 + inner_stride * loopCount4; const X* buffer3 = buffer2 + inner_stride * loopCount4; DeviationOp::updateInnerLoop4b(buffer0, buffer1, buffer2, buffer3, loopCount4, inner_stride, agg, agg1, agg2, agg3); // tail if (tail > 0) { DeviationOp::template updateInnerLoop1b(&(buffer3[loopCount4 * inner_stride]), tail, inner_stride, agg3); } } // merge all auto merged = DeviationOp::mergeAggregates(agg, agg1); merged = DeviationOp::mergeAggregates(merged, agg2); merged = DeviationOp::mergeAggregates(merged, agg3); ptrAggs[thread_id] = merged; } else { #endif if (inner_stride == 1) { const X* buffer0 = bufferX + start; if (loopTotal > vectorizationThreshold) { auto length8 = loopTotal / 8; auto bufferTail = buffer0 + (loopTotal & (-8)); auto tail = loopTotal & 7; double xn[8] = {}; double xmean[8] = {}; double xM2[8] = {}; DeviationOp::updateInnerLoop1b_vec8(buffer0, length8, xn, xmean, xM2); auto agg = DeviationOp::mergeAggregates(xn[0], xmean, xM2); // add tail into if (tail > 0) { DeviationOp::template updateInnerLoop1b(bufferTail, tail, agg); } ptrAggs[thread_id] = agg; } else { DeviationOp::updateInnerLoop1b(buffer0, loopTotal, ptrAggs[thread_id]); } } else { DeviationOp::updateInnerLoop1b(&(bufferX[start * inner_stride]), loopTotal, inner_stride, ptrAggs[thread_id]); } #if defined(BLOCKX4) } #endif } else { // just lets do general case if (inner_stride == 1) { DeviationOp::template updateGeneralLoop1b(second_rank, bufferX, ptrAggs[thread_id], inner_bases, inner_strides, start, stop, inner_last, inner_stride); } else { DeviationOp::template updateGeneralLoop1b(second_rank, bufferX, ptrAggs[thread_id], inner_bases, inner_strides, start, stop, inner_last, inner_stride); } } }; #if 0 int Count = 0; func(0, 0, inner_total, 1); #else int Count = samediff::Threads::parallel_tad(func, 0, inner_total, 1, maxThreads); #endif auto current = ptrAggs[0]; for (int i = 1; i < Count; i++) { current = DeviationOp::mergeAggregates(current, ptrAggs[i]); } *outputZ = DeviationOp::getDeviation(current, biasCorrected); } template static void reductionCases(Movement& movement, sd::LongType loopTotal, const int& second_rank, const sd::LongType* inner_bases, const sd::LongType* inner_strides, const X* bufferX, Z* outputZ, bool biasCorrected) { using AggType = typename DeviationOp::aggregate_type; sd::LongType inner_stride = LastIndexFaster ? inner_strides[second_rank - 1] : inner_strides[0]; sd::LongType loopTotal_K = loopTotal / 4; sd::LongType loopTotalTail = loopTotal & 3; if (inner_stride == 1) { if (second_rank == 1) { LOG_CALLS(0) sd::LongType inner_total = getLength(inner_bases, second_rank); auto loopCount4 = inner_total & (-8); auto loopCount4_8th = inner_total / 8; auto tail = inner_total & 7; bool use_vector = loopCount4_8th > 16; for (sd::LongType i = 0; i < loopTotal_K; i++) { AggType agg0, agg1, agg2, agg3; const X* buff0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); movement.increment(); const X* buff1 = &(bufferX[movement.First()]); Z* output1 = &(outputZ[movement.Second()]); movement.increment(); const X* buff2 = &(bufferX[movement.First()]); Z* output2 = &(outputZ[movement.Second()]); movement.increment(); const X* buff3 = &(bufferX[movement.First()]); Z* output3 = &(outputZ[movement.Second()]); movement.increment(); if (use_vector) { double xn[8] = {}; double x0mean[8] = {}; double x1mean[8] = {}; double x2mean[8] = {}; double x3mean[8] = {}; double x0M2[8] = {}; double x1M2[8] = {}; double x2M2[8] = {}; double x3M2[8] = {}; DeviationOp::updateInnerLoop4b_vec8(buff0, buff1, buff2, buff3, loopCount4_8th, xn, x0mean, x1mean, x2mean, x3mean, x0M2, x1M2, x2M2, x3M2); // merge vectors agg0 = DeviationOp::mergeAggregates(xn[0], x0mean, x0M2); agg1 = DeviationOp::mergeAggregates(xn[0], x1mean, x1M2); agg2 = DeviationOp::mergeAggregates(xn[0], x2mean, x2M2); agg3 = DeviationOp::mergeAggregates(xn[0], x3mean, x3M2); if (tail > 0) { // tails into merged , this time for each DeviationOp::template updateInnerLoop4b(&(buff0[loopCount4]), &(buff1[loopCount4]), &(buff2[loopCount4]), &(buff3[loopCount4]), tail, agg0, agg1, agg2, agg3); } } else { DeviationOp::updateInnerLoop4b(buff0, buff1, buff2, buff3, inner_total, agg0, agg1, agg2, agg3); } *output0 = DeviationOp::getDeviation(agg0, biasCorrected); *output1 = DeviationOp::getDeviation(agg1, biasCorrected); *output2 = DeviationOp::getDeviation(agg2, biasCorrected); *output3 = DeviationOp::getDeviation(agg3, biasCorrected); } for (sd::LongType i = 0; i < loopTotalTail; i++) { AggType agg0; const X* buff0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); if (use_vector) { double xn[8] = {}; double x0mean[8] = {}; double x0M2[8] = {}; DeviationOp::updateInnerLoop1b_vec8(buff0, loopCount4_8th, xn, x0mean, x0M2); // merge vectors agg0 = DeviationOp::mergeAggregates(xn[0], x0mean, x0M2); if (tail > 0) { // tails into merged DeviationOp::template updateInnerLoop1b(&(buff0[loopCount4]), tail, agg0); } } else { DeviationOp::updateInnerLoop1b(buff0, inner_total, agg0); } movement.increment(); *output0 = DeviationOp::getDeviation(agg0, biasCorrected); } } else { sd::LongType inner_last; sd::LongType inner_loop = getLength(inner_bases, second_rank, 1, inner_last); if (second_rank == 2) { LOG_CALLS(11) for (sd::LongType i = 0; i < loopTotal_K; i++) { const X* buffer0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer1 = &(bufferX[movement.First()]); Z* output1 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer2 = &(bufferX[movement.First()]); Z* output2 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer3 = &(bufferX[movement.First()]); Z* output3 = &(outputZ[movement.Second()]); movement.increment(); DeviationOp::template reduceConstRankLoop4b<2, LastIndexFaster>( buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides, inner_loop, inner_last, biasCorrected); } for (sd::LongType i = 0; i < loopTotalTail; i++) { const X* buffer0 = &(bufferX[movement.First()]); DeviationOp::template reduceConstRankLoop1b<2, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]), inner_bases, inner_strides, inner_loop, inner_last, biasCorrected); movement.increment(); } } else if (second_rank == 3) { LOG_CALLS(12) for (sd::LongType i = 0; i < loopTotal_K; i++) { const X* buffer0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer1 = &(bufferX[movement.First()]); Z* output1 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer2 = &(bufferX[movement.First()]); Z* output2 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer3 = &(bufferX[movement.First()]); Z* output3 = &(outputZ[movement.Second()]); movement.increment(); DeviationOp::template reduceConstRankLoop4b<3, LastIndexFaster>( buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides, inner_loop, inner_last, biasCorrected); } for (sd::LongType i = 0; i < loopTotalTail; i++) { const X* buffer0 = &(bufferX[movement.First()]); DeviationOp::template reduceConstRankLoop1b<3, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]), inner_bases, inner_strides, inner_loop, inner_last, biasCorrected); movement.increment(); } } else { LOG_CALLS(13) AggType agg; for (sd::LongType i = 0; i < loopTotal; i++) { const X* buffer0 = &(bufferX[movement.First()]); DeviationOp::template updateGeneralLoop1b(second_rank, buffer0, agg, inner_bases, inner_strides, 0, inner_loop, inner_last); outputZ[movement.Second()] = DeviationOp::getDeviation(agg, biasCorrected); movement.increment(); } } } } else { if (second_rank == 1) { LOG_CALLS(20) sd::LongType inner_total = getLength(inner_bases, second_rank); for (sd::LongType i = 0; i < loopTotal_K; i++) { AggType agg, agg1, agg2, agg3; const X* buffer0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer1 = &(bufferX[movement.First()]); Z* output1 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer2 = &(bufferX[movement.First()]); Z* output2 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer3 = &(bufferX[movement.First()]); Z* output3 = &(outputZ[movement.Second()]); movement.increment(); DeviationOp::updateInnerLoop4b(buffer0, buffer1, buffer2, buffer3, inner_total, inner_stride, agg, agg1, agg2, agg3); *output0 = DeviationOp::getDeviation(agg, biasCorrected); *output1 = DeviationOp::getDeviation(agg1, biasCorrected); *output2 = DeviationOp::getDeviation(agg2, biasCorrected); *output3 = DeviationOp::getDeviation(agg3, biasCorrected); } for (sd::LongType i = 0; i < loopTotalTail; i++) { AggType agg; const X* buffer0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); DeviationOp::updateInnerLoop1b(buffer0, inner_total, inner_stride, agg); movement.increment(); *output0 = DeviationOp::getDeviation(agg, biasCorrected); } } else { sd::LongType inner_last; sd::LongType inner_loop = getLength(inner_bases, second_rank, 1, inner_last); if (second_rank == 2) { LOG_CALLS(21) for (sd::LongType i = 0; i < loopTotal_K; i++) { const X* buffer0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer1 = &(bufferX[movement.First()]); Z* output1 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer2 = &(bufferX[movement.First()]); Z* output2 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer3 = &(bufferX[movement.First()]); Z* output3 = &(outputZ[movement.Second()]); movement.increment(); DeviationOp::template reduceConstRankLoop4b<2, LastIndexFaster>( buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides, inner_loop, inner_last, inner_stride, biasCorrected); } for (sd::LongType i = 0; i < loopTotalTail; i++) { const X* buffer0 = &(bufferX[movement.First()]); DeviationOp::template reduceConstRankLoop1b<2, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]), inner_bases, inner_strides, inner_loop, inner_last, inner_stride, biasCorrected); movement.increment(); } } else if (second_rank == 3) { LOG_CALLS(22) for (sd::LongType i = 0; i < loopTotal_K; i++) { const X* buffer0 = &(bufferX[movement.First()]); Z* output0 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer1 = &(bufferX[movement.First()]); Z* output1 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer2 = &(bufferX[movement.First()]); Z* output2 = &(outputZ[movement.Second()]); movement.increment(); const X* buffer3 = &(bufferX[movement.First()]); Z* output3 = &(outputZ[movement.Second()]); movement.increment(); DeviationOp::template reduceConstRankLoop4b<3, LastIndexFaster>( buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides, inner_loop, inner_last, inner_stride, biasCorrected); } for (sd::LongType i = 0; i < loopTotalTail; i++) { const X* buffer0 = &(bufferX[movement.First()]); DeviationOp::template reduceConstRankLoop1b<3, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]), inner_bases, inner_strides, inner_loop, inner_last, inner_stride, biasCorrected); movement.increment(); } } else { LOG_CALLS(23) AggType agg; for (sd::LongType i = 0; i < loopTotal; i++) { const X* buffer0 = &(bufferX[movement.First()]); DeviationOp::template updateGeneralLoop1b( second_rank, buffer0, agg, inner_bases, inner_strides, 0, inner_loop, inner_last, inner_stride); outputZ[movement.Second()] = DeviationOp::getDeviation(agg, biasCorrected); movement.increment(); } } } } } template static void reductionCaseNonScalar(const int& first_rank, const int& output_rank, bool squashed, const int& second_rank, const sd::LongType*& outer_bases, const sd::LongType* outer_strides, const sd::LongType* output_strides, const sd::LongType& output_stride, const sd::LongType*& inner_bases, const sd::LongType* inner_strides, const X* bufferX, Z* outputZ, bool biasCorrected) { sd::LongType total = getLength(outer_bases, first_rank); sd::LongType inner_stride = LastIndexFaster ? inner_strides[second_rank - 1] : inner_strides[0]; sd::LongType outer_stride = LastIndexFaster ? outer_strides[second_rank - 1] : outer_strides[0]; auto func = [first_rank, output_rank, squashed, outer_bases, outer_strides, output_strides, output_stride, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected](uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void { sd::LongType loopTotal = stop - start; sd::LongType stride = LastIndexFaster ? outer_strides[first_rank - 1] : outer_strides[0]; if (first_rank == 1) { if (stride == 1) { ZipGenericCoordsRank1Stride1 movement; movement.init(nullptr, nullptr, nullptr, 0, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } else { ZipGenericCoordsRank1BothStrideN movement; movement.init(nullptr, &stride, &output_stride, 0, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } } else if (squashed && first_rank <= output_rank) { if (first_rank == 2) { if (output_stride == 1) { ZipGenericCoordsConstMovementSecondStride1<2, LastIndexFaster> movement; movement.init(outer_bases, outer_strides, nullptr, first_rank, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } else { ZipGenericCoordsConstMovementSecondStrideN<2, LastIndexFaster> movement; movement.init(outer_bases, outer_strides, &output_stride, first_rank, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } } else if (first_rank == 3) { if (output_stride == 1) { ZipGenericCoordsConstMovementSecondStride1<3, LastIndexFaster> movement; movement.init(outer_bases, outer_strides, nullptr, first_rank, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } else { ZipGenericCoordsConstMovementSecondStrideN<3, LastIndexFaster> movement; movement.init(outer_bases, outer_strides, &output_stride, first_rank, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } } else { ZipGenericCoordsMovementSecondStrideN movement; movement.init(outer_bases, outer_strides, &output_stride, first_rank, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } } else { ZipGenericCoordsMovement movement; movement.init(outer_bases, outer_strides, output_strides, first_rank, start); reductionCases(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } }; #if 0 func(0, 0, total, 1); #else // uint32_t numThreads = sd::Environment::getInstance().maxMasterThreads(); sd::LongType inner_total = getLength(inner_bases, second_rank); if (total * inner_total <= threadingThreshold) { numThreads = 1; } else { if (inner_stride > outer_stride && total <= 1024) { auto desired = total > 4 ? (total / 4) : 1; numThreads = numThreads > desired ? desired : numThreads; } } samediff::Threads::parallel_tad(func, 0, total, 1, numThreads); #endif } template static void reduction_(NDArray& input, NDArray& output, const std::vector& dimensions, bool biasCorrected) { char input_order = input.ordering(); bool try_squash_outer = (input_order == output.ordering()) && output.ews() != 0; auto input_shapeInfo = input.shapeInfo(); auto output_shapeInfo = output.shapeInfo(); const sd::LongType rank = input_shapeInfo[0]; const sd::LongType* input_bases = &(input_shapeInfo[1]); const sd::LongType* input_strides = &(input_shapeInfo[rank + 1]); const sd::LongType output_rank = output_shapeInfo[0]; const sd::LongType* output_strides = &(output_shapeInfo[output_rank + 1]); sd::LongType new_bases[SD_MAX_RANK]; sd::LongType new_strides[SD_MAX_RANK]; sd::LongType first_begin, first_end, second_begin, second_end; // rePartition into two parts based on the selection rePartition(input_order, dimensions, rank, input_bases, input_strides, new_bases, new_strides, first_begin, first_end, second_begin, second_end, try_squash_outer, true); int first_rank = first_end - first_begin; // the first rank can be 0 for scalar cases int second_rank = second_end - second_begin; auto bufferX = input.bufferAsT(); auto outputZ = output.bufferAsT(); const sd::LongType* outer_bases = &(new_bases[first_begin]); const sd::LongType* outer_strides = &(new_strides[first_begin]); const sd::LongType* inner_bases = &(new_bases[second_begin]); const sd::LongType* inner_strides = &(new_strides[second_begin]); const sd::LongType output_stride = output.ordering() == 'c' ? output_strides[output_rank - 1] : output_strides[0]; if (input_order == 'c') { if (first_rank == 0) { reductionCase1Scalar(second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } else { reductionCaseNonScalar(first_rank, output_rank, try_squash_outer, second_rank, outer_bases, outer_strides, output_strides, output_stride, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } } else { if (first_rank == 0) { LOG_CALLS(100); reductionCase1Scalar(second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } else { LOG_CALLS(101); reductionCaseNonScalar(first_rank, output_rank, try_squash_outer, second_rank, outer_bases, outer_strides, output_strides, output_stride, inner_bases, inner_strides, bufferX, outputZ, biasCorrected); } } } template SD_LIB_HIDDEN void variance_(NDArray& input, NDArray& output, const std::vector& dimensions, bool biasCorrected) { return reduction_>(input, output, dimensions, biasCorrected); } template SD_LIB_HIDDEN void standardDeviation_(NDArray& input, NDArray& output, const std::vector& dimensions, bool biasCorrected) { return reduction_>(input, output, dimensions, biasCorrected); } } // namespace helpers } // namespace ops } // namespace sd