-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom kernel for converting timestamps b/n UTC and non-UTC non-DST timezones #1553
Changes from 19 commits
616e368
ac5aa18
8c53cce
c87f7fc
558b882
e33bb3a
ca8502a
3a22b6d
10476bc
2f0f32a
3094fe7
5b7f09e
d78159a
8b016b7
058c5cd
9d71bd4
21f7364
2c13b6d
7afbf1c
8db2924
0da8e78
0aadc89
7a54d34
8c4ddc9
e68beae
52ae251
255cb23
5588d06
a190226
1b76f84
6b4a496
7355eb4
44ce6ae
cf0449c
965e92a
c3bfa14
a165262
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* Copyright (c) 2023, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* 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. | ||
*/ | ||
|
||
#include "cudf_jni_apis.hpp" | ||
#include "timezones.hpp" | ||
|
||
extern "C" { | ||
|
||
JNIEXPORT jlong JNICALL Java_com_nvidia_spark_rapids_jni_GpuTimeZoneDB_convertTimestampColumnToUTC( | ||
JNIEnv *env, jclass, jlong input_handle, jlong transitions_handle, jint tz_index) { | ||
JNI_NULL_CHECK(env, input_handle, "column is null", 0); | ||
JNI_NULL_CHECK(env, transitions_handle, "column is null", 0); | ||
try { | ||
cudf::jni::auto_set_device(env); | ||
auto input = reinterpret_cast<cudf::column_view const*>(input_handle); | ||
auto transitions = reinterpret_cast<cudf::table_view const*>(transitions_handle); | ||
auto index = static_cast<cudf::size_type>(tz_index); | ||
return cudf::jni::ptr_as_jlong( | ||
spark_rapids_jni::convert_timestamp_to_utc(*input, *transitions, index).release()); | ||
} | ||
CATCH_STD(env, 0); | ||
} | ||
|
||
JNIEXPORT jlong JNICALL Java_com_nvidia_spark_rapids_jni_GpuTimeZoneDB_convertUTCTimestampColumnToTimeZone( | ||
JNIEnv *env, jclass, jlong input_handle, jlong transitions_handle, jint tz_index) { | ||
JNI_NULL_CHECK(env, input_handle, "column is null", 0); | ||
JNI_NULL_CHECK(env, transitions_handle, "column is null", 0); | ||
try { | ||
cudf::jni::auto_set_device(env); | ||
auto input = reinterpret_cast<cudf::column_view const*>(input_handle); | ||
auto transitions = reinterpret_cast<cudf::table_view const*>(transitions_handle); | ||
auto index = static_cast<cudf::size_type>(tz_index); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, |
||
return cudf::jni::ptr_as_jlong( | ||
spark_rapids_jni::convert_utc_timestamp_to_timezone(*input, *transitions, index).release()); | ||
} | ||
CATCH_STD(env, 0); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,201 @@ | ||||||
/* | ||||||
* Copyright (c) 2023, NVIDIA CORPORATION. | ||||||
* | ||||||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
* you may not use this file except in compliance with the License. | ||||||
* You may obtain a copy of the License at | ||||||
* | ||||||
* http://www.apache.org/licenses/LICENSE-2.0 | ||||||
* | ||||||
* 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. | ||||||
*/ | ||||||
|
||||||
#include "timezones.hpp" | ||||||
|
||||||
#include <cudf/column/column.hpp> | ||||||
#include <cudf/column/column_device_view.cuh> | ||||||
#include <cudf/column/column_factories.hpp> | ||||||
#include <cudf/detail/null_mask.hpp> | ||||||
#include <cudf/lists/lists_column_device_view.cuh> | ||||||
#include <cudf/lists/list_device_view.cuh> | ||||||
#include <cudf/table/table.hpp> | ||||||
#include <cudf/types.hpp> | ||||||
|
||||||
#include <rmm/cuda_stream_view.hpp> | ||||||
#include <rmm/exec_policy.hpp> | ||||||
|
||||||
#include <thrust/binary_search.h> | ||||||
|
||||||
using column = cudf::column; | ||||||
using column_device_view = cudf::column_device_view; | ||||||
using lists_column_device_view = cudf::detail::lists_column_device_view; | ||||||
using size_type = cudf::size_type; | ||||||
using struct_view = cudf::struct_view; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: This can be phrased more simply: using cudf::column;
using cudf::column_device_view;
// etc. |
||||||
|
||||||
namespace { | ||||||
|
||||||
/** | ||||||
* @brief adjust timestamp value by offset | ||||||
* | ||||||
* @tparam typestamp_type type of the input and output timestamp | ||||||
* @param timestamp input timestamp | ||||||
* @param transitions the transitions | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing two more |
||||||
*/ | ||||||
template <typename timestamp_type> | ||||||
mythrocks marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
__device__ timestamp_type convert_timestamp_timezone(timestamp_type const& timestamp, | ||||||
lists_column_device_view const& transitions, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you convert this to a struct/functor, you could eliminate the wrapping lambdas at the calls to thrust::transform and the counting iterator. eg:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually had this way before, but for some reason it was not the easiest code to debug. I couldn't step through the code or set breakpoints properly with (cuda-)gdb. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, cuda-gdb falls apart pretty quickly. |
||||||
size_type tz_index, | ||||||
bool to_utc) { | ||||||
|
||||||
using duration_type = typename timestamp_type::duration; | ||||||
using cuda::std::chrono::duration_cast; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not type alias any name having There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this specifically error prone? |
||||||
|
||||||
auto epoch_seconds = static_cast<int64_t>(duration_cast<cudf::duration_s>(timestamp.time_since_epoch()).count()); | ||||||
|
||||||
auto const tz_transitions = cudf::list_device_view{transitions, tz_index}; | ||||||
|
||||||
auto size = tz_transitions.size(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
|
||||||
cudf::device_span<int64_t const> transition_times( | ||||||
&(transitions.child().child(to_utc ? 1 : 0).data<int64_t>()[tz_transitions.element_offset(0)]), | ||||||
static_cast<size_t>(size)); | ||||||
|
||||||
|
||||||
auto idx = thrust::upper_bound(thrust::seq, | ||||||
transition_times.begin(), | ||||||
transition_times.end(), | ||||||
epoch_seconds) - transition_times.begin(); | ||||||
|
||||||
auto const list_offset = tz_transitions.element_offset(size_type(idx-1)); | ||||||
auto const utc_offset = duration_cast<duration_type>( | ||||||
cudf::duration_s{static_cast<int64_t>( | ||||||
transitions.child().child(2).element<int32_t>(list_offset) | ||||||
)}); | ||||||
return to_utc ? timestamp - utc_offset : timestamp + utc_offset; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If if constexpr (to_utc) {
return timestamp - utc_offset;
}
else {
return timestamp + utc_offset;
} Verbose, but one branch would get eliminated. |
||||||
} | ||||||
|
||||||
} | ||||||
|
||||||
namespace spark_rapids_jni { | ||||||
|
||||||
std::unique_ptr<column> convert_timestamp_to_utc(cudf::column_view const& input, | ||||||
cudf::table_view const& transitions, | ||||||
size_type tz_index, | ||||||
rmm::cuda_stream_view stream, | ||||||
rmm::mr::device_memory_resource* mr) { | ||||||
|
||||||
auto type = input.type().id(); | ||||||
auto num_rows = input.size(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
|
||||||
// get the fixed transitions | ||||||
auto const ft_cdv_ptr = column_device_view::create(transitions.column(0), stream); | ||||||
lists_column_device_view fixed_transitions = cudf::detail::lists_column_device_view{*ft_cdv_ptr}; | ||||||
NVnavkumar marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
auto results = cudf::make_timestamp_column(input.type(), input.size(), | ||||||
cudf::detail::copy_bitmask(input, stream, mr), | ||||||
input.null_count(), stream, mr); | ||||||
|
||||||
switch (type) { | ||||||
case cudf::type_id::TIMESTAMP_SECONDS: | ||||||
thrust::transform(rmm::exec_policy(stream), | ||||||
thrust::make_counting_iterator(0), | ||||||
thrust::make_counting_iterator(num_rows), | ||||||
results->mutable_view().begin<cudf::timestamp_s>(), | ||||||
[input_data = input.begin<cudf::timestamp_s>(), fixed_transitions, tz_index] __device__ (auto const i) { | ||||||
auto const timestamp = input_data[i]; | ||||||
return convert_timestamp_timezone<cudf::timestamp_s>(timestamp, fixed_transitions, tz_index, true); | ||||||
} | ||||||
); | ||||||
break; | ||||||
case cudf::type_id::TIMESTAMP_MILLISECONDS: | ||||||
thrust::transform(rmm::exec_policy(stream), | ||||||
thrust::make_counting_iterator(0), | ||||||
thrust::make_counting_iterator(num_rows), | ||||||
results->mutable_view().begin<cudf::timestamp_ms>(), | ||||||
[input_data = input.begin<cudf::timestamp_ms>(), fixed_transitions, tz_index] __device__ (auto const i) { | ||||||
auto const timestamp = input_data[i]; | ||||||
return convert_timestamp_timezone<cudf::timestamp_ms>(timestamp, fixed_transitions, tz_index, true); | ||||||
} | ||||||
); | ||||||
break; | ||||||
case cudf::type_id::TIMESTAMP_MICROSECONDS: | ||||||
thrust::transform(rmm::exec_policy(stream), | ||||||
thrust::make_counting_iterator(0), | ||||||
thrust::make_counting_iterator(num_rows), | ||||||
results->mutable_view().begin<cudf::timestamp_us>(), | ||||||
[input_data = input.begin<cudf::timestamp_us>(), fixed_transitions, tz_index] __device__ (auto const i) { | ||||||
auto const timestamp = input_data[i]; | ||||||
return convert_timestamp_timezone<cudf::timestamp_us>(timestamp, fixed_transitions, tz_index, true); | ||||||
} | ||||||
); | ||||||
break; | ||||||
default: | ||||||
CUDF_FAIL("Unsupported timestamp unit for timezone conversion"); | ||||||
} | ||||||
|
||||||
|
||||||
return results; | ||||||
} | ||||||
|
||||||
std::unique_ptr<column> convert_utc_timestamp_to_timezone(cudf::column_view const& input, | ||||||
cudf::table_view const& transitions, | ||||||
size_type tz_index, | ||||||
rmm::cuda_stream_view stream, | ||||||
rmm::mr::device_memory_resource* mr) { | ||||||
|
||||||
auto const type = input.type().id(); | ||||||
auto num_rows = input.size(); | ||||||
|
||||||
// get the fixed transitions | ||||||
auto const ft_cdv_ptr = column_device_view::create(transitions.column(0), stream); | ||||||
lists_column_device_view fixed_transitions = cudf::detail::lists_column_device_view{*ft_cdv_ptr}; | ||||||
|
||||||
auto results = cudf::make_timestamp_column(input.type(), input.size(), | ||||||
cudf::detail::copy_bitmask(input, stream, mr), | ||||||
input.null_count(), stream, mr); | ||||||
|
||||||
switch (type) { | ||||||
case cudf::type_id::TIMESTAMP_SECONDS: | ||||||
thrust::transform(rmm::exec_policy(stream), | ||||||
thrust::make_counting_iterator(0), | ||||||
thrust::make_counting_iterator(num_rows), | ||||||
results->mutable_view().begin<cudf::timestamp_s>(), | ||||||
[input_data = input.begin<cudf::timestamp_s>(), fixed_transitions, tz_index] __device__ (auto const i) { | ||||||
auto const timestamp = input_data[i]; | ||||||
return convert_timestamp_timezone<cudf::timestamp_s>(timestamp, fixed_transitions, tz_index, false); | ||||||
} | ||||||
); | ||||||
break; | ||||||
case cudf::type_id::TIMESTAMP_MILLISECONDS: | ||||||
thrust::transform(rmm::exec_policy(stream), | ||||||
thrust::make_counting_iterator(0), | ||||||
thrust::make_counting_iterator(num_rows), | ||||||
results->mutable_view().begin<cudf::timestamp_ms>(), | ||||||
[input_data = input.begin<cudf::timestamp_ms>(), fixed_transitions, tz_index] __device__ (auto const i) { | ||||||
auto const timestamp = input_data[i]; | ||||||
return convert_timestamp_timezone<cudf::timestamp_ms>(timestamp, fixed_transitions, tz_index, false); | ||||||
} | ||||||
); | ||||||
break; | ||||||
case cudf::type_id::TIMESTAMP_MICROSECONDS: | ||||||
thrust::transform(rmm::exec_policy(stream), | ||||||
thrust::make_counting_iterator(0), | ||||||
thrust::make_counting_iterator(num_rows), | ||||||
results->mutable_view().begin<cudf::timestamp_us>(), | ||||||
[input_data = input.begin<cudf::timestamp_us>(), fixed_transitions, tz_index] __device__ (auto const i) { | ||||||
auto const timestamp = input_data[i]; | ||||||
return convert_timestamp_timezone<cudf::timestamp_us>(timestamp, fixed_transitions, tz_index, false); | ||||||
} | ||||||
); | ||||||
break; | ||||||
default: | ||||||
CUDF_FAIL("Unsupported timestamp unit for timezone conversion"); | ||||||
} | ||||||
return results; | ||||||
} | ||||||
|
||||||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,64 @@ | ||||
/* | ||||
* Copyright (c) 2023, NVIDIA CORPORATION. | ||||
* | ||||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||||
* you may not use this file except in compliance with the License. | ||||
* You may obtain a copy of the License at | ||||
* | ||||
* http://www.apache.org/licenses/LICENSE-2.0 | ||||
* | ||||
* 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. | ||||
*/ | ||||
|
||||
#include <cudf/column/column_view.hpp> | ||||
#include <cudf/table/table.hpp> | ||||
#include <cudf/utilities/default_stream.hpp> | ||||
#include <rmm/cuda_stream_view.hpp> | ||||
|
||||
#include <cstddef> | ||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
namespace spark_rapids_jni { | ||||
|
||||
/** | ||||
* @brief Convert input column timestamps in current timezone to UTC | ||||
* | ||||
* The transition rules are in enclosed in a table, and the index corresponding to the | ||||
* current timezone is given. | ||||
* | ||||
* @param input the column of input timestamps in the current timezone | ||||
* @param transitions the table of transitions for all timezones | ||||
* @param tz_index the index of the row in `transitions` corresponding to the current timezone | ||||
* @param stream CUDA stream used for device memory operations and kernel launches. | ||||
* @param mr Device memory resource used to allocate the returned timestamp column's memory | ||||
*/ | ||||
std::unique_ptr<cudf::column> convert_timestamp_to_utc( | ||||
cudf::column_view const& input, | ||||
cudf::table_view const& transitions, | ||||
cudf::size_type tz_index, | ||||
rmm::cuda_stream_view stream = cudf::get_default_stream(), | ||||
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); | ||||
|
||||
/** | ||||
* @brief Convert input column timestamps in UTC to specified timezone | ||||
* | ||||
* The transition rules are in enclosed in a table, and the index corresponding to the | ||||
* specific timezone is given. | ||||
* | ||||
* @param input the column of input timestamps in UTC | ||||
* @param transitions the table of transitions for all timezones | ||||
* @param tz_index the index of the row in `transitions` corresponding to the specific timezone | ||||
* @param stream CUDA stream used for device memory operations and kernel launches. | ||||
* @param mr Device memory resource used to allocate the returned timestamp column's memory | ||||
*/ | ||||
std::unique_ptr<cudf::column> convert_utc_timestamp_to_timezone( | ||||
cudf::column_view const& input, | ||||
cudf::table_view const& transitions, | ||||
cudf::size_type tz_index, | ||||
rmm::cuda_stream_view stream = cudf::get_default_stream(), | ||||
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); | ||||
|
||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is recommended to use
auto const
.