Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 49 additions & 50 deletions GarthImgLab.Benchmarks/ApplyPerPixel.cs
Original file line number Diff line number Diff line change
@@ -1,89 +1,95 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global

namespace GarthImgLab.Benchmarks;

using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using ImageMagick;
using static System.Collections.Concurrent.Partitioner;
using static Helper;
using static Parallel;

[MemoryDiagnoser, SuppressMessage("ReSharper", "ClassCanBeSealed.Global")]
public class ApplyPerPixel: IDisposable {
public class ApplyPerPixel {
private int _ch;
private MagickImage? _oriImg;
private IMagickImage<ushort>? _testImg;
private MagickImage _oriImg = new();
private IMagickImage<ushort> _testImg = new MagickImage();
private uint _w, _h;

public void Dispose() {
_oriImg?.Dispose();
_testImg?.Dispose();
GC.SuppressFinalize(this);
}
[Params("RGB8.tif", "RGB16.tif")] public string FileName { get; set; } = "RGB8.tif";

[GlobalSetup]
public void GlobalSetup() {
_oriImg = new(Path.Combine(AppContext.BaseDirectory, "lena_std.tif"));
_oriImg = new(Path.Combine(AppContext.BaseDirectory, FileName));
_w = _oriImg.Width;
_h = _oriImg.Height;
_ch = (int)_oriImg.ChannelCount;
_testImg = _oriImg.CloneArea(_w, _h);
if (!Test()) throw new InvalidOperationException("结果不一致");
}

[GlobalCleanup]
public void Cleanup() {
_oriImg.Dispose();
_testImg.Dispose();
}

private bool Test() {
GetValueSetPixel();
var expected = _testImg!.Signature;

var expected = _testImg.Signature;
_testImg.Dispose();
_testImg = _oriImg!.CloneArea(_w, _h);
_testImg = _oriImg.CloneArea(_w, _h);

ParGetValueSetPixel();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

GetValuesSetPixels();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

ParGetValuesSetPixels();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

GetAreaSetPixels();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

ParGetAreaSetPixels();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

ToArraySetPixels();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

ParToArraySetPixels();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

AreaPointerUnsafe();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

ParAreaPointerUnsafe();
if (_testImg.Signature != expected) return false;

_testImg.Dispose();
_testImg = _oriImg.CloneArea(_w, _h);

return true;
}

[Benchmark]
public void GetValueSetPixel() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
for (var y = 0; y < _h; y++)
for (var x = 0; x < _w; x++) {
var v = px.GetValue(x, y)!;
Expand All @@ -94,9 +100,9 @@ public void GetValueSetPixel() {

[Benchmark]
public void ParGetValueSetPixel() {
using var px = _testImg!.GetPixels();
Parallel.ForEach(
Partitioner.Create(0, (int)_h),
using var px = _testImg.GetPixels();
ForEach(
Create(0, (int)_h),
range => {
for (var (y, to) = range; y < to; y++)
for (var x = 0; x < _w; x++) {
Expand All @@ -109,18 +115,18 @@ public void ParGetValueSetPixel() {

[Benchmark(Baseline = true)]
public void GetValuesSetPixels() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
var v = px.GetValues()!;
for (var i = 0; i < v.Length; i += _ch) MockMap(ref v[i], ref v[i + 1], ref v[i + 2]);
px.SetPixels(v);
}

[Benchmark]
public void ParGetValuesSetPixels() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
var v = px.GetValues()!;
Parallel.ForEach(
Partitioner.Create(0, (int)(_w * _h)),
ForEach(
Create(0, (int)(_w * _h)),
range => {
for (var (i, to) = range; i < to; i++) {
var j = i * _ch;
Expand All @@ -132,18 +138,18 @@ public void ParGetValuesSetPixels() {

[Benchmark]
public void GetAreaSetPixels() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
var v = px.GetArea(0, 0, _w, _h)!;
for (var i = 0; i < v.Length; i += _ch) MockMap(ref v[i], ref v[i + 1], ref v[i + 2]);
px.SetPixels(v);
}

[Benchmark]
public void ParGetAreaSetPixels() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
var v = px.GetArea(0, 0, _w, _h)!;
Parallel.ForEach(
Partitioner.Create(0, (int)(_w * _h)),
ForEach(
Create(0, (int)(_w * _h)),
range => {
for (var (i, to) = range; i < to; i++) {
var j = i * _ch;
Expand All @@ -155,18 +161,18 @@ public void ParGetAreaSetPixels() {

[Benchmark]
public void ToArraySetPixels() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
var v = px.ToArray()!;
for (var i = 0; i < v.Length; i += _ch) MockMap(ref v[i], ref v[i + 1], ref v[i + 2]);
px.SetPixels(v);
}

[Benchmark]
public void ParToArraySetPixels() {
using var px = _testImg!.GetPixels();
using var px = _testImg.GetPixels();
var v = px.ToArray()!;
Parallel.ForEach(
Partitioner.Create(0, (int)(_w * _h)),
ForEach(
Create(0, (int)(_w * _h)),
range => {
for (var (i, to) = range; i < to; i++) {
var j = i * _ch;
Expand All @@ -178,30 +184,23 @@ public void ParToArraySetPixels() {

[Benchmark]
public unsafe void AreaPointerUnsafe() {
using var px = _testImg!.GetPixelsUnsafe();
using var px = _testImg.GetPixelsUnsafe();
var ptr = (ushort*)px.GetAreaPointer(0, 0, _w, _h);
var lim = ptr + _w * _h * _ch;
for (var p = ptr; p < lim; p += _ch) MockMap(ref p[0], ref p[1], ref p[2]);
}

[Benchmark]
public unsafe void ParAreaPointerUnsafe() {
using var px = _testImg!.GetPixelsUnsafe();
using var px = _testImg.GetPixelsUnsafe();
var ptr = (ushort*)px.GetAreaPointer(0, 0, _w, _h);
Parallel.ForEach(
Partitioner.Create(0, (int)(_w * _h)),
ForEach(
Create(0, (int)(_w * _h)),
range => {
var (i, to) = range;
var p = ptr + i * _ch;
var lim = ptr + to * _ch;
for (; p < lim; p += _ch) MockMap(ref p[0], ref p[1], ref p[2]);
});
}

private static void MockMap(ref ushort r, ref ushort g, ref ushort b) {
double x = r / 65535d, y = g / 65535d, z = b / 65535d;
r = (ushort)Math.Round(Math.Pow(Math.Clamp(y + z - x, 0, 1), 1.5) * 65535);
g = (ushort)Math.Round(Math.Pow(Math.Clamp(x + z - y, 0, 1), 1.5) * 65535);
b = (ushort)Math.Round(Math.Pow(Math.Clamp(x + y - z, 0, 1), 1.5) * 65535);
}
}
Binary file added GarthImgLab.Benchmarks/Data/RGB16.tif
Binary file not shown.
3 changes: 2 additions & 1 deletion GarthImgLab.Benchmarks/GarthImgLab.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<Using Include="BenchmarkDotNet.Attributes"/>
</ItemGroup>
<ItemGroup>
<None Include="Data\lena_std.tif" CopyToOutputDirectory="PreserveNewest" Link="lena_std.tif"/>
<None Include="Data\RGB8.tif" CopyToOutputDirectory="PreserveNewest" Link="RGB8.tif"/>
<None Include="Data\RGB16.tif" CopyToOutputDirectory="PreserveNewest" Link="RGB16.tif"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.16.0-preview.1"/>
Expand Down
12 changes: 12 additions & 0 deletions GarthImgLab.Benchmarks/Helper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace GarthImgLab.Benchmarks;

public static class Helper {
public static void MockMap(ref ushort r, ref ushort g, ref ushort b) {
ushort r0 = r, g0 = g, b0 = b;
unchecked {
r = (ushort)(g0 * 31 + 1);
g = (ushort)(b0 * 37 + 3);
b = (ushort)(r0 * 41 + 5);
}
}
}
Loading