github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/functor.cu (about)

     1  // Modifications Copyright (c) 2018 Uber Technologies, Inc.
     2  // Copyright (c) 2009 The Go Authors. All rights reserved.
     3  //
     4  // Redistribution and use in source and binary forms, with or without
     5  // modification, are permitted provided that the following conditions are
     6  // met:
     7  //
     8  //    * Redistributions of source code must retain the above copyright
     9  // notice, this list of conditions and the following disclaimer.
    10  //    * Redistributions in binary form must reproduce the above
    11  // copyright notice, this list of conditions and the following disclaimer
    12  // in the documentation and/or other materials provided with the
    13  // distribution.
    14  //    * Neither the name of Google Inc. nor the names of its
    15  // contributors may be used to endorse or promote products derived from
    16  // this software without specific prior written permission.
    17  //
    18  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    19  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    20  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    21  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    22  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    23  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    24  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    25  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    26  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    27  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    28  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    29  
    30  #include "query/functor.hpp"
    31  #include <tuple>
    32  
    33  extern __constant__  uint16_t DAYS_BEFORE_MONTH_DEVICE[13];
    34  
    35  namespace ares {
    36  
    37  const int SECONDS_PER_HOUR = 60 * 60;
    38  const int SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR;
    39  const int DAYS_PER_400_YEARS = 365 * 400 + 97;
    40  const int DAYS_PER_100_YEARS = 365 * 100 + 24;
    41  const int DAYS_PER_4_YEARS = 365 * 4 + 1;
    42  const int SECONDS_PER_FOUR_DAYS = 4 * SECONDS_PER_DAY;
    43  const int SECONDS_PER_WEEK = 7 * SECONDS_PER_DAY;
    44  
    45  // The unsigned zero year for internal calculations.
    46  // Must be 1 mod 400, and times before it will not compute correctly,
    47  // but otherwise can be changed at will.
    48  const int64_t ABSOLUTE_ZERO_TS = -62135596800;
    49  
    50  inline __host__ __device__ bool isLeapYear(uint16_t year) {
    51    return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    52  }
    53  
    54  // For host, daysBeforeMonth will be DAYS_BEFORE_MONTH_HOST, otherwise
    55  // it should be DAYS_BEFORE_MONTH_DEVICE.
    56  inline __host__ __device__ uint16_t
    57  getDaysBeforeMonth(uint8_t month,
    58                     bool isLeapYear,
    59                     const uint16_t *daysBeforeMonth) {
    60    uint16_t days = daysBeforeMonth[month];
    61    if (isLeapYear && month >= 2) {
    62      days++;
    63    }
    64    return days;
    65  }
    66  
    67  // All following code refers to https://golang.org/src/time/time.go
    68  // Line 940. To save space, we only allowed ts >= 0, so time before 1970-01-01
    69  // is not supported.
    70  __host__ __device__
    71  uint32_t resolveTimeBucketizer(int64_t ts,
    72                                 enum TimeBucketizer timeBucketizer,
    73                                 const uint16_t *daysBeforeMonth) {
    74    // Adjust the timestamp to zero year for calculation.
    75    ts -= ABSOLUTE_ZERO_TS;
    76    uint32_t days = ts / SECONDS_PER_DAY;
    77  
    78    // 400 years cycle.
    79    int64_t n = days / DAYS_PER_400_YEARS;
    80    uint16_t year = 400 * n;
    81    int64_t start = n * DAYS_PER_400_YEARS * SECONDS_PER_DAY;
    82    days -= DAYS_PER_400_YEARS * n;
    83  
    84    // Cut off 100-year cycles.
    85    // The last cycle has one extra leap year, so on the last day
    86    // of that year, day / daysPer100Years will be 4 instead of 3.
    87    // Cut it back down to 3 by subtracting n>>2.
    88    n = days / DAYS_PER_100_YEARS;
    89    n -= n >> 2;
    90    year += 100 * n;
    91    start += n * DAYS_PER_100_YEARS * SECONDS_PER_DAY;
    92    days -= DAYS_PER_100_YEARS * n;
    93  
    94    // Cut off 4-year cycles.
    95    // The last cycle has a missing leap year, which does not
    96    // affect the computation.
    97    n = days / DAYS_PER_4_YEARS;
    98    year += 4 * n;
    99    start += n * DAYS_PER_4_YEARS * SECONDS_PER_DAY;
   100    days -= DAYS_PER_4_YEARS * n;
   101  
   102  
   103    // Cut off years within a 4-year cycle.
   104    // The last year is a leap year, so on the last day of that year,
   105    // day / 365 will be 4 instead of 3. Cut it back down to 3
   106    // by subtracting n>>2.
   107    n = days / 365;
   108    n -= n >> 2;
   109    year += n;
   110    days -= 365 * n;
   111    start += n * 365 * SECONDS_PER_DAY;
   112  
   113    // Adjust back;
   114    start += ABSOLUTE_ZERO_TS;
   115  
   116    // get day of the year.
   117    if (timeBucketizer == YEAR) {
   118      return start;
   119    }
   120  
   121    // day of the year.
   122    if (timeBucketizer == DAY_OF_YEAR) {
   123      return days;
   124    }
   125  
   126    bool leapYear = isLeapYear(year + 1);
   127    // Estimate month on assumption that every month has 31 days.
   128    // The estimate may be too low by at most one month, so adjust.
   129    uint8_t month = days / 31;
   130    uint16_t monthEnd = getDaysBeforeMonth(month + 1, leapYear, daysBeforeMonth);
   131    if (days >= monthEnd) {
   132      month++;
   133    }
   134  
   135    if (timeBucketizer == MONTH || timeBucketizer == DAY_OF_MONTH) {
   136      uint32_t
   137          dayBeforeMonth = getDaysBeforeMonth(month, leapYear, daysBeforeMonth);
   138      // month start.
   139      if (timeBucketizer == MONTH) {
   140        return start + dayBeforeMonth * SECONDS_PER_DAY;
   141      }
   142      // day of month.
   143      return days - dayBeforeMonth;
   144    }
   145  
   146    // month of year.
   147    if (timeBucketizer == MONTH_OF_YEAR) {
   148      return month;
   149    }
   150  
   151    int quarter = month / 3;
   152  
   153    // quarter of year.
   154    if (timeBucketizer == QUARTER_OF_YEAR) {
   155      return quarter;
   156    }
   157  
   158    // quarter start.
   159    return start + getDaysBeforeMonth(quarter * 3, leapYear, daysBeforeMonth)
   160        * SECONDS_PER_DAY;
   161  }
   162  
   163  // The function overload for resolveTimeBucketizer with daysBeforeMonth provided
   164  // according to the running environment.
   165  __host__ __device__
   166  thrust::tuple<uint32_t, bool> resolveTimeBucketizer(
   167      const thrust::tuple<uint32_t, bool> t,
   168      enum TimeBucketizer timeBucketizer) {
   169    if (!thrust::get<1>(t)) {
   170      return thrust::make_tuple(0, false);
   171    }
   172  
   173    // Choose daysBeforeMonth based on whether it's running on device.
   174    const uint16_t *daysBeforeMonth;
   175  #ifdef RUN_ON_DEVICE
   176    daysBeforeMonth = DAYS_BEFORE_MONTH_DEVICE;
   177  #else
   178    // Have to allocate this array and assign values in stack as even we
   179    // compile in host mode, nvcc still thinks it's a device function as we
   180    // annotate it with __device__.
   181    uint16_t daysBeforeMonthHost[13] = {
   182        0,
   183        31,
   184        31 + 28,
   185        31 + 28 + 31,
   186        31 + 28 + 31 + 30,
   187        31 + 28 + 31 + 30 + 31,
   188        31 + 28 + 31 + 30 + 31 + 30,
   189        31 + 28 + 31 + 30 + 31 + 30 + 31,
   190        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
   191        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
   192        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
   193        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
   194        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
   195    };
   196    daysBeforeMonth = daysBeforeMonthHost;
   197  #endif
   198  
   199    return thrust::make_tuple(
   200        resolveTimeBucketizer(
   201            thrust::get<0>(t), timeBucketizer, daysBeforeMonth), true);
   202  }
   203  
   204  // The function is used to get the start of week timestamp based on
   205  // the given timestamp, will return 0 if ts is before 1970/1/1
   206  __host__ __device__
   207  uint32_t getWeekStartTimestamp(uint32_t ts) {
   208      if (ts < SECONDS_PER_FOUR_DAYS) {
   209          return 0;
   210      }
   211      return ts - (ts - SECONDS_PER_FOUR_DAYS) % SECONDS_PER_WEEK;
   212  }
   213  
   214  __host__ __device__
   215  thrust::tuple<uint32_t, bool> GetWeekStartFunctor::operator()(
   216      const thrust::tuple<uint32_t, bool> t) const {
   217      if (!thrust::get<1>(t)) {
   218        return thrust::make_tuple(0, false);
   219      }
   220      return thrust::make_tuple(getWeekStartTimestamp(thrust::get<0>(t)), true);
   221  }
   222  
   223  __host__ __device__
   224  thrust::tuple<uint32_t, bool> GetMonthStartFunctor::operator()(
   225      const thrust::tuple<uint32_t, bool> t) const {
   226    return resolveTimeBucketizer(t, MONTH);
   227  }
   228  
   229  __host__ __device__
   230  thrust::tuple<uint32_t, bool> GetQuarterStartFunctor::operator()(
   231      const thrust::tuple<uint32_t, bool> t) const {
   232    return resolveTimeBucketizer(t, QUATER);
   233  }
   234  
   235  __host__ __device__
   236  thrust::tuple<uint32_t, bool> GetYearStartFunctor::operator()(
   237      const thrust::tuple<uint32_t, bool> t) const {
   238    return resolveTimeBucketizer(t, YEAR);
   239  }
   240  
   241  __host__ __device__
   242  thrust::tuple<uint32_t, bool> GetDayOfMonthFunctor::operator()(
   243      const thrust::tuple<uint32_t, bool> t) const {
   244    return resolveTimeBucketizer(t, DAY_OF_MONTH);
   245  }
   246  
   247  __host__ __device__
   248  thrust::tuple<uint32_t, bool> GetDayOfYearFunctor::operator()(
   249      const thrust::tuple<uint32_t, bool> t) const {
   250    return resolveTimeBucketizer(t, DAY_OF_YEAR);
   251  }
   252  
   253  __host__ __device__
   254  thrust::tuple<uint32_t, bool> GetMonthOfYearFunctor::operator()(
   255      const thrust::tuple<uint32_t, bool> t) const {
   256    return resolveTimeBucketizer(t, MONTH_OF_YEAR);
   257  }
   258  
   259  __host__ __device__
   260  thrust::tuple<uint32_t, bool> GetQuarterOfYearFunctor::operator()(
   261      const thrust::tuple<uint32_t, bool> t) const {
   262    return resolveTimeBucketizer(t, QUARTER_OF_YEAR);
   263  }
   264  }  // namespace ares