-
-
Notifications
You must be signed in to change notification settings - Fork 888
Use GetPixelRowSpan to access pixel data in Histogram equalization #1431
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
Conversation
| { | ||
| // TODO: We should bulk convert here. | ||
| ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); | ||
| TPixel pixel = pixelRow[x]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can still use ref here and avoid the double access.
| { | ||
| for (int dy = yStart; dy < yEnd; dy++) | ||
| { | ||
| int dyOffSet = dy * sourceWidth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me like int dyOffSet = dy * sourceWidth; (and the same below) were the actual source of the issue and we could have mitigated the issue with rows whilekeeping Unsafe.Add.
I'd like to see a before/after benchmark if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while keeping Unsafe.Add
Do we really want it? What I meant in #1429 (comment) is a policy against Unsafe.* regardless of whether it can be justified in a particular use-case.
These processors are the spice in the library, it's not the core functionality where we really need the 1-2 extra percents got by micro-optimization. I'd rather go low-risk here. It's much better to get IndexOutOfRangException than an access violation which is a security-critical bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see benchmarks before I make a decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do agree we should be a lot more careful with Unsafe though. I'm guilty of reaching for it far too often.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think I've spotted a way to keep perf for all in a safe manner. https://github.com/SixLabors/ImageSharp/pull/1431/files#r524378700
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try a microbenchmark iterating through a span. You shall see no difference or almost no difference if the loop is defined as for (int i = 0; i < span.Length; i++).
Since real life code does much more than that, benefits shall be negligible or nonexistent. I barely see such code in BCL, I believe the only visible benefit is on .NET Framework, or maybe older .NET Core.
If there is no proof for visible benefits, I would strongly prefer to see a guideline point against Unsafe.*.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why I suggested the slice outside the loop in this instance so we can use .Length. There's little change in Core 3.1 with the current approach but Core 2.1 and NET 472 are suffering according to the benchmarks.
If there is no proof for visible benefits, I would strongly prefer to see a guideline point against Unsafe.*.
Yep. Totally with you. I know of several places in the library where it would be smart to revert to safer code.
Codecov Report
@@ Coverage Diff @@
## master #1431 +/- ##
==========================================
- Coverage 83.08% 83.07% -0.01%
==========================================
Files 707 707
Lines 31834 31831 -3
Branches 3590 3590
==========================================
- Hits 26449 26445 -4
- Misses 4668 4669 +1
Partials 717 717
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
|
after the changes of this PR: before that:
|
| Span<TPixel> rowSpan = source.GetPixelRowSpan(dy); | ||
| int tileX = 0; | ||
| int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); | ||
| for (int dx = x; dx < xLimit; dx++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we sliced the rowSpan and used .Length then we should be able avoid all bounds checks and maintain previous performance in a safe manner on all targets.
|
@brianpopow I guess before/after are the other way around in #1431 (comment)? Any explanation for the significant difference for Framework VS Core? I would be surprised if it's only about indexers. |
Give me a moment i will do the benchmark again. I really think i made a mistake. |
@antonfirsov @JimBobSquarePants: I have run the benchmark now many times, but with the same results as above. The current master branch is actually slower than the change in this PR. I could not believe my eyes, maybe some else wants to double check this. After the change of this PR: Current master branch: I cannot really tell where the large difference between the framework and core comes from. |
|
Haha... I read it the same as Anton. I say we merge this then! |
|
Can we add a test based on what we know about the root cause? (Maybe doesn't need the |
It seems it has to be large to trigger this. 15k x 10k was the "smallest" image where i have seen this. This seems too large for a unit test in my opinion. @antonfirsov what do you think? |
antonfirsov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add such a test with RemoteExecutor, but it will probably run for seconds, and we probably don't want the additional load on our CI, in that case the PR is fine as is.
Use GetPixelRowSpan to access pixel data in Histogram equalization
Prerequisites
Description
This PR changes the histogram equalization to use GetPixelRowSpan to access pixel data to fix an Access Violation issue for very large images reported in #1429.