Closed GoogleCodeExporter closed 8 years ago
Original comment by drewmcco...@mac.com
on 2 May 2010 at 3:24
This is absolutely needed for the library to be used on the iPhone. I don't
mind loosing the ability to perform
animations, but the library is too slow to be used as real time data presenter.
Having the possibility to quickly
render it all in a single graphics context would be a huge usability
improvement for many performance critical
applications.
Original comment by carbon...@gmail.com
on 23 May 2010 at 9:49
It won't help the memory usage, but you should be able to use the
layoutAndRenderInContext: method on any
layer including the graph right now. Just maintain the graph normally, but
don't add it to the view hierarchy.
Original comment by eskr...@mac.com
on 23 May 2010 at 1:53
Thank you for your answer. I tried to draw the graph on a image context using
layoutAndRenderIncontext:
(thanks for the tip) and then put that image on a view. However, I do not see
any performance improvement in
doing so for what I need. What I need is being able to quickly change plots
from a continuously moving data
source in real time. Is there a way I can achieve more performance to do so
with core-plot. I am not changing
axis or backgrounds, only plots, but unfortunately they seem to take forever to
render. Thanks for any tip.
Original comment by carbon...@gmail.com
on 23 May 2010 at 3:03
How much data are you rendering, and how often are you refreshing? You may be
better trying to reduce the
refresh rate, perhaps by skipping some refreshes. In this case, it may not be
core animation that is the problem.
That is more an issue when you have interaction.
It would be very useful if you could run your app on the iPhone with
Instruments to check where the time is
being spent. Saving the instruments data, and perhaps uploading it here may be
useful to finding out what the
issue is.
Original comment by drewmcco...@mac.com
on 23 May 2010 at 3:28
I am refreshing once every 2 seconds, this is long enough for core graph to
keep doing its thing but it happens
that the time it takes to render makes the iphone unresponsible for a while. I
am currently testing it with a
NSArray per plot which does not change and using the faster numbersForPlot data
source method, so the issue
should not be the data feed at this time. I am rendering 3 graphs with 2 XY
plots in each and 60 points per plot.
Even with no plots, the time it takes to render the 3 empty plots makes the
iPod noticeably freeze for half a
second or so every 2 seconds. I wonder whether I could render on a second
thread just to make the iPod feel
more responsible while the graphs are on. I will run instruments later to see
if I am able to provide some
additional light on this. Thanks,
Original comment by carbon...@gmail.com
on 23 May 2010 at 5:49
I did the following to test it for performance. Added NSLog statements to my
calls to reloadData, the end of numbersForPlot delegate, and the end of
CPLayer drawInContext. This is a fragment of the log:
2010-05-24 11:25:01.137 ScadaMobile[6469:207] Charts View reloadData
2010-05-24 11:25:01.141 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:01.145 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:01.150 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:01.154 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:01.159 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:01.163 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:01.168 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:01.172 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:01.200 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:01.208 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:01.218 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:01.227 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:01.353 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x276030> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:01.470 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x275f40> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:01.599 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272f00> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:01.716 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272e10> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:01.844 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x270150> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:01.970 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x26f420> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:03.137 ScadaMobile[6469:207] Charts View reloadData
2010-05-24 11:25:03.141 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:03.145 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:03.150 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:03.154 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:03.159 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:03.163 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:03.168 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:03.172 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:03.200 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:03.209 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:03.218 ScadaMobile[6469:207] Chart View numbersForPlot field:0
2010-05-24 11:25:03.227 ScadaMobile[6469:207] Chart View numbersForPlot field:1
2010-05-24 11:25:03.367 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x276030> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:03.484 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x275f40> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:03.612 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272f00> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:03.730 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272e10> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:03.858 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x270150> bounds: {{0, 0}, {320, 146}}>
2010-05-24 11:25:03.977 ScadaMobile[6469:207] CPLayer drawInContext:
<<CPScatterPlot: 0x26f420> bounds: {{0, 0}, {320, 146}}>
It can be seen that the iPod takes about 0.850 seconds from the reloadData call
to the end of the last render. Also the time from reloadData to the
end of numbersForPlot is 0.090. So it seems the bottleneck here is mostly
rendering.
I attach also an Instruments file. It is interesting (although a bit
frustrating) to see that there is not a single place where optimizations can be
effectively done. Actual rendering is only about 8.2% (if I understand it well)
and operations with NSDecimalNumbers take a lot of time all over the
place (at least 20%), because they seem to be implemented using strings
internally, which is what I think is really killing the library.
I will appreciate any comments.
Joan
Original comment by carbon...@gmail.com
on 24 May 2010 at 10:03
Attachments:
It actually looks to me like there was a bug in there, causing decimals to be
used when doubles should have
been used. If you are using NSNumbers, there should be very few NSDecimals used.
I have fixed and pushed, so update and see if that helps, at least with the
NSDecimal methods.
Original comment by drewmcco...@mac.com
on 24 May 2010 at 6:15
Thanks for having looked at it. It has been a significant improvement. Now it
takes 0.548 seconds to plot the
three graphs instead of the previous 0.850. That's 55% faster, not bad. It has
become certainly much more
usable, the freezes on the interface are now less noticeable. Just for
curiosity I will study it a bit with Instruments
to see where the time is being taken now and will post it later.
Joan
Original comment by carbon...@gmail.com
on 24 May 2010 at 7:18
Ok, I looked at it and squeezed more performance out of it. The same test runs
now in 0.268 seconds compared to the original 0.850 and your fix at 0.548. Find
changes
attached. What I just propose is two simple changes to avoid unnecessary
NSDecimalNumber calculations and conversions when the data sources are floating
point numbers.
I believe there is still some of room for performance improvements, the library
is still somewhat killed by allocations/deallocations, may be extensive use of
auto-released
objects, and some redundant method calls inside loops. This would be much
harder to fully optimize because in some cases it would require different
coding patterns. I am
happy with the current result, but may propose additional optimizations if I
feel they are not too tedious to implement and are significant. Just for your
information I copy
below the logs running the same test app with the proposed changes.
2010-05-24 23:55:25.657 ScadaMobile[6705:207] Charts View reloadData
2010-05-24 23:55:25.661 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:25.665 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:25.670 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:25.674 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:25.679 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:25.683 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:25.688 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:25.692 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:25.706 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:25.710 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:25.714 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:25.720 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:25.748 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x276090> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:25.782 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x275f90> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:25.837 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272f40> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:25.863 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272e40> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:25.901 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x270170> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:25.926 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x26f430> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:27.657 ScadaMobile[6705:207] Charts View reloadData
2010-05-24 23:55:27.661 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:27.666 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:27.670 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:27.674 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:27.679 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:27.683 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:27.688 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:27.692 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:27.697 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:27.701 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:27.706 ScadaMobile[6705:207] Chart View numbersForPlot field:0
2010-05-24 23:55:27.710 ScadaMobile[6705:207] Chart View numbersForPlot field:1
2010-05-24 23:55:27.749 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x276090> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:27.785 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x275f90> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:27.832 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272f40> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:27.863 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x272e40> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:27.900 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x270170> bounds: {{0, 0}, {320, 146}}>
2010-05-24 23:55:27.925 ScadaMobile[6705:207] CPLayer drawInContext:
<<CPScatterPlot: 0x26f430> bounds: {{0, 0}, {320, 146}}>
Joan
Original comment by carbon...@gmail.com
on 24 May 2010 at 10:04
Attachments:
Sorry, there was a bug in one of the files I attached on my previous post. Here
is the correct one.
Thanks,
Joan
Original comment by carbon...@gmail.com
on 24 May 2010 at 10:28
Attachments:
I've incorporated some changes 'inspired' by yours. Not exactly the same, but I
think they will lead to a good
performance boost.
Original comment by drewmcco...@mac.com
on 25 May 2010 at 8:49
Thank you for committing changes. However there were a couple of performance
improvements in my code
that you did not incorporate. Current performance is not much better that the
one we had after your fixed the
earlier bug. I will try to explain why. All changes refer to CPScatterPlot.m
1- In calculateViewPoints you should have self.xValues and self.yValues in
local vars *outside* the for loop.
This is a huge performance boost because it avoids accessing the cachedData
dictionary by key in CPPlot and
the creation/release of the NSNumber key for each point. Currenlty, a 100
points plot is accessing 200 times
an object in a dictionary by key.
2- The same applies to calculatePointsToDraw. The self.yValues and self.xValues
calls should be out of the
loop. If not, this adds 200 additional accesses to the dictionary.
3- The compareToNumber calls in calculatePointsTodraw also took a lot of time
in its previous
implementation. Current implementation should be a bit slower than mine, but
should be ok, and does not
add another method.
A developing pattern in Objective-C I always recommend is to use local
variables to temporary store objects
that may be far than where they are used. Not doing it when loops are involved
hits very seriously
performance. Objective-C is not like C++ where the compiler has a lot of info
to optimize function calls.
Objective-C can be so dynamic that to be safe the compiler has no other choice
than to generate separate
code for every and each property access or method call. On top of this
Objective-C calls are significantly
slower than C or C++ calls. This brings some responsibility to the developer,
who should be aware of it. This
is why a simple measure such as using local variables to avoid unnecessary
calls prove to give big
performance boosts in many cases.
Proposals 1 and 2 are very easy to add, and they cut processing time for a
simple XY Plot by as much as a half.
I will appreciate that you incorporate them to avoid having to maintain a
separate branch myself for my use. I
really need the added performance they give. Thank you very much.
Joan.
Original comment by carbon...@gmail.com
on 25 May 2010 at 1:45
Ah, sorry, missed the other fixes. They were a bit subtle.
They are in there now.
Thanks, and good work. If you think you want to contribute regularly, we can
arrange commit rights.
Original comment by drewmcco...@mac.com
on 25 May 2010 at 2:00
Sorry, I just realized that you already incorporated what I mentioned in my
previous post, please forget about
it. I must have been looking somewhere else. My apologies. It's really
fantastic you respond that quick to user
proposals.
Just we could also add NSUInteger locals replacing all uses of
self.xValues.count on for loops. It is being called
as many times as the loop iterates, so removing it may well squeze a bit more
of performance, and it is very
easy to do. I will post the definite performance figures in case you decide to
do so. Thanks again.
Joan
Joan.
Original comment by carbon...@gmail.com
on 25 May 2010 at 2:14
Core-Plot is really well thought in general terms and its clever class
structure makes it easily expandable. That
tells a lot about who designed it. However its support for NSDecimalNumbers
and its reliance on NSNumbers
makes it too slow for some uses. After the last proposed changes simple
plotting speed has nearly doubled.
According to the comparative tests based on what I described on a early post we
are now at 0.280 seconds,
from the original (buggy) 0.850 and its fix at 0.548 s.
By going back to the files I posted and eliminating any remaining redundant
calls to self.xValues.count I
checked that we can squeeze another 0.038 seconds, so we would be at 0.244.
This is the easiest thing to do.
However, after looking carefully at it I discovered that there is still, as
unbelievable it could seem, a huge
margin for improvement if we could get rid of all the NSNumber stuff. In order
to have this option for really
performance demanding applications I propose to split the cachedData storage in
a way that is able to store
NSData objects (as oposed to NSArrays) when the data source is a C array of
doubles, as well as maintaining a
flag indicating the storage type, which would be set when the cache is filled.
With this approach on caching we should be able to eliminate a lot of slow code
in relation to NSNumber
creation/destruction/comparison as well as intermediate allocation of objects,
and memory. From my
observations on the times the current code takes to do things, I estimate that
we should be able to get
plotting times in the neighborhood of 0.100 or less.
Someone willing to do it?
Joan.
Original comment by carbon...@gmail.com
on 27 May 2010 at 11:10
There is a branch called BWNumericData that started to implement some of those
ideas. I started to bring it up to
date a little while ago but got sidetracked. You're welcome to take a look at
that and see if those additions are on
the right track.
Original comment by eskr...@mac.com
on 28 May 2010 at 12:46
I don't understand what the BWNumericData branch is all about. Seems way
overkill for my purpose, which is
simply bringing real time performance to core-plot. I will try to propose
myself some simpler enhancements. BTW, It is still hard for me to get why
NSDecimalNumbers are used at all. Would you point me to an application
where 38 decimal digit precision is really needed for plotting a graph (?).
Looks to me that double precision
numbers should be more that enough for most applications. Thanks.
Original comment by carbon...@gmail.com
on 28 May 2010 at 10:45
I agree that doubles are adequate most of the time. Core Plot was created with
scientific and financial
applications in mind where the additional precision is required to avoid
rounding errors and to support very
small and very large values.
Original comment by eskr...@mac.com
on 28 May 2010 at 11:17
The collapseLayers option allows for non-layer drawing now.
Original comment by drewmcco...@mac.com
on 8 Jan 2011 at 9:57
Original issue reported on code.google.com by
drewmcco...@mac.com
on 26 Apr 2010 at 6:19