Skip to content

Conversation

@Snektron
Copy link
Contributor

Proposed changes

This pull request builds on #3267 by proving the "validation" infrastructure, the means to compare a set of Outputs.

Design

The design of the validation infrastructure is relatively straight forward:

  • Each SIGNATURE should come with a validate() implementation, which should be implemented in a similar way that the other functions/types from testing.hpp are implemented.
  • validate() returns a ValidationReport, which is a structure that keeps all relevant information about comparing the tensors from two Outputs. Note that crucially, validate() should not do any reporting by itself. Rather, glue logic should be implemented by the user to turn ValidationReport into a relevant error message.
  • You can see this clue code for CK-Builder itself in testing_utils.hpp, its MatchesReference(). This functionality is relatively barebones right now, it will be expanded upon in a different PR to keep the scope of this one down.

Implementation

The comparison is done on the GPU (using an atomic for now), to keep tests relatively quick. Some notable items from this PR:

  • To help compare the tensors and with writing tests, I've written a generic function tensor_foreach which invokes a callback on every element of a tensor.
  • For that it was useful that the TensorDescriptor has a rank which is known at compile-time, so I've changed the implementation of TensorDescriptor for that. I felt like it was a better approach than keeping it dynamic, for multiple reasons:
    • This is C++ and we should use static typing where possible and useful. This way, we don't have to implement runtime assertions about the tensor rank.
    • We know already know the rank of tensors statically, as it can be derived from the SIGNATURE.
    • It simpifies the implementation of tensor_foreach and other comparison code.
  • There are a lot of new tests for validating the validation implementation, validating validation validation tests (Only 3 recursive levels though...). For a few of those functions, I felt like it would be useful to expose them to the user.
  • Doc comments everywhere.

This is the simplest most implementation for validation.
This implements very barebones Googletest integration by
implementing a custom Matcher which calls ckt::validate().
This is a better version of the existing hip_check_error
implementations. It has the following improvements:
- Throws exceptions. Thos way the underlying unit testing implementation
  can cleanly catch & report errors, instead of a test just exiting in a
  hostile way.
- Uses std::source_location for source locations, so that we don't have to
  use a macro for the root "checking" function.
- Allows us to define and catch different types of errors without
  additional required control flow.
This implementation just performs the comparison as if it was a
flat piece of memory, meaning including stride data. While this is
primarily done because its much simpler, checking strides is also
useful so that we can check that there were no out-of-bounds writes.

In the future, the implementation of compare_tensors may need to
change such that the "actual" and "expected" tensors can have a
different stride/layout.
The specialization of DataTypeToCK was missing for DataType::U8. This
commit also changes the test such that if there is a variant missing
in the future, we will get a warning/compile error.
CKB testing should include user-readable error messages if a tensor
fails to validate. For this purpose, ckt::validate now returns a
"ValidationReport" a set of "cases" which hold information about
how and why a particular check failed. The testing infrastructure
(see testing_utils.hpp) can then turn this metadata into readable
error messages. This mechanism is to be expanded.
This adds initial unit tests for CKB testing's validation utilities. This
mainly adds tests for the current version of ValidationReport, as well
as some utilities for performing the tests (which are also tested themselves).
This new version compares the tensors actually, rather than comparing just the
backing buffers. This should make it easier to gather coordinates, as well as
allow us to compare non-packed kernels.
This nets us a few advantages:
- More static validation of the rank (we don't need to explicitly check).
- Easier metaprogramming in validation kernels.

There are also some disadvantages though:
- More metaprogramming in general.
- Harder to use everywhere else.
Putting this code into a separate header allows us to reuse it for data generation
and more validation stuff.
Now that we have this sort of abstract implementation, we can
re-use this implementation.
This frees up the Extent name for use with the TensorDescriptor. Since
it will be used to create extents like ckt::Extent{1, 2, 3}, it makes
sense to use this sorter name for that. The FilterExtent typename will
mostly be used in contexts where it can be inferred, so its not so its
better to use a longer name there.
This functionality is useful outside of unit_validation.cpp, so move
it to a more common location.
…tor.hpp

At this point the tensor descriptor stuff has become larger than the actual
tensor buffer stuff, so lets move it into a separate file.
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