Skip to content

Conversation

@nirandaperera
Copy link
Contributor

Adding a preliminary impl for an if_else(cond: Datum, left: Datum, right: Datum) function. It works as follows,

def if_else(cond, left, right):
    for c, true_val, false_val in zip(cond, left, right):
        if c:
            yield true_val
        else:
            yield false_val

null values will be promoted to the output.

Current commit only provides only Array-Array-Array kernels for the function.

@github-actions
Copy link

@lidavidm lidavidm self-requested a review May 21, 2021 22:00
Copy link
Member

@lidavidm lidavidm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some quick initial feedback. I think the overall approach looks fine. Testing this thoroughly will be important.

For a different PR I added a DatumFromJSON to make it easier to test functions that accept a lot of permutations of scalar/array arguments. I could pull that out separately if that would make testing easier here. (That way you don't need a lot of overloads and boilerplate to test all the possible input combinations.)

return Status::OK();
}

template <typename Type, bool swap = false, typename Enable = void>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is swap doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea is to reuse the impl for the cases like, cond, left: Array, right: Scalar and cond, left: Sca;ar, right: Array. In the second scenario, I can swap left and right and invert the cond without changing the loop mechanism.


// nulls will be promoted as follows
// cond.val && (cond.data && left.val || ~cond.data && right.val)
Status promote_nulls(KernelContext* ctx, const ArrayData& cond, const ArrayData& left,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use CamelCase (PromoteNulls)

template <typename Type>
struct ResolveExec {
static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
if (batch.length == 0) return Status::OK();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need to check for this? You can assume you have three arguments.

};

template <typename Type, bool swap>
struct IfElseFunctor<Type, swap, enable_if_t<is_boolean_type<Type>::value>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: type_traits.h already has an enable_if_boolean

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same with enable_if_number etc)

const T* left_data = left.GetValues<T>(1);
int64_t offset = cond.offset;

// todo this can be improved by intrinsics. ex: _mm*_mask_store_e* (vmovdqa*)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would assume memcpy already does this for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean, load with mask?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I think I misunderstood the optimization you're thinking about. How would SIMD help here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Say, you first copy right to ouput. Then, cond becomes a mask to store left onto output. For that there are specialized SIMD instructions.
https://software.intel.com/sites/landingpage/IntrinsicsGuide/#cats=Store&text=mask_store&expand=5564,5566

So, we can drop the BitBlock objects, and remove all the loops and memcpy inside the while loop. We'd have to handle the alignment though.

}
};

void AddPrimitiveKernels(const std::shared_ptr<ScalarFunction>& scalar_function,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of these function names are a little generic, can we rename them to reflect that they're for IfElse? We might end up putting other similar kernels here.

@nirandaperera
Copy link
Contributor Author

Just some quick initial feedback. I think the overall approach looks fine. Testing this thoroughly will be important.

For a different PR I added a DatumFromJSON to make it easier to test functions that accept a lot of permutations of scalar/array arguments. I could pull that out separately if that would make testing easier here. (That way you don't need a lot of overloads and boilerplate to test all the possible input combinations.)

That would be great @lidavidm . If you could send me the PR# that would be great! :-)

@lidavidm
Copy link
Member

Just some quick initial feedback. I think the overall approach looks fine. Testing this thoroughly will be important.
For a different PR I added a DatumFromJSON to make it easier to test functions that accept a lot of permutations of scalar/array arguments. I could pull that out separately if that would make testing easier here. (That way you don't need a lot of overloads and boilerplate to test all the possible input combinations.)

That would be great @lidavidm . If you could send me the PR# that would be great! :-)

See #10386

@nirandaperera
Copy link
Contributor Author

I am closing this PR because there are some major refactors and it would be better to review them fresh.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants