i did evaluate the run-time performance of a 6 XYSeries plot setup (500 X axis samples) on a nexus 7 2013 with simple synthesized sine waves in the getY (idx) functs and compared it to a JNI getY (idx) based setup where the JNI C++ code simply fetched the data from a buffer within the JNI capsule
here is a simplified setup for the synthesized sine wave generation (example shows one XYSeries)
public static int DatCnt = 0 ;
static long DatSav ;
@Override
public double GetY (int idx) // being called by super.getY (idx), which implements XYSeries
{
long t0 = 0 ;
if (DatCnt > 0) { DatCnt-- ; t0 = System.nanoTime () ; }
if (DatCnt == 499) DatSav = t0 ;
double dat = Sin (idx) * 45 + 50 ;
//if (DatCnt > 0 &&
// DatCnt < 10) {
// Log.i ("com.efiLabs.PlotXYtst", String.format ("dat0 %6d %d", (System.nanoTime () - t0) / 1000, DatCnt)) ;
//}
if (DatCnt == 1) { ActPlotNew0.Tsum += t0 - DatSav ;
Log.i ("com.efiLabs.PlotXYtst", String.format ("dat0 %6d %6d", (t0 - DatSav) / 1000, ActPlotNew0.Tsum / 1000)) ;
}
return dat ;
//return Sin (idx) * 45 + 50 ;
}
and this is the android monitor output
06-04 16:14:40.707 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onDown x_1493 y_523
06-04 16:14:40.797 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_23
06-04 16:14:40.797 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 233989457389
06-04 16:14:40.861 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat0 33874 33874
06-04 16:14:40.901 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat1 26428 60302
06-04 16:14:40.938 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat2 24627 84930
06-04 16:14:40.974 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat3 25299 110229
06-04 16:14:41.010 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat4 24047 134277
06-04 16:14:41.046 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat5 25299 159576
06-04 16:14:41.069 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_517
06-04 16:14:41.069 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 272247
06-04 16:14:41.129 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat0 24322 24322
06-04 16:14:41.165 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat1 24536 48858
06-04 16:14:41.200 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat2 24688 73547
06-04 16:14:41.236 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat3 24414 97961
06-04 16:14:41.271 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat4 23895 121856
06-04 16:14:41.305 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat5 23834 145690
06-04 16:14:41.326 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_37
06-04 16:14:41.326 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 256622
06-04 16:14:41.387 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat0 23834 23834
06-04 16:14:41.429 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat1 30395 54229
06-04 16:14:41.473 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat2 28411 82641
06-04 16:14:41.507 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat3 23406 106048
06-04 16:14:41.542 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat4 23803 129852
06-04 16:14:41.576 18061-18061/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat5 24108 153961
a vertical scroll will start the loop with the DatUpd () functs being called periodically
public static long Tstart, Tsum ;
public synchronized void DataUpd ()
{
LogI (String.format ("sum %d", (System.nanoTime () - Tstart) / 1000)) ;
DataY_0.DatCnt = 500 ;
DataY_1.DatCnt = 500 ;
DataY_2.DatCnt = 500 ;
DataY_3.DatCnt = 500 ;
DataY_4.DatCnt = 500 ;
DataY_5.DatCnt = 500 ;
Tstart = System.nanoTime () ;
Tsum = 0 ;
Plot.redraw () ;
}
the "dat3 25299 110229" shows the exex times for each series (dat0 - dat5), whereas the 1st number is the individual time accumulation from the x-data 0 to 500, and the 2nd is a running time tally ... 25299 us for the dat3 series and 110299 us for dat0 to dat3 and so on ... an average data fetch time per ZYSeries is about 25 ms
the total sum of 272247 us is the time from the 1st DatUpd () call to the next one in the scroll operation ... i didn't have a hook into when the plot redraw process was complete
the total getY (idx) data fetch time for all 6 XYSeries is 159576 us and the total redraw cycle time 272247 us ... 272247 - 159576 = 112671 us
of course i do not have any idea of the percentage of the remaining time (after the fetch time) of 112671 us, which is androidplot time and which is scroll gesture processing
and now to the C++ JNI setup timing
06-04 17:09:39.783 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onDown x_1620 y_497
06-04 17:09:39.881 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_16
06-04 17:09:39.882 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 25543273
06-04 17:09:39.947 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: rdif 36865 36865
06-04 17:09:39.995 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: rpm 36956 73822
06-04 17:09:40.032 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: tdif 36224 110046
06-04 17:09:40.085 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: ext0 42205 152252
06-04 17:09:40.138 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: ext1 41778 194030
06-04 17:09:40.202 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: raw2 53955 247985
06-04 17:09:40.226 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_724
06-04 17:09:40.226 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 344085
06-04 17:09:40.293 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: rdif 36956 36956
06-04 17:09:40.341 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: rpm 36651 73608
06-04 17:09:40.377 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: tdif 36315 109924
06-04 17:09:40.429 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: ext0 40954 150878
06-04 17:09:40.481 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: ext1 40802 191680
06-04 17:09:40.534 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: raw2 41015 232696
06-04 17:09:40.555 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_4
06-04 17:09:40.555 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 329620
06-04 17:09:40.619 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: rdif 36712 36712
06-04 17:09:40.666 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: rpm 36499 73211
06-04 17:09:40.702 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: tdif 36163 109375
06-04 17:09:40.779 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: ext0 63323 172698
06-04 17:09:40.836 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: ext1 42266 214965
06-04 17:09:40.888 4067-4067/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: raw2 41259 256225
comparison :
accumulated fetch time is 247985 us
the sine synthesized data fetches take an average 25 ms / series whereas the C++ JNI data fetches take about 40 ms ... 15 ms longer, an increase of 60 % over the sine wave synthesis example
the percentage of the getY (idx) synthesized sine wave generation time portion is unknown and so is what else is happening inside androidplot from one getY (idx) to the next one
25 ms / 500 x-samples is 50 us, 15 ms / 500 x-samples = 30 us ... assuming that the sine calculation can't take the majority of the 50 us i would say that a single getY (idx) call processing, not counting the data fetch takes maybe 40 to 45 us ...
the C++ JNI data fetch time of 30 us is not lightning fast, but something i have to live with ... 30 us * 500 * 6 = 90 ms for the whole data fetch only portion ... i could do this 10 times / sec if the redraw wouldn't take any time
i use the FastLineAndPointRenderer.Formatter setup
public static FastLineAndPointRenderer.Formatter LineFormat (int color, int width, int dash, int spc)
{
Paint paint = new Paint () ;
paint.setColor (color) ;
paint.setStyle (Paint.Style.STROKE) ;
paint.setAntiAlias (false) ;
paint.setStrokeWidth (PixelUtils.dpToPix (width)) ;
paint.setPathEffect (new DashPathEffect (
new float [] { PixelUtils.dpToPix (dash), PixelUtils.dpToPix (spc) }, 0)) ;
FastLineAndPointRenderer.Formatter line = new FastLineAndPointRenderer.Formatter (paint.getColor (), null, null) ;
line.setLinePaint (paint) ;
line.setVertexPaint (null) ;
line.setPointLabelFormatter (null) ;
return line ;
}
please, can anyone shine a bit more light onto my setup and comment this paper
is there a way to speed it up and still sick with 500 x-points ... that's what's being used in QT on the desktop setup and it's zippy zippy fast
what can be improved and what additional info should i provide
i hope i do not have made too many typos ;)
did more testing and lowered the x-points from 500 to 50 for the sine synthesized plot and here is the data
06-04 20:11:16.172 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onDown x_1464 y_562
06-04 20:11:16.407 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_24
06-04 20:11:16.407 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 46652130
06-04 20:11:16.442 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat0 2319 2319
06-04 20:11:16.471 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat1 2380 4699
06-04 20:11:16.512 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat2 2410 7110
06-04 20:11:16.549 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat3 2288 9399
06-04 20:11:16.580 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat4 2593 11993
06-04 20:11:16.610 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat5 2532 14526
06-04 20:11:16.650 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_350
06-04 20:11:16.651 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 243133
06-04 20:11:16.689 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat0 2288 2288
06-04 20:11:16.720 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat1 2319 4608
06-04 20:11:16.751 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat2 2288 6896
06-04 20:11:16.782 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat3 2685 9582
06-04 20:11:16.812 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat4 2319 11901
06-04 20:11:16.842 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat5 2410 14312
06-04 20:11:16.881 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_46
06-04 20:11:16.881 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: sum 230346
06-04 20:11:16.915 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat0 2319 2319
06-04 20:11:16.944 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat1 2349 4669
06-04 20:11:16.973 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat2 2410 7080
06-04 20:11:17.002 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat3 2319 9399
06-04 20:11:17.033 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat4 2471 11871
06-04 20:11:17.064 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: dat5 2563 14434
06-04 20:11:17.116 26868-26868/com.efiLabs.PlotXY2tst I/com.efiLabs.PlotXYtst: onScroll x_2
yes, the getY (idx) times for 50 x samples compared to 500 are now 2.5 ms compared to 25 ms with 500
but the total time redraw scoll still remains 240 ms ... how come ???
here is my scroll funct inherited from a base class just calling it
protected boolean OnScroll (MotionEvent evt0, MotionEvent evt1, float dx, float dy)
{
DataY.Offs += (dx * 50) / (mWidth - mWidth / 12) ; DataUpd () ;
return true ;
}
am i doing something wrong with the scroll handling of the plot ???
the previous timing values with 500 x-data points sort of match, but now ??? what happens in the 243 ms with the getY (idx) only taking up 14 ms ... why is the scroll refresh frequ not increasing ???
the whole graph scolling is still as jerky as before
As far as the scroll characteristics go, I don't have an in depth answer there but I imagine there's some sort of message loop / throttling occurring inside the OS. Have you measured the scroll callback without the redraw call to get a sense of what the max theoretical frequency of the scroll cb is?
In any case, speeding up your render times can only help so here are a couple general suggestions:
Probably the biggest thing you can do to boost performance in your app is to use FastXYSeries
. This means either using your own or using SampledXYSeries
which is the only "out of the box" implementation. My suggestion would be to implement your own.
To get the best results from this approach you'll also need to set a min/max y boundary and make sure that your series min/max y-vals all fall within that boundary. The idea is that for apps that draw a stream of incoming data, it's possible to maintain a running tally of the min/max values observed. The FastXYSeries
interface gives Androidplot a way to query these values allowing it skip the very expensive step of iterating through each series looking for visible min/max vals.
If FastXYSeries
is not feasible, then using just about any optimized XYSeries
implementation besides SimpleXYSeries
will be an improvement as it is optimized for versatility, not performance. For dynamic plots of a fixed size (as yours appears to be) FixedSizeEditableXYSeries
is a particularly good choice.
EDIT: Updates to added questions below
all the above so far has been done using the FastLineAndPointRenderer.Formatter which doesn't seem to care if the setInterpolationParams is being used ... would make sense since we want to do it fast
Correct (noted in the class Javadoc )
one wonders about the differences between the LineAndPointFormatter and the FastLineAndPointRenderer.Formatter since this setup provides the same timing
There are several differences but the two main ones are: * Bare minimum number of instantiations of new instances on each render cycle. * Draws each series using simple line segments instead of a Path.
It's definitely much faster. If you're not seeing a difference in your benchmarks I'd have to assume that either the benchmark is not accurate or theres a bottleneck elsewhere in your render cycle. One thing you can try is using Androidplot's PlotStatistics utility. The DemoApp's orientation sensor sample provides a usage example. It provides an actual performance and theoretical performance (performance of Androidplot's internal rendering only) value in FPS overlay on the plot for which it is enabled. If the two numbers are vastly different then it means the bottleneck is outside of Androidplot. The most common source of bottlenecks is not usin separate threads thread to monitor your sample source and trigger redraws to your plot at a stable frequency. The ECG plot in the DemoApp provides a reasonable example of doing this.
I also noticed in your code above that you do quite a bit of logging. Keep in mind that logging is very expensive and can have a significant impact on performance, particularly if you are calling it on every invocation of getY() as it appears above. I would not be at all surprised if the overhead is upwards of 10-30% here.
somehow i overlooked the size () function in the XYSeries providing the true x-data count of the plots ... was pretty late after 12 hours of coding ;)
below : synthesized sine wave getY (idx) timing data
dat0 2349 2349
dat1 2258 4608
dat2 2716 7324
dat3 2624 9948
dat4 2258 12207
dat5 2471 14678
sum 69396
it takes now 69 ms for 50 x-samples and 6 plots of them, with 14 ms for all the getY (idx) fetches
below : C++ JNI getY (idx) data fetch timing data
rdif 3936 3936
rpm 4150 8087
tdif 3784 11871
ext0 4058 15930
ext1 4089 20019
raw2 4150 24169
sum 74432
it takes now 74 ms for 50 x-samples and 6 plots of them with 24 ms for all the getY (idx) fetches
all the above so far has been done using the FastLineAndPointRenderer.Formatter which doesn't seem to care if the setInterpolationParams is being used ... would make sense since we want to do it fast
FastLineAndPointRenderer.Formatter line = new FastLineAndPointRenderer.Formatter (paint.getColor (), null, null) ;
line.setLinePaint (paint) ;
line.setVertexPaint (null) ;
line.setPointLabelFormatter (null) ;
line.setInterpolationParams (new CatmullRomInterpolator.Params (10, CatmullRomInterpolator.Type.Centripetal)) ;
the LineAndPointFormatter alone without the setInterpolationParams produces the same timing as the FastLineAndPointRenderer
one wonders about the differences between the LineAndPointFormatter and the FastLineAndPointRenderer.Formatter since this setup provides the same timing
LineAndPointFormatter line = new LineAndPointFormatter (paint.getColor (), null, null, null) ;
line.setLinePaint (paint) ;
line.setVertexPaint (null) ;
line.setPointLabelFormatter (null) ;
but now, when you add the
line.setInterpolationParams (new CatmullRomInterpolator.Params (10, CatmullRomInterpolator.Type.Centripetal)) ;
to the regular LineAndPointFormatter the interpolation sets in and the following timing is the result
dat0 2838 2838
dat1 3112 5950
dat2 2716 8666
dat3 3021 11688
dat4 2136 13824
dat5 2258 16082
sum 277618
the getY (idx) timing seems the same, but the overall cycle time changed from 69 ms to 277 ms which makes sense since the interpolation of 50 x 6 points will take some time
i'm so far happy with my results and will use 50 instead of 500 x-data samples ... the JNI C++ code will either just return one value and skip 4 or average 5 of them together
maybe my humble anroidplot eval might help others with their androidplot design
i switched now the XYSeries interface to the new FastXYSeries and added a minMax () returning the following region
private RectRegion Region = new RectRegion (0, TApCfgX.SzeX (), 0, TApCfg.SzeY ()) ;
where x-max is 50 and y-max = 1000, since the graph has 50 x-values and the y-data is not exceeding 1000
the speed stayed about the same as without it
my setup is a fixed one of 50 x-samples and y = 0 to 1000
i can move the graph via and offset variable in my getY (idx) implementation through a longer buffer
i do not intend to use any built in zoom functions
what would be the conditions where an increase in speed could be noticed
overall i'm very happy with the speed improvement by switching from the (now insane looking) 500 x-samples to 50 samples
i need to either fetch every 10th y-data in my JNI C++ getY (idx) access function or average over all 10 ... todo next
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.