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

PERF: Let SpatialObject directly access m_ObjectToWorldTransformInverse #4435

Conversation

N-Dekker
Copy link
Contributor

Replaced this->GetObjectToWorldTransformInverse() calls with direct access to m_ObjectToWorldTransformInverse. A performance improvement greater than 6% was observed, from more than 0.74 sec. down to less than 0.69 sec, when calling IsInsideInWorldSpace 2^25 times (as tested on VS2019 Release).

@github-actions github-actions bot added type:Performance Improvement in terms of compilation or execution time area:Core Issues affecting the Core module labels Jan 26, 2024
@N-Dekker N-Dekker requested a review from aylward January 26, 2024 15:04
@N-Dekker N-Dekker marked this pull request as ready for review January 26, 2024 15:04
@aylward
Copy link
Member

aylward commented Jan 26, 2024

Not ideal.

  1. You are not guaranteed to have a valid inverse value unless you check the mtimes.
  2. ITK's design pattern is not to directly access parent member vars for this reason. So, the pattern used here isn't consistent with the rest of ITK.

How about instead still creating a function call to access it, but eliminate the test for mtimes in that function. The function name should make it obvious that the variable is being assumed to be valid. Perhaps

GetLastValidObjectToWorldTransformInverse()

or

GetObjectToWorldTransformInverseWithoutVerification()

or

such.

@aylward
Copy link
Member

aylward commented Jan 26, 2024

@dzenanz - what is the recommended pattern for this situation?

@aylward
Copy link
Member

aylward commented Jan 26, 2024

Ah - I see this is all within the base class - not derived. So my comment on derived class isn't valid, but it still holds that the inverse might not be valid without the mtime test.

s

@N-Dekker
Copy link
Contributor Author

Thanks for your feedback, @aylward m_ObjectToWorldTransformInverse is kept up-to-date with each SetObjectToWorldTransform call:

SpatialObject<TDimension>::SetObjectToWorldTransform(const TransformType * transform)
{
if (!transform->GetInverse(m_ObjectToWorldTransformInverse))
{
itkExceptionMacro("Transform must be invertible.");
}

As well as by ProtectedComputeObjectToWorldTransform() (which is called by SpatialObject::Update()):

SpatialObject<TDimension>::ProtectedComputeObjectToWorldTransform()
{
m_ObjectToWorldTransform->SetFixedParameters(this->GetObjectToParentTransform()->GetFixedParameters());
m_ObjectToWorldTransform->SetParameters(this->GetObjectToParentTransform()->GetParameters());
if (this->HasParent())
{
m_ObjectToWorldTransform->Compose(this->GetParent()->GetObjectToWorldTransform(), false);
}
if (!m_ObjectToWorldTransform->GetInverse(m_ObjectToWorldTransformInverse))

That's why I think m_ObjectToWorldTransformInverse is kept up-to-date well enough 🤷

@N-Dekker
Copy link
Contributor Author

My previous pull request proposes a different approach:

One of the ideas behind PR #4434 is to take those relatively expensive GetObjectToWorldTransformInverse() calls out of the pixel-by-pixel loop, and store a pointer to that inverse transform in a local instance of the proposed WorldSpaceContext class, before going into the loop.

So this PR (#4435) kind-of competes with my other PR, #4434 😃 If IsInsideInWorldSpace would no longer call GetObjectToWorldTransformInverse(), the proposed WorldSpaceContext class would become less interesting. What do you think?

@aylward
Copy link
Member

aylward commented Jan 26, 2024

This is outstanding!

I like the approach in #4434 - very nice!

Perhaps just add a comment to those worldspace functions that they assume that the inverse is up to date. Then we'll cross our fingers and hope some third party hasn't done something that breaks. As you said, it should be up to date.

Thanks!

@N-Dekker
Copy link
Contributor Author

@aylward Thanks! If this PR passes the CI, I'll add a note to each of the four adjusted member functions (DerivativeAtInWorldSpace, IsInsideInWorldSpace, IsEvaluableAtInWorldSpace, ValueAtInWorldSpace) saying something like:

This member function assumes that the internal ObjectToWorldTransformInverse transform is up-to-date. This transform may be updated explicitly by calling GetObjectToWorldTransformInverse(), Update(), or SetObjectToWorldTransform(transform)

OK?

@aylward
Copy link
Member

aylward commented Jan 26, 2024

Perfect!

Replaced `this->GetObjectToWorldTransformInverse()` calls with direct access to
m_ObjectToWorldTransformInverse. A performance improvement greater than 6% was
observed, from more than 0.74 sec. down to less than 0.69 sec, when calling
`IsInsideInWorldSpace` 2^25 times (as tested on VS2019 Release).
@N-Dekker N-Dekker force-pushed the SpatialObject-m_ObjectToWorldTransformInverse branch from 4b6ffe2 to 4fe1aa4 Compare January 26, 2024 16:50
@dzenanz dzenanz merged commit 1e0475c into InsightSoftwareConsortium:master Jan 31, 2024
12 checks passed
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Feb 11, 2024
Replaced `SpatialObject::GetObjectToWorldTransformInverse()` calls within the
implementation of SpatialObject with direct access to its own
`m_ObjectToWorldTransform` data member.

Follow-up to pull request InsightSoftwareConsortium#4435
commit 1e0475c "PERF: Let SpatialObject
directly access m_ObjectToWorldTransformInverse"

(However, in this case, no significant performance improvement is expected, so
it's just a matter of style.)
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Feb 11, 2024
Replaced `SpatialObject::GetObjectToWorldTransform()` calls within the
implementation of SpatialObject with direct access to its own
`m_ObjectToWorldTransform` data member.

Follow-up to pull request InsightSoftwareConsortium#4435
commit 1e0475c "PERF: Let SpatialObject
directly access m_ObjectToWorldTransformInverse"

(However, in this case, no significant performance improvement is expected, so
it's just a matter of style.)
N-Dekker added a commit to SuperElastix/elastix that referenced this pull request Apr 9, 2024
Upgraded ITK on the CI to version 5.4rc03: https://github.com/InsightSoftwareConsortium/ITK/releases/tag/v5.4rc03 (commits from 2023-10-10 to 2024-04-04: InsightSoftwareConsortium/ITK@v5.4rc02...v5.4rc03)

Including major performance improvements of `SpatialObject.IsInsideInWorldSpace(point)`:
 * InsightSoftwareConsortium/ITK#4435 "Let SpatialObject directly access m_ObjectToWorldTransformInverse"
 * InsightSoftwareConsortium/ITK#4440 "Add SpatialObject IsInsideInWorldSpace(const PointType &) overload"
 * InsightSoftwareConsortium/ITK#4441 "Make `m_ObjectToWorldTransformInverse->TransformPoint` non-virtual"
 * InsightSoftwareConsortium/ITK#4450 "Let `ImageSpatialObject` update the image regions of its base class, let `ImageMaskSpatialObject` use these image regions"

Follow-up to pull request #982 commit 5f0940d
N-Dekker added a commit to SuperElastix/elastix that referenced this pull request Apr 9, 2024
Upgraded ITK on the CI to version 5.4rc03: https://github.com/InsightSoftwareConsortium/ITK/releases/tag/v5.4rc03 (commits from 2023-10-10 to 2024-04-04: InsightSoftwareConsortium/ITK@v5.4rc02...v5.4rc03)

Including major performance improvements of `SpatialObject.IsInsideInWorldSpace(point)`:
 * InsightSoftwareConsortium/ITK#4435 "Let SpatialObject directly access m_ObjectToWorldTransformInverse"
 * InsightSoftwareConsortium/ITK#4440 "Add SpatialObject IsInsideInWorldSpace(const PointType &) overload"
 * InsightSoftwareConsortium/ITK#4441 "Make `m_ObjectToWorldTransformInverse->TransformPoint` non-virtual"
 * InsightSoftwareConsortium/ITK#4450 "Let `ImageSpatialObject` update the image regions of its base class, let `ImageMaskSpatialObject` use these image regions"

Follow-up to pull request #982 commit 5f0940d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:Core Issues affecting the Core module type:Performance Improvement in terms of compilation or execution time
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants