Comments (44)
I am loading more than 13000 records in line charts for iOS. But, charts freeze the UI while loading. Also, after loading If the user selects any point then also it takes too much time to highlight the selection.
from charts.
Rather than going to OpenGL for drawing, you could also try lowering down to Core Animation. Maybe the CAShapeLayer class could work. Apple recommends to use multiple CAShapeLayer
s when rendering a complicated path, so you might need to break up the line segment into multiple segments. Using Core Animation should move the work to the GPU.
from charts.
There's an improvement with CGContextStrokeLineSegments as opposed to using paths, so you should try this.
Still trying the other ways of improving things :-)
from charts.
@AlBirdie do you feel the difference with line segments?
Also I've just realized: In android to get the maximum performance you set it to hardware layer- in which case the dashed lines aren't dashed, they're all solid.
So first thing if you disable dashed lines on iOS you also get a significant boost!
But I think I can actually draw it using a OpenGL ES on CIImage, but need to be very careful because if one GL line runs when app is inactive- it will crash.
Also there's a chance I can still allow dashes by pre-rendering a texture. It's gonna take some work and it's not my main priority, but I'm starting a side project of a OpenGL layer that can seamlessly replace the CGContext.
from charts.
The original performance bottleneck in the Android version was many extra allocations (of arrays) inside the rendering code. Memory should not be allocated inside rendering code. So @PhilJay has moved that code into a Buffer that is preallocated, and delegating the rendering calculations to the Buffer classes while rendering.
I chose to not move the rendering calculations to the Buffer classes, but just preallocate the required memory the same way - but have the rendering calculations in the same loop as the rendering code. This way it is far easier to manage the code.
Also it IS a performance gain that the rendering calculations are not being done inside extra functions, and the data is looped only once inside the rendering code instead of twice.
Btw, in Swift performance is FAR FAR better than Java anyway, so you would have seen that performance gain even without pre-allocating those arrays.
In order to graphically explain it, this is what is/was being done in the rendering code:
- [BEGIN RENDERING]
- Loop over DataSets
- -- Allocate array for points of this DataSet -- Now preallocated in both platforms
- -- Calculate points for rendering -- Moved to Buffers in Android version
- -- Render those points
- [COMMIT RENDERING]
Also note that in Java, abstractions (functions, classes, inheritance) come with a great price. It is not for nothing the Google themselves wrote an important recommendation:
Be careful with code abstractions
Often, developers use abstractions simply as a "good programming practice,"
because abstractions can improve code flexibility and maintenance.
However, abstractions come at a significant cost:
generally they require a fair amount more code that needs to be executed,
requiring more time and more RAM for that code to be mapped into memory.
So if your abstractions aren't supplying a significant benefit, you should avoid them.
Summing it up:
- You can be comfortable with working with thousands of data points!
- I'm trying to convince Phil to move point calculations back to rendering loops to keep it simpler to maintain ;-)
from charts.
Thanks for your explanations Daniel. I'm not quite sure what to take from this, though.
What I've done is setup the most basic project (see the code below) and the performance isn't comparable with the Android version, not even close to be completely honest. Using a thousand datapoints results in massive framedrops once you get close to displaying the maximum amount of datapoints (zoom out completely) even on most current iOS devices such as iPad Mini3 and iPhone6. As I said, MPAndroidcharts can renderer several thousands on much slower devices without breaking a sweat.
override func viewDidLoad() {
super.viewDidLoad()
lineChart = LineChartView(frame: view.frame);
view.addSubview(lineChart);
lineChart.backgroundColor = UIColor.whiteColor()
lineChart.descriptionText = "Just a test"
lineChart.leftAxis.enabled = false
lineChart.legend.enabled = false
setData(20)
}
func setData(range:Float) {
var count = 1000;
var xVals = Array<String>();
for(var i = 0; i<count; i++) {
xVals.append(String(i) + "");
}
var yVals = Array<ChartDataEntry>();
for (var i = 0; i<count; i++) {
var mult = range + 1
var val:Float = Float(random()) * mult + 3;
yVals.append(ChartDataEntry(value: val, xIndex: i));
}
var lineSet:LineChartDataSet = LineChartDataSet(yVals: yVals, label: " ");
lineSet.drawCirclesEnabled = false;
lineSet.drawValuesEnabled = false;
var lineData:LineChartData = LineChartData(xVals: xVals, dataSet: lineSet);
lineChart.data = lineData;
}
from charts.
Well I haven't tested all of the charts for performance yet - so I need to do that soon.
The bottleneck is probably an allocation I have missed somewhere... I'll test and let you know! :-)
from charts.
Thanks a lot Daniel. Looking forward to what you can squeeze out of this. Would be awesome to have a viable companion for MPAndroidCharts on iOS.
from charts.
Well, I've played a little with the LineChartRenderer (which is particularly slow compared to for example the BarChartRenderer) and Instruments says that the CGContext's drawPath is the culprit here.
I tried mitigating this issue by using UIBezierPath instead, but performance is the same as soon as you call stroke() on the path. Before that, the drawing performance is great, but the path is filled with regards to the start and end point, not what we want for a simple line chart.
Hopefully you've got a little more luck finding a solution to this.
from charts.
I'm testing this right now, and yes it seems that the actual CGContextStrokePath
suffers from poor performance.
One of the major hitters is the dashed line. When disabling it, the performance ~doubles. But it is still poor with a thousand points.
I'm doing some reading in Apple docs to find out how to make CoreGraphics faster
from charts.
Yes, Core Graphics definitely is rendering the paths slow. It seems to happen because of the resolution - the resolution on newer Apple devices are very high, which means more pixels to render.
I think the CG is rendering on the CPU instead of on the GPU. Maybe someone knows a way to change that?
from charts.
CG is most definitely using the CPU for rendering and as far as I know there's no way to change that.
GPU rendering would mean using OpenGL, unfortunately. Bummer that CG is that slow compared to how Android renders this stuff on the CPU.
from charts.
from charts.
There might be a chance with using the UIBezierPath. As I said, that thing is sufficiently fast as soon as you don't call 'stroke', which essentially tells the system that the path is a stroke and shouldn't be filled from the startpoint to the endpoint. I have no idea what is going on with that in the background, but this shows that the graph drawing itself isn't the issue by itself.
However, since I couldn't find a way to use the UIBezierPath properly for the line chart, it might be a viable candiate for the grid lines as one could trick the path to draw without calling stroke. Even though that is 'wrong' from an API point of view, it should yield better performance.
from charts.
The UIBezierPath is just a UIKit wrapper around CGPath, CGContextDrawPath,
etc.
The difference in performance that is observed in some cases is due to it's
setting up the CG first, with the UIBezierPath's properties of
antialiasing, miter limit etc.
Actually there's absolutely no reason to use it when you know how to use
Core Graphics.
And yes, we do need to call "stroke", as we do want to stroke that path...
not to fill it.
Stroking in most cases is harder for the system than filling in solid
color, because it needs to take care of the width of the lines, the joins
between lines and the shape of the joins.
On Fri, Apr 17, 2015 at 10:50 AM, AlBirdie [email protected] wrote:
There might be a chance with using the UIBezierPath. As I said, that thing
is sufficiently fast as soon as you don't call 'stroke', which essentially
tells the system that the path is a stroke and shouldn't be filled from the
startpoint to the endpoint. I have no idea what is going on with that in
the background, but this shows that the graph drawing itself isn't the
issue by itself.However, since I couldn't find a way to use the UIBezierPath properly for
the line chart, it might be a viable candiate for the grid lines as one
could trick the path to draw without calling stroke. Even though that is
'wrong' from an API point of view, it should yield better performance.—
Reply to this email directly or view it on GitHub
#29 (comment)
.
from charts.
@danielgindi have you thought about that if the animation is slowing things down, how about draw all lines without animation, and use a mask view covering it, then animate the mask view to imitate the animation?
from charts.
Well that's a great idea! :)
Although there's a downside:
It will cripple the kind of animations you can do with it. as we allow
animating the Y axis so the chart "grows", and the way that the line fills
on the X axis is also different than when there's just a stroke.
My list of things to try are:
- Somehow enhance CoreGraphics or elevate part of the rendering to the
GPU
- Pre-render all animation frames- it will delay animation start, and
consume a considerable amount of memory- Your masking idea- which limits the kinds of animations
If there are other ideas I'd love to hear them!
from charts.
Daniel, did you try offloading work to the GPU yet?
I'm really, really unhappy with the commercial solution we are currently working with (terrible API, awefully closed, and loads of bugs that cause the app to crash), and having such great success with MPAndroidCharts, we'd love to switch to iOS-charts eventually if the performance is on par. Rest assured, your work would be rewarded. ;)
from charts.
Trying a few different techniques, I'll give you an update in a few days!
On Mon, Apr 20, 2015 at 12:38 PM, AlBirdie [email protected] wrote:
Daniel, did you try offloading work to the GPU yet?
I'm really, really unhappy with the commercial solution we are currently
working with (terrible API, awefully closed, and loads of bugs that cause
the app to crash), and having such great success with MPAndroidCharts, we'd
love to switch to iOS-charts eventually if the performance is on par. Rest
assured, your work would be rewarded. ;)—
Reply to this email directly or view it on GitHub
#29 (comment)
.
from charts.
@AlBirdie what kind of performance bottleneck you met? I am currently ownning a product that draw charts just like ios-charts, we already had a chart library internally. I also concerns about the performance, currently, we just load 100-1000 data sets, seems ok now.
I am also considering changing to ios-charts if possible in the future, but our library had gestures that might have conflicts with ios-charts.
from charts.
The performance trouble is a slow frame-rate in animations when having to
draw 500-1000 lines in a line chart.
Regarding Gestures - we are using standard UIGestureRecognizers - which you
can disable, modify, or work with. Everything is standardized. :-)
On Mon, Apr 20, 2015 at 12:53 PM, Xuan [email protected] wrote:
@AlBirdie https://github.com/AlBirdie what kind of performance
bottleneck you met? I am currently ownning a product that draw charts just
like ios-charts, we already had a chart library internally. I also concerns
about the performance, currently, we just load 100-1000 data sets, seems ok
now.I am also considering changing to ios-charts if possible in the future,
but our library had gestures that might have conflicts with ios-charts.—
Reply to this email directly or view it on GitHub
#29 (comment)
.
from charts.
@danielgindi well, I think we use the mask view to overcome the animations... Our line chart has gradient layer. As your demo, your animation can do both X+Y direction at the same time, while we just do the X direction. I am not sure if the mask trick can help you.
What I have in my mind now is that if we had a vector to describe your X+Y direction, maybe there is a chance to use the mask trick... look forward to your results!
from charts.
@liuxuan30 just general slow performance I guess. Animations aren't an issue since I'm not working with those. I'm working on finance charts where you need to have multiple datasets in a single chart (multiple stocks + a range of indicators). For a data range of 250 items that easily adds up to several thousand points that need to be rendered at once during panning and pinching. The commercial solution I'm currently working with does that pretty well (using OpenGL you can render thousands of points without overloading the CPU), but I'm not a friend of closed source libraries where you have to wait months for bug fixes.
I'd feel much more comfortable with ios-charts, especially since the API of MPAndroidCharts is just incredibly easy to work with.
from charts.
I see, finance data is disaster. Our server force to just send up to 1000 data sets to the mobile device, so reduced our overload. Is there any chance to use OpenGL for ios-charts? @danielgindi
from charts.
OpenGL support would be aces. A GPU rendered line chart would probably suffice for now.
Unfortunately, I've got absolutely no idea about OpenGL, otherwise I'd be happy to help out.
from charts.
Thanks Daniel, I'll create a small test app that benches the two version against each other to see what's what. What performance gains did you gain using the new LineSegments?
from charts.
For years in my day job we've used a commercial product that as noted above by @AlBirdie makes extensive use of OpenGL, and thus bypasses CoreGraphics for everything except annotations. Certainly they seem to be able to cope with pretty large datasets with fairly smooth rendering and they claim to use the GPU for this. However their approach comes with massive headaches, not least bugs they seemingly cannot be bothered to fix such as async rendering calls that crash in OpenGL as the app has gone into the background, idiotic class structures and insane restrictions on customisability. What I want doesn't exist today, but in iOS-Charts I see that it might be the way to get there. Anyway, for me reliability wins out over performance every time, but it's by the tiniest margin, both are massively important.
from charts.
ps in my opinion dashed lines are a hangover from the days when computers didn't have the option to render in colour or even grayscale - they are not needed unless we're rendering to pure black and white and making something that looks like an old letterpress-printed textbook, surely? ;) So it's far less important to optimise for those than it is to optimise for the general 10K points case.
from charts.
Absolutely with you @onlyforart regarding the dashed lines.
Given your description about your commercial charting library, I'm wondering if we've been using the same product. :) We finally ditched it by the way. What took me the better part of three months to implement (even that wasn't long enough to get around severe bugs and restrictions (e.g crosshair), took a mere two weeks with iOSCharts ('idiotic class structures' ;)). Total waste of time and money.
from charts.
@AlBirdie yes that's the one. Don't even mention the crosshairs. Made with the help of too much Newcastle Brown Ale and undoubtedly aimed at the corporate market (where a gnarly API is actually a positive sales factor, because it locks customers into an expensive support/maintenance cycle). Did you know the history of those guys? actually (in their deep past) they were real pioneers in the software industry. Anyway it's time to move on and look to the future now.
We mostly draw Candlestick charts (these are financial sector apps). What I really want for iOS and Android is something that is actually very easy with HighCharts/HighStock in the HTML5/Javascript world:
- huge numbers of datapoints (10K or more)
- automatic consolidation of OHLC data into sensible scaled bars (eg show 1h bars when the zoom factor is appropriate, or 1m or 1s bars when zoomed out)
- smooth panning and scrolling
- pinch to zoom
- overlaying multiple data series and annotations and crosshairs, some programatically and some by user interaction eg to set a limit price, all rendered smoothly
- (here's the kicker - HighStock doesn't help with this! though it seems pretty obvious to me) incremental fetching of data (with lookahead) eg fetch low resolution data for the entire dataset and, as with maps apps, fetching the hi-rez data (eg millisecond "tick" data) only for the viewed area of the chart
We're not using iOS-Charts yet though it's on my "to-do" list and getting closer to the top, so I did not look inside the code all that much yet, but as soon as I do - probably next month - I'll look into all that.
Back to rendering, OpenGL's tendency to crash if the circumstances are less than perfect is a worry. GLKit could help with GPU drawing; I had also wondered a while back if SpriteKit could help (chart rendering isn't a million miles from making games), but right now GLKit seems a better candidate.
from charts.
LOL @onlyforart , you had me at Newcastle Brown Ale. 👍 :)
I'm in the same boat regarding your requirements (working on finance products as well), but yes, let's get back to the actual drawing of these charts. Looking forward to your findings in the future.
from charts.
@onlyforart , @AlBirdie thanks for your insights :-)
It's really nice to see people ditching commercial enterprise product for our library, although I feel bad for them... I'm conflicted!
I was hesitant to let OpenGL have a slice of this because I knew there are possible crashes if it's not managed perfectly, and that's not really possible in a UIKit app. If you're using OpenGL to create a game, the whole thing is an OpenGL canvas and you do not have to worry about a UIKit call trying to cause an OpenGL rendering while in the background.
@onlyforart your point about dashed lines is correct, but unfortunately I have had experience with clients demanding to have dashed lines. There was a case on Android where to boost performance the developer had to change the layer to a hardware layer, and told the client that dashed lines will become solid lines, that's the cost. And there's of course an option to move all drawing code to OpenGL, but then drawing a single line is a headache, but you can create a texture for a dashed line and work with it. They didn't want to pay for it so dashed lines were certainly not so critical, but they did make a a lot of noise about it.
A note to myself: Try to use GLKit for the rendering. See what happens.
And if I take your list:
- "huge numbers of datapoints (10K or more)" - we are working on it :) I can't say about Android, as moving it to GL will be an even bigger PITA, but it is already having pretty good performance by leveraging the hardware layer, which is doing all drawing on the GPU.
- "automatic consolidation of OHLC data into sensible scaled bars" - we are going to put the approximation filters into use very soon, so I think that will take care of that. Instead of having many 1m bars that you can't read, you'll have an approximation of 1 hour bar etc.
"smooth panning and scrolling"- done :-)"pinch to zoom"- done :-)- "overlaying multiple data series and annotations and crosshairs" - I do not know if I understand correctly, but currently there's the Combined chart that can overlay multiple chart types, and every chart type can accept multiple datasets. In addition, you can always hook into ViewPortHandler and take coordinates so you can overlay whatever you want on top of the char
- incremental fetching of data - we are planning on abstracting the datasource, while still having a the old APIs to set static datasets (it will just be a built in datasource that works with those). I guess that this will also fit your requirement, but we don't know when we'll get to it yet
from charts.
@danielgindi , don't feel bad for them. If the product you're developing is basically a rotten tomato and yet you still charge some serious dough for it, people will eventually move to a different product. There's nothing wrong with that. I've been developing finance charts for several years now, have used pretty much all the solutions for iOS that are currently out there, and have in fact written my own charting engine in ActionScript3 back in the days, so I feel pretty confident saying that iOSCharts and MPAndroidCharts are in fact the only products I can currently recommend to any developer who needs charting in his app. Everything else out there just doesn't cut it.
Regarding the second requirement, if you guys are going to implement this, it has to be optional. We've got this kind of consolidation in one of our charting products and customers are steadily moving to a fixed frequency instead. Changing frequencies during zooming turned out to be not only confusing to the average finance charts user, power users got annoyed because they wanted fixed frequencies.
from charts.
We've never imagined forcing this on the users! You can see that the code already includes the Filters, and in MPAndroidCharts you can see the historically the filters were enabled by a property that sets the filter (any custom filter or the built in ones), but removed later due to structural changes.
When we implement it the functionality will stay the same - it will be just another cool feature :-)
from charts.
@AlBirdie Re "Regarding the second requirement, if you guys are going to implement this, it has to be optional. We've got this kind of consolidation in one of our charting products and customers are steadily moving to a fixed frequency instead. Changing frequencies during zooming turned out to be not only confusing to the average finance charts user, power users got annoyed because they wanted fixed frequencies." Completely agree - this is for charts for informational/non-trading users. Trading users have different needs.
@danielgindi Re "abstracting the datasource" maybe this is something we could contribute to, if we have time. No promises, but I'll add it to our backlog.
from charts.
Did anybody play around with splitting the drawing code to separate threads (CALayer.drawsAsynchronously)? That might help in case this allows to draw chart extras, each dataset, the grid and axes separately. Given my very humble experience with CoreGraphics (read; none whatsoever ;-)), I haven't done any experiments with this, I just found it when I was looking for GLKit and how it could improve the chart performance.
from charts.
I needed to show a candle stick chart with 10k data points so I ran some time measurements on CandleStickChartRenderer.drawDataSet().
It turned out that most of the time is spent on calling dataSet.entryIndex (lines 76,77).
I might be mistaken, but it looks like the dataSet.entryIndex() call is redundant as _minX, _maxX values are always equal to the minx, maxx returned from dataSet.entryIndex()
I've managed to make a candle stick chart with 10k data points pan/zoom smoothly. by changing
CandleStickChartRenderer.swift, line 76,77 from:
var minx = max(dataSet.entryIndex(entry: entryFrom, isEqual: true), 0);
var maxx = min(dataSet.entryIndex(entry: entryTo, isEqual: true) + 1, entries.count);
to
var minx = max(_minX, 0);
var maxx = _maxX + 1;
I've made the same change to LineChartRenderer and was able to show a combined candle stick/line chart with 2 data series (10k data points each)
from charts.
Wow, that's a massive performance increase @dorsoft !
I've just tested this and I couldn't believe my eyes. Even on an iPad 2 with 4 CombinedCharts showing up to three datasets each with 250 datapoints each and automatic min/max calculations of the y axis we are now able to pan and zoom all charts simultaneously in a fairly smooth manner. It is not 60fps, but close. Impressive for such an old device and WAY (!) faster than the commercial OpenGL solution we've talked about earlier.
from charts.
from charts.
I just read this, looks very interesting but it's gonna need some intense testing to see if it is actually suitable for all scenarios :-)
from charts.
I've created a PR with candle chart performance enhancement.
The enhancment was tested thouroughly using auto scale min/max and nil values.
from charts.
I've found a possible bottleneck. In a realtime chart Adding many entries per second to the chart I noticed that automatically the calcMinMax of the dataset is called just after EACH values.append(e)
calcMinMax function determine the min and max values using .forEach that is causing the CPU over 60% spent looping the values, moreover in the main thread.
I'm performing some experiment disabling min/max calculation or using native for loop instead of array .forEach
@danielgindi please have a look, is really calcMinMax on all values needed? I calculate min/max Y values manually in my code and only on visible values, so maybe you could expose some boolean to enable/disable min/max auto calculation and improving performance removing the forEach function and avoid function calls.
```
open override func calcMinMax()
{
guard !values.isEmpty else { return }
_yMax = -Double.greatestFiniteMagnitude
_yMin = Double.greatestFiniteMagnitude
_xMax = -Double.greatestFiniteMagnitude
_xMin = Double.greatestFiniteMagnitude
values.forEach { calcMinMax(entry: $0) }
}
from charts.
@samueleperricone Please make this a separate ticket and I will prioritize this.
from charts.
@jjatie Thanks, Just open #3166
from charts.
Related Issues (20)
- How to make stacked line chart?
- Creating Stacked Bar Chart How To Round Corner Radius Of Bar and How To Set DataPoints Background Color
- BarChart: highlight x-axis value when tapped HOT 1
- Incorrect scale of candlestick chart
- Y-axis max label is less than the highest Y-Value
- Library not loaded on physical device HOT 1
- crash animationUpdate() HOT 2
- Concurrency warning when conforming `ChartViewDelegate` with MainActor attribute.
- Pie chart update data set
- BalloonMarker Corner and barChart corner
- How to alpha background when select a marker ?
- How to alpha background when select a marker ? Now I use BalloonMarker
- Install pod 'DGCharts' & pod 'ChartsRealm' is the name of the libraries
- Install pod 'DGCharts' & pod 'ChartsRealm' is the name of the libraries, then i'm trying to build the application i'm getting below error:
- Install pod 'DGCharts' & pod 'ChartsRealm' is the name of the libraries, then i'm trying to build the application i'm getting errors: HOT 3
- Bar Chart Displaying Wrong Y Values JUST rightAxis,but leftAxis is OK!!! HOT 3
- Bar chart, x-axis or zero axis is overlapping the bars.
- % symbol not showing in Piechart
- View Destroy,RadarChartView not releasing, memory leak. Is there a solution?
- How to Fill Area Between Two Lines with Different Colors Based on Line Intersection
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from charts.