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