Change 20307 - Abandoned
BUG: Make valid calling with m_NumElements == 0 In this test call, m_Data is nullptr, and m_NumElements is 0, so clearly,&m_Data[0] is not a useful pointer, and with 0 elements, there is nothing to fill. This implements checking for zero element objects. Change-Id: I6b9ba9d6ddf05450485a88ffbff4f14568205f26
Author Oct 23, 2015 2:00 PM
Committer Oct 23, 2015 5:28 PM
Commit
b98992939a079af1eeb9fb5aeb6dd5e5c546daf1
Parent(s)
03b384c29f63b9d74181c0292a8f4be20fc9b5ce
Change-Id
I6b9ba9d6ddf05450485a88ffbff4f14568205f26
Owner
Uploader
Reviewers
Kitware Build Robot Luc Hermitte
David Cole
Project ITK
Branch master
Topic
Updated 3 years, 2 months ago

Code-Review
-1 Luc Hermitte
Verified
+1 Kitware Build Robot
Files
History
Hans J. Johnson
Oct 23, 2015

Uploaded patch set 1.

Kitware Build Robot
Oct 23, 2015
David Cole
Oct 23, 2015

Patch Set 1: Code-Review-2

No good. I was wondering if all the "const" were necessary. As it turns out, some of them are absolutely not even compile-able... ;-)

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\xutility(2633) : error C389 2: '_Dest' : you cannot assign to a variable that is const

At least for the fill one.

Hans J. Johnson
Oct 23, 2015

Uploaded patch set 2.

Hans J. Johnson
Oct 23, 2015

Patch Set 1:

David, Sorry. I uploaded the wrong patch. I fixed this before uploading, but my commit --amend had a typo.

David Cole
Oct 23, 2015

Patch Set 2: -Code-Review

No worries... However, end of the day now, and I don't have time to try it now. Maybe I can get to it tomorrow.....

Kitware Build Robot
Oct 23, 2015
Luc Hermitte
Oct 24, 2015

Patch Set 2: Code-Review-1

This test shall not be done. The for loops embedded in the STL algorithms are already doing it.

We should first test with just this->m_Data. If VC++ is OK with this in debug mode, then we have our correction. If not, then we should fall back to plain loops.

David Cole
Oct 24, 2015

Patch Set 2:

You cannot call std::fill_n with a nullptr for Debug VS 2013.

The implementation of fill_n looks like this:

template<class _OutIt,
class _Diff,
class _Ty> inline
_OutIt fill_n(_OutIt _Dest, _Diff _Count, const _Ty& _Val)
{ // copy _Val _Count times through [_Dest, ...)
_DEBUG_POINTER(_Dest);
return (_Fill_n(_Dest, _Count, _Val,
_Is_checked(_Dest)));
}

The _DEBUG_POINTER line is called unconditionally, and will throw this assert if the pointer is nullptr.

David Cole
Oct 24, 2015

Patch Set 2:

Why are you against doing a nullptr check or a check on the number of elements prior to calling fill_n?

If you are calling fill_n so frequently that you're concerned about adding a micro-smidge of time to each call, perhaps you're calling it too frequently...

David Cole
Oct 24, 2015

Patch Set 2:

If you want the fastest code possible, without doing the appropriate nullptr safety checks, then you must guarantee that the entire test suite never tries to execute a test case with a nullptr. So another solution would be simply NOT to execute tests that result in these calls for Debug Microsoft builds.

It is my contention that **if** there is a performance problem with code after simply adding "if (this->m_Data)" checks before fill and copy calls, that the performance problem is with the **callers** of these things.

Fill and copy effectively perform N assignment operations. If you cannot afford a single nullptr check when you are doing N assignment operations then you should write special case code to avoid the safety checks.

The rest of us would like to be alerted ASAP when we accidentally try to "fill" or "copy into" an empty container.

Hans J. Johnson
Oct 24, 2015

Patch Set 2:

David, I agree with ou completely.

I was trying to get this ball rolling. Will you please take over this patch set and change as necessary?

Luc Hermitte
Oct 25, 2015

Patch Set 2:

You cannot call std::fill_n with a nullptr for Debug VS 2013.The implementation of fill_n looks like this:template<class _OutIt, class _Diff, class _Ty> inline _OutIt fill_n(_OutIt _Dest, _Diff _Count, const _Ty& _Val) { // copy _Val _Count times through [_Dest, ...) _DEBUG_POINTER(_Dest); return (_Fill_n(_Dest, _Count, _Val, _Is_checked(_Dest))); }The _DEBUG_POINTER line is called unconditionally, and will throw this assert if the pointer is nullptr.

That's odd. I don't read such a precondition in the standard. Is it the same with std::copy()?


> Why are you against doing a nullptr check or a check on the number
> of elements prior to calling fill_n?
>
> If you are calling fill_n so frequently that you're concerned about
> adding a micro-smidge of time to each call, perhaps you're calling
> it too frequently...

I totally agree that such algorithms are often called to many times. and yet, when they are, they are called on pixels. This means they are sometimes called billion times. We don't need to add a test that we could easily avoid and still stay robust.

What we need is a fill_n implementation that have as a precondition: "ptr!=nullptr || size==0", and not "ptr!=nullptr".

If you want the fastest code possible, without doing the appropriate nullptr safety checks, then you must guarantee that the entire test suite never tries to execute a test case with a nullptr. So another solution would be simply NOT to execute tests that result in these calls for Debug Microsoft builds.

We simply need algorithms with less restrictive preconditions. For instance

// untested code
template <typename OutIt, typename Size, typename T>
OutIt itk::fill_n(OutIt start, Size size, T const& value) {
assert(start ||size == 0); // the test is not correct, just to give an idea of what it should look like
for ( ; size != 0 ; --size, ++start)
*start = value;
return start;
}


> It is my contention that **if** there is a performance problem with
> code after simply adding "if (this->m_Data)" checks before fill and
> copy calls, that the performance problem is with the **callers** of
> these things.

Somehow I agree. Unfortunately, we tend to call these functions too many times, i.e. once per pixel if not more. We have a lot of pixels.

Here it's easy to have a correct code (that'll pass tests with VC++ in Debug Mode) without this redundant branching. Why pay for this branching?


> Fill and copy effectively perform N assignment operations. If you
> cannot afford a single nullptr check when you are doing N
> assignment operations then you should write special case code to
> avoid the safety checks.

N is often small (< 16)

The rest of us would like to be alerted ASAP when we accidentally try to "fill" or "copy into" an empty container.

We don't need to here. However, if you consider that filling or copying an empty vector is a programming error, then we should document it and formalize the related precondition (assertion, or C++17 [[expect: size != 0]]).

Hans J. Johnson
Oct 30, 2015

Abandoned

This was taken up under a different patch set by Luc Hermitte

Powered by Gerrit Static Archive (2.12-1-g6f7dc21) |