query/functor.cu (230 lines of code) (raw):

// Modifications Copyright (c) 2018 Uber Technologies, Inc. // Copyright (c) 2009 The Go Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "query/functor.hpp" #include <tuple> extern __constant__ uint16_t DAYS_BEFORE_MONTH_DEVICE[13]; namespace ares { const int SECONDS_PER_HOUR = 60 * 60; const int SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; const int DAYS_PER_400_YEARS = 365 * 400 + 97; const int DAYS_PER_100_YEARS = 365 * 100 + 24; const int DAYS_PER_4_YEARS = 365 * 4 + 1; const int SECONDS_PER_FOUR_DAYS = 4 * SECONDS_PER_DAY; const int SECONDS_PER_WEEK = 7 * SECONDS_PER_DAY; // The unsigned zero year for internal calculations. // Must be 1 mod 400, and times before it will not compute correctly, // but otherwise can be changed at will. const int64_t ABSOLUTE_ZERO_TS = -62135596800; inline __host__ __device__ bool isLeapYear(uint16_t year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } // For host, daysBeforeMonth will be DAYS_BEFORE_MONTH_HOST, otherwise // it should be DAYS_BEFORE_MONTH_DEVICE. inline __host__ __device__ uint16_t getDaysBeforeMonth(uint8_t month, bool isLeapYear, const uint16_t *daysBeforeMonth) { uint16_t days = daysBeforeMonth[month]; if (isLeapYear && month >= 2) { days++; } return days; } // All following code refers to https://golang.org/src/time/time.go // Line 940. To save space, we only allowed ts >= 0, so time before 1970-01-01 // is not supported. __host__ __device__ uint32_t resolveTimeBucketizer(int64_t ts, enum TimeBucketizer timeBucketizer, const uint16_t *daysBeforeMonth) { // Adjust the timestamp to zero year for calculation. ts -= ABSOLUTE_ZERO_TS; uint32_t days = ts / SECONDS_PER_DAY; // 400 years cycle. int64_t n = days / DAYS_PER_400_YEARS; uint16_t year = 400 * n; int64_t start = n * DAYS_PER_400_YEARS * SECONDS_PER_DAY; days -= DAYS_PER_400_YEARS * n; // Cut off 100-year cycles. // The last cycle has one extra leap year, so on the last day // of that year, day / daysPer100Years will be 4 instead of 3. // Cut it back down to 3 by subtracting n>>2. n = days / DAYS_PER_100_YEARS; n -= n >> 2; year += 100 * n; start += n * DAYS_PER_100_YEARS * SECONDS_PER_DAY; days -= DAYS_PER_100_YEARS * n; // Cut off 4-year cycles. // The last cycle has a missing leap year, which does not // affect the computation. n = days / DAYS_PER_4_YEARS; year += 4 * n; start += n * DAYS_PER_4_YEARS * SECONDS_PER_DAY; days -= DAYS_PER_4_YEARS * n; // Cut off years within a 4-year cycle. // The last year is a leap year, so on the last day of that year, // day / 365 will be 4 instead of 3. Cut it back down to 3 // by subtracting n>>2. n = days / 365; n -= n >> 2; year += n; days -= 365 * n; start += n * 365 * SECONDS_PER_DAY; // Adjust back; start += ABSOLUTE_ZERO_TS; // get day of the year. if (timeBucketizer == YEAR) { return start; } // day of the year. if (timeBucketizer == DAY_OF_YEAR) { return days; } bool leapYear = isLeapYear(year + 1); // Estimate month on assumption that every month has 31 days. // The estimate may be too low by at most one month, so adjust. uint8_t month = days / 31; uint16_t monthEnd = getDaysBeforeMonth(month + 1, leapYear, daysBeforeMonth); if (days >= monthEnd) { month++; } if (timeBucketizer == MONTH || timeBucketizer == DAY_OF_MONTH) { uint32_t dayBeforeMonth = getDaysBeforeMonth(month, leapYear, daysBeforeMonth); // month start. if (timeBucketizer == MONTH) { return start + dayBeforeMonth * SECONDS_PER_DAY; } // day of month. return days - dayBeforeMonth; } // month of year. if (timeBucketizer == MONTH_OF_YEAR) { return month; } int quarter = month / 3; // quarter of year. if (timeBucketizer == QUARTER_OF_YEAR) { return quarter; } // quarter start. return start + getDaysBeforeMonth(quarter * 3, leapYear, daysBeforeMonth) * SECONDS_PER_DAY; } // The function overload for resolveTimeBucketizer with daysBeforeMonth provided // according to the running environment. __host__ __device__ thrust::tuple<uint32_t, bool> resolveTimeBucketizer( const thrust::tuple<uint32_t, bool> t, enum TimeBucketizer timeBucketizer) { if (!thrust::get<1>(t)) { return thrust::make_tuple(0, false); } // Choose daysBeforeMonth based on whether it's running on device. const uint16_t *daysBeforeMonth; #ifdef RUN_ON_DEVICE daysBeforeMonth = DAYS_BEFORE_MONTH_DEVICE; #else // Have to allocate this array and assign values in stack as even we // compile in host mode, nvcc still thinks it's a device function as we // annotate it with __device__. uint16_t daysBeforeMonthHost[13] = { 0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, }; daysBeforeMonth = daysBeforeMonthHost; #endif return thrust::make_tuple( resolveTimeBucketizer( thrust::get<0>(t), timeBucketizer, daysBeforeMonth), true); } // The function is used to get the start of week timestamp based on // the given timestamp, will return 0 if ts is before 1970/1/1 __host__ __device__ uint32_t getWeekStartTimestamp(uint32_t ts) { if (ts < SECONDS_PER_FOUR_DAYS) { return 0; } return ts - (ts - SECONDS_PER_FOUR_DAYS) % SECONDS_PER_WEEK; } __host__ __device__ thrust::tuple<uint32_t, bool> GetWeekStartFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { if (!thrust::get<1>(t)) { return thrust::make_tuple(0, false); } return thrust::make_tuple(getWeekStartTimestamp(thrust::get<0>(t)), true); } __host__ __device__ thrust::tuple<uint32_t, bool> GetMonthStartFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, MONTH); } __host__ __device__ thrust::tuple<uint32_t, bool> GetQuarterStartFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, QUATER); } __host__ __device__ thrust::tuple<uint32_t, bool> GetYearStartFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, YEAR); } __host__ __device__ thrust::tuple<uint32_t, bool> GetDayOfMonthFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, DAY_OF_MONTH); } __host__ __device__ thrust::tuple<uint32_t, bool> GetDayOfYearFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, DAY_OF_YEAR); } __host__ __device__ thrust::tuple<uint32_t, bool> GetMonthOfYearFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, MONTH_OF_YEAR); } __host__ __device__ thrust::tuple<uint32_t, bool> GetQuarterOfYearFunctor::operator()( const thrust::tuple<uint32_t, bool> t) const { return resolveTimeBucketizer(t, QUARTER_OF_YEAR); } } // namespace ares