Skip to content

Commit

Permalink
Implement Series.datetime.floor (#9571)
Browse files Browse the repository at this point in the history
  • Loading branch information
skirui-source authored Nov 17, 2021
1 parent 60380de commit e08ae9c
Show file tree
Hide file tree
Showing 9 changed files with 502 additions and 53 deletions.
93 changes: 92 additions & 1 deletion cpp/include/cudf/datetime.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, NVIDIA CORPORATION.
* Copyright (c) 2019-2021, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -378,5 +378,96 @@ std::unique_ptr<column> ceil_nanosecond(
column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest day
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<cudf::column> floor_day(
cudf::column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest hour
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<cudf::column> floor_hour(
cudf::column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest minute
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<cudf::column> floor_minute(
cudf::column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest second
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<cudf::column> floor_second(
cudf::column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest millisecond
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<column> floor_millisecond(
column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest microsecond
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<column> floor_microsecond(
column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

/**
* @brief Round down to the nearest nanosecond
*
* @param column cudf::column_view of the input datetime values
* @param mr Device memory resource used to allocate device memory of the returned column.
*
* @throw cudf::logic_error if input column datatype is not TIMESTAMP
* @return cudf::column of the same datetime resolution as the input column
*/
std::unique_ptr<column> floor_nanosecond(
column_view const& column,
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource());

} // namespace datetime
} // namespace cudf
199 changes: 162 additions & 37 deletions cpp/src/datetime/datetime_ops.cu
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ enum class datetime_component {
NANOSECOND
};

enum class rounding_kind { CEIL, FLOOR };

template <datetime_component Component>
struct extract_component_operator {
template <typename Timestamp>
Expand Down Expand Up @@ -88,32 +90,59 @@ struct extract_component_operator {
}
};

template <datetime_component COMPONENT>
struct ceil_timestamp {
// This functor takes the rounding type as runtime info and dispatches to the ceil/floor/round
// function.
template <typename DurationType>
struct RoundFunctor {
template <typename Timestamp>
CUDA_DEVICE_CALLABLE auto operator()(rounding_kind round_kind, Timestamp dt)
{
switch (round_kind) {
case rounding_kind::CEIL: return cuda::std::chrono::ceil<DurationType>(dt);
case rounding_kind::FLOOR: return cuda::std::chrono::floor<DurationType>(dt);
default: cudf_assert(false && "Unsupported rounding kind.");
}
__builtin_unreachable();
}
};

struct RoundingDispatcher {
rounding_kind round_kind;
datetime_component component;

RoundingDispatcher(rounding_kind round_kind, datetime_component component)
: round_kind(round_kind), component(component)
{
}

template <typename Timestamp>
CUDA_DEVICE_CALLABLE Timestamp operator()(Timestamp const ts) const
{
using namespace cuda::std::chrono;
// want to use this with D, H, T (minute), S, L (millisecond), U
switch (COMPONENT) {
switch (component) {
case datetime_component::DAY:
return time_point_cast<typename Timestamp::duration>(ceil<duration_D>(ts));
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_D>{}(round_kind, ts));
case datetime_component::HOUR:
return time_point_cast<typename Timestamp::duration>(ceil<duration_h>(ts));
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_h>{}(round_kind, ts));
case datetime_component::MINUTE:
return time_point_cast<typename Timestamp::duration>(ceil<duration_m>(ts));
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_m>{}(round_kind, ts));
case datetime_component::SECOND:
return time_point_cast<typename Timestamp::duration>(ceil<duration_s>(ts));
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_s>{}(round_kind, ts));
case datetime_component::MILLISECOND:
return time_point_cast<typename Timestamp::duration>(ceil<duration_ms>(ts));
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_ms>{}(round_kind, ts));
case datetime_component::MICROSECOND:
return time_point_cast<typename Timestamp::duration>(ceil<duration_us>(ts));
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_us>{}(round_kind, ts));
case datetime_component::NANOSECOND:
return time_point_cast<typename Timestamp::duration>(ceil<duration_ns>(ts));
default: cudf_assert(false && "Unexpected resolution");
return time_point_cast<typename Timestamp::duration>(
RoundFunctor<duration_ns>{}(round_kind, ts));
default: cudf_assert(false && "Unsupported datetime rounding resolution.");
}

return {};
__builtin_unreachable();
}
};

Expand Down Expand Up @@ -196,10 +225,11 @@ struct is_leap_year_op {
};

// Specific function for applying ceil/floor date ops
template <typename TransformFunctor>
struct dispatch_ceil {
struct dispatch_round {
template <typename Timestamp>
std::enable_if_t<cudf::is_timestamp<Timestamp>(), std::unique_ptr<cudf::column>> operator()(
rounding_kind round_kind,
datetime_component component,
cudf::column_view const& column,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr) const
Expand All @@ -221,7 +251,7 @@ struct dispatch_ceil {
column.begin<Timestamp>(),
column.end<Timestamp>(),
output->mutable_view().begin<Timestamp>(),
TransformFunctor{});
RoundingDispatcher{round_kind, component});

return output;
}
Expand Down Expand Up @@ -384,13 +414,14 @@ std::unique_ptr<column> add_calendrical_months(column_view const& timestamp_colu
}
}

template <datetime_component Component>
std::unique_ptr<column> ceil_general(column_view const& column,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr)
std::unique_ptr<column> round_general(rounding_kind round_kind,
datetime_component component,
column_view const& column,
rmm::cuda_stream_view stream,
rmm::mr::device_memory_resource* mr)
{
return cudf::type_dispatcher(
column.type(), dispatch_ceil<detail::ceil_timestamp<Component>>{}, column, stream, mr);
column.type(), dispatch_round{}, round_kind, component, column, stream, mr);
}

std::unique_ptr<column> extract_year(column_view const& column,
Expand Down Expand Up @@ -498,53 +529,147 @@ std::unique_ptr<column> extract_quarter(column_view const& column,
std::unique_ptr<column> ceil_day(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::DAY>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::DAY,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> ceil_hour(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::HOUR>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::HOUR,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> ceil_minute(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::MINUTE>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::MINUTE,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> ceil_second(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::SECOND>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::SECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> ceil_millisecond(column_view const& column,
rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::MILLISECOND>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::MILLISECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> ceil_microsecond(column_view const& column,
rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::MICROSECOND>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::MICROSECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> ceil_nanosecond(column_view const& column,
rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::ceil_general<detail::datetime_component::NANOSECOND>(
column, rmm::cuda_stream_default, mr);
return detail::round_general(detail::rounding_kind::CEIL,
detail::datetime_component::NANOSECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_day(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::DAY,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_hour(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::HOUR,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_minute(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::MINUTE,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_second(column_view const& column, rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::SECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_millisecond(column_view const& column,
rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::MILLISECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_microsecond(column_view const& column,
rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::MICROSECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> floor_nanosecond(column_view const& column,
rmm::mr::device_memory_resource* mr)
{
CUDF_FUNC_RANGE();
return detail::round_general(detail::rounding_kind::FLOOR,
detail::datetime_component::NANOSECOND,
column,
rmm::cuda_stream_default,
mr);
}

std::unique_ptr<column> extract_year(column_view const& column, rmm::mr::device_memory_resource* mr)
Expand Down
Loading

0 comments on commit e08ae9c

Please sign in to comment.