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

The performance of 'DrawLines' degrades the thinner the lines are #18

Closed
4 tasks done
pmcxs opened this issue Jun 15, 2019 · 5 comments · Fixed by #96
Closed
4 tasks done

The performance of 'DrawLines' degrades the thinner the lines are #18

pmcxs opened this issue Jun 15, 2019 · 5 comments · Fixed by #96

Comments

@pmcxs
Copy link

pmcxs commented Jun 15, 2019

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

Description

Drawing thinner lines takes much more time to process than broader lines.

The following tests were done on a 500x500 image (sorted by processing time):

  • 100 lines with 10px: 448 ms
  • 1000 lines with 10 px: 640 ms
  • 100 lines with 1px: 1530 ms
  • 1000 lines with 1px: 44680 ms

1000 10px lines render faster than 100 1px lines, which seems strange.

Steps to Reproduce

Option 1

Create a simple project, referencing the latest ImageSharp library and include the code below:

var numberOfLines = 100;  // Update accordingly
var imageSize = 500; // Update accordingly
var lineWidth = 5; // Update accordingly

var lines = new List<int[]>();
var rnd = new Random(1);

for (int i = 0; i < numberOfLines; i++)
{
    int pixelX = rnd.Next(imageSize);
    int pixelY = rnd.Next(imageSize);
    lines.Add(new int[] { pixelX, pixelY });
}

SixLabors.Primitives.PointF[] imageSharpLines = lines
    .Select(r => new SixLabors.Primitives.PointF(r[0], r[1]))
    .ToArray();

PointF[] systemDrawingLines = lines.Select(r => new PointF(r[0], r[1])).ToArray();

var w = new Stopwatch();

w.Start();
using (var image = new Image<Rgba32>(imageSize, imageSize))
{
    image.Mutate(x => x

        .DrawLines(
            Rgba32.Red,
            lineWidth,
            imageSharpLines));

    using (var stream = File.Create($"test-imageSharp-{imageSize}-{numberOfLines}-{lineWidth}.png"))
    {
        image.SaveAsPng(stream);
    }
}
w.Stop();

Console.WriteLine($"Took {w.ElapsedMilliseconds} ms with ImageSharp");

Option 2

This was detected on a comparison between ImageSharp, System.Drawing and a custom simplified implementation. That benchmark is available at: https://github.com/pmcxs/core-linedrawing-benchmark/. It can be executed to replicate the issue:

  • Clone the aforementioned repo
  • Run it in Release. By default it will do various comparisons, including the ones specified above.

System Configuration

  • ImageSharp version: 1.0.0-dev002737
  • Environment (Operating system, version and so on): Windows 10 Pro
  • .NET Framework version: .NET Core 2.1
@pmcxs pmcxs changed the title The performance of DrawLine degrades the thinner the lines are The performance of DrawLines degrades the thinner the lines are Jun 15, 2019
@pmcxs pmcxs changed the title The performance of DrawLines degrades the thinner the lines are The performance of 'DrawLines' degrades the thinner the lines are Jun 15, 2019
@JimBobSquarePants
Copy link
Member

Thanks @pmcxs for all the detail.

We're planning on separating ImagSharp.Drawing into a separate repository as we really haven't begun to do the same performance work on that library as we have the main library and we don't want to delay the main libraries RC1 release .

It would be brilliant if you have any advice to give once we do that migration.

@antonfirsov antonfirsov transferred this issue from SixLabors/ImageSharp Jan 17, 2020
@implemental
Copy link

Wouldn't a simple explanation behind why larger lines are faster is because the stopwatch includes the PNG saving to disk and larger lines produce a less-complex PNG and therefore improves performance during serialization.

@JimBobSquarePants
Copy link
Member

@implemental Yep, that’s definitely a factor. I hadn’t actually looked at the benchmarking code.... it’s not great.

System.Drawing saves png’s at zlib compression level 1 with no filter where we use level 6 with paeth filtering. The encoding speed alone with be 2-3x slower.

@antonfirsov
Copy link
Member

antonfirsov commented Nov 20, 2020

There are two factors explaining this:

  1. The thinner the lines, the more points will the final tessellated polygon contain (after extending the multiline with the polygon thickness). In my experiment I've seen ~200x more points in the tessellation with the provided repro code with thickness 1px vs 10px EDIT: 200x degenerate, see The performance of 'DrawLines' degrades the thinner the lines are #18 (comment)
  2. The performance of the scanning code on current master is very bad, and degrades with the number of (tessellated polygon) points in a non-linear manner.

I don't think we can do anything with (1) since it's just the math behind extending a multi-line to a polygon. (2) will be fixed with #96.

@pmcxs if still interested, check out our nightly builds after we merge that PR (ETA: couple of days).

@antonfirsov
Copy link
Member

antonfirsov commented Nov 20, 2020

Actually, (numberOfLines=1000, lineWidth=10) is a degenerate case, where the 500x500 canvas will be filled with mess so I it's not a useful baseline. In normal cases the multiplier between 1px and 10px is much smaller.

I consider this issue to be fixed by #96.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants