简体   繁体   中英

androidplot 1.4.3 redraw run-time analysis with JNI getY (idx) access

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM