ChartsOrg / ChartsOrg/Charts

Scatter charts efficiency

Open
#3,868 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
28k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

A comment about the designs of these renderers.

Everything works just fine when you have datasets that people can count on two hands. But you don't know how much data someone is going to throw at the chart so you should give some thought to efficient designs.

The shape renderers for points are all drawing the same shape over and over again. So if you have a circle with a hole in it and 30,000 points, it's executing the same draw code 30,000 times. That is slow as molasses. What it should do instead is to make a bitmap, render the shape once into the bitmap (ideally when the thing is configured, or lazily as needed), and then just hammer that bitmap down over and over again. It will be much faster.

In general the design of the renderers gives no thought to the fact that it will be called over and over again with the same parameters.

So these methods, they all call back to the dataset to get what are effectively constant values, and then do math on them on every rendering. And the results of that math are going to be the same because the original values are all constant, taken from the dataset.

This makes for a bunch of needless math and a whole lot of method calls all extracting the same values tens of thousands of times on large datasets.

This example:

open class CircleShapeRenderer : NSObject, IShapeRenderer
{
open func renderShape(
context: CGContext,
dataSet: IScatterChartDataSet,
viewPortHandler: ViewPortHandler,
point: CGPoint,
color: NSUIColor)
{
let shapeSize = dataSet.scatterShapeSize
let shapeHalf = shapeSize / 2.0
let shapeHoleSizeHalf = dataSet.scatterShapeHoleRadius
let shapeHoleSize = shapeHoleSizeHalf * 2.0
let shapeHoleColor = dataSet.scatterShapeHoleColor
let shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.0
let shapeStrokeSizeHalf = shapeStrokeSize / 2.0

Shows the design errors. When this renderer is created, it should receive the size one time and hold the value. It should do what math is needed to calculate the repeated use values one time as well.

Furthermore, most of these values are calculated and then not used in half of the branches of the rendering code (which are branching based on constants). This is weak programming but the optimizer will probably clear that one up and fix what the programmer wrote. But it's not right to depend on hopes and prayers and optimizers to move your math to the branches that it is going to be used. If the optimizer isn't catching it then this code is doing the following:

1. continually fetching constant values thousands of times via method calls
2. doing needless math on the constants to calculate more constants thousands of times
3. throwing away half of the constants it calculates

It's not much work to implement things like cleanly without the repeated method calls to get constant values and has a huge benefit in responsiveness and performance.

I'm using an out of date version of the kit because things break a lot between releases and I'm not a swift programmer, so it's hard to deal with patching and fixing the incompatibilities. Please consider this as a suggestion of an area to look at. Just avoiding thousands of method calls and needless math is free performance, especially for battery powered devices. Caching a bitmap and stamping it is a bit more work but it's not so difficult to do at least in ObjC for this kind of implementation.

The basic shapes that are just filled are going to execute fairly quickly but as soon as you get into hollow shapes and stroking, this is performance cancer to implement it as it is.

Contributor guide

Open the contributing guide

Research direction

Start with the CircleShapeRenderer example in the issue and inspect the other shape renderers to identify repeated dataset access, calculations, and drawing. Measure rendering with large scatter datasets, then evaluate whether cached values and bitmap-based shape reuse reduce the repeated work without changing the rendered shapes.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
mobile-dev, performance
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.