Skip to content
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

Floating point compressor: write tests that test special floating point cases #3384

Open
wants to merge 15 commits into
base: dev
Choose a base branch
from

Conversation

abigalekim
Copy link
Contributor

@abigalekim abigalekim commented Jul 22, 2022

Here, I write extra test cases that test special types of floating point numbers for the float scaling filter.

Test cases to write:

  • Zeros
  • Denormalized numbers
  • Infinity

TYPE: IMPROVEMENT
DESC: Floating point compressor additional testing

@shortcut-integration
Copy link

@abigalekim abigalekim requested a review from ihnorton July 22, 2022 05:40
@abigalekim abigalekim marked this pull request as draft July 22, 2022 05:42
@abigalekim abigalekim marked this pull request as ready for review July 22, 2022 18:43
@abigalekim abigalekim requested a review from jp-dark July 22, 2022 19:25
Copy link
Contributor

@jp-dark jp-dark left a comment

Choose a reason for hiding this comment

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

I think subnormals should be allowed. We can discuss this further if needed.

tiledb/sm/filter/float_scaling_filter.h Show resolved Hide resolved
tiledb/sm/filter/float_scaling_filter.cc Outdated Show resolved Hide resolved
Comment on lines 86 to 87
double scale = 2.53;
double offset = 0.138;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why test with just these specific values?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried playing with random values just now, and found out that the compiler optimizes the result out when the values are too large, causing a test failure that is not actually my float scaling filter codes fault, but the tests fault. I wish there was a way to compile tests with less than O3 but alas...

BTW, this is only problematic in the CPP API test suite, not the tile one.

I have submitted a change where the values are between -64 and 64, and this seems to pass. Perhaps we should investigate this further. I think I talked to Isaiah about this already and he just said to note that this is a lossy compressor...

Copy link
Contributor

Choose a reason for hiding this comment

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

the compiler optimizes the result out when the values are too large

That really doesn't sound right. Do you have specific values that were generated that we causing failures? (If not, it illustrates why using non-repeatable random numbers is bad for testing and using seedable, repeatable pseudorandom sequences is good for testing.)

"Too large" with scaling could mean that you're getting overflow, in which case the testing is correctly identifying a defect in the compressor, failing to recognize overflow and failing on compression, which is better than writing out bad data without a failure.

Copy link
Contributor

Choose a reason for hiding this comment

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

I checked the original PR #3243. It has an overflow/underflow defect. There's no checking at all for either condition. The reason the error is not showing up with fixed test parameters is that the data is within the realm of validity for the filter parameters.

A simple example. With 56-bit mantissas (double), scale = 1 and offset = 2^11, then data max_float - (2^10) will have an addition overflow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is it better that I check for overflow/underflow with https://en.cppreference.com/w/cpp/numeric/fenv/feclearexcept or manually?

test/src/unit-filter-pipeline.cc Outdated Show resolved Hide resolved
@abigalekim abigalekim requested a review from jp-dark July 26, 2022 03:17
Copy link
Contributor

@jp-dark jp-dark left a comment

Choose a reason for hiding this comment

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

There are a few implicit integer to floating-point compiler warnings that block compiling on clang when warnings-as-errors is on.

test/src/unit-filter-pipeline.cc Outdated Show resolved Hide resolved
test/src/unit-filter-pipeline.cc Outdated Show resolved Hide resolved
test/src/unit-filter-pipeline.cc Outdated Show resolved Hide resolved
test/src/unit-cppapi-float-scaling-filter.cc Show resolved Hide resolved
@@ -87,6 +90,19 @@ Status FloatScalingFilter::run_forward(
const T* part_data = static_cast<const T*>(i.data());
for (uint32_t j = 0; j < num_elems_in_part; ++j) {
T elem = part_data[j];

// We should only handle numbers that are either normalized or 0.
switch (std::fpclassify(elem)) {
Copy link
Member

@ihnorton ihnorton Aug 16, 2022

Choose a reason for hiding this comment

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

@abigalekim this check needs to happen after the computation, and we need to check that the classification of the input matches the classification of the output (nan -> nan, infinite -> infinite)

Copy link
Member

Choose a reason for hiding this comment

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

(per @eric-hughes-tiledb -- we also need to check for integer overflow, if not done already)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are addressing overflow in a different PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait, also how? We shouldn't store the input data, and since we're converting and storing as integers, it doesn't really make sense to support infinite/NaN numbers.

Copy link
Member

Choose a reason for hiding this comment

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

Check fpclassify(elem) == fpclassify( [the floating point expression inside round ] )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

because of this change, zeros and denormalized array tests fail. is this expected? should I make the change in this PR? There are other PRs coming up to address this specific change.

Copy link
Contributor

@eric-hughes-tiledb eric-hughes-tiledb left a comment

Choose a reason for hiding this comment

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

There remain major problems with this PR. The following are all blocking problems for me. I will continue to request changes on this PR until they're fixed.

  • There's no documentation about which tests do what. These tests are subtle enough that we need documentation, both for the tests and the test support classes.
    • The documentation needs to include inequalities that show that the generated data won't overflow if it's not supposed to overflow (or will overflow if that's what's being tested).
  • There's a large amount of copypasta. Even some of the comments are copypasta. It's a bad idea to make a maintenance burden in fresh code. Where there's copypasta, it needs to be turned into support functions or classes.
    • The constructor of a PRNG class will need a seed value as an argument.
    • For tests with slightly different tests inside basically the same loop, a lambda is appropriate to instantiate the difference.
  • Problems with random numbers to generate test cases:
    • Most importantly, there are tests that use non-repeatable seeds for PRNG. These are unacceptable in unit tests, exactly because they are non-repeatable. One of the consequences of this is that any reference to std::random_device needs to be eliminated.
      • Printing out a seed is not an acceptable substitute by itself. Perhaps in concert with other mechanisms to make replication reliable and straightforward it would be; I'd need to see it.
    • Continued use of std::mt19937, a 32-bit generator, to generate 64-length numbers. Use std::mt19937_64.
  • Non-C.41 use of set_option. The filter class already has a good constructor; use it.


f.set_option(TILEDB_SCALE_FLOAT_BYTEWIDTH, &byte_width)
.set_option(TILEDB_SCALE_FLOAT_FACTOR, &scale)
.set_option(TILEDB_SCALE_FLOAT_OFFSET, &offset);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do not use non-C.41 functions like set_option when a C.41 constructor is available. Generate PRNG-originated test values first and construct the filter object (with the full constructor) second.

f.set_option(TILEDB_SCALE_FLOAT_OFFSET, &offset);
INFO(
"Scale: " + std::to_string(scale) + ", Offset: " +
std::to_string(offset) + ", Byte Width: " + std::to_string(byte_width));
Copy link
Contributor

Choose a reason for hiding this comment

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

Printing out the seeds is not a substitute for using a fixed seed. Replication of an error would require manual intervention in a debugger or custom code, neither of which counts as "actually repeatable".

T dis_max = std::min(
std::numeric_limits<T>::max(),
static_cast<T>(std::numeric_limits<W>::max()));
std::uniform_real_distribution<T> dis(dis_min, dis_max);
Copy link
Contributor

Choose a reason for hiding this comment

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

This distribution has two problems.

  • It's going to overflow on generated values in all cases. The floating scale is in the interval [-64, 64], so value within [max/64,max] will overflow on multiplication. The offset adds to the complication. This all needs documentation with an inequality estimate showing that the generated values won't generate an overflow.
  • It's going to overflow on generated values for certain type arguments T and W, for example double and int8_t.

If you're not seeing failures, it's that you're getting lucky and not generating enough values. int dim_hi = 10 and that means only 100 values are being generated, which isn't enough to see problems.

@@ -3963,7 +3964,7 @@ TEST_CASE("Filter: Test encryption", "[filter][encryption]") {
}

template <typename FloatingType, typename IntType>
void testing_float_scaling_filter() {
void testing_float_scaling_filter(bool negative) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding an argument without using it?

@@ -3963,7 +3964,7 @@ TEST_CASE("Filter: Test encryption", "[filter][encryption]") {
}

template <typename FloatingType, typename IntType>
void testing_float_scaling_filter() {
void testing_float_scaling_filter(bool negative) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is extremely poorly named. What's it testing?
It needs both a new name and documentation.

}
}

TEMPLATE_TEST_CASE(
Copy link
Contributor

Choose a reason for hiding this comment

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

None of these tests belong in unit-filter-pipeline. They're not testing FilterPipeline


Tile tile;
Datatype t = Datatype::FLOAT32;
switch (sizeof(FloatingType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This switch statement is copypasta. Figure out how to get rid of it.

FilterPipeline pipeline;
ThreadPool tp(4);
CHECK(pipeline.add_filter(FloatScalingFilter()).ok());
pipeline.get_filter<FloatScalingFilter>()->set_option(
Copy link
Contributor

Choose a reason for hiding this comment

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

More non-C.41

@eric-hughes-tiledb
Copy link
Contributor

There remain major problems with this PR.

There's more. That's all I have time for this morning.

@ihnorton ihnorton removed the request for review from jp-dark February 17, 2023 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants