SciChart Android实时绘图:如何最大程度地提高绘图速度? [英] SciChart Android Real Time Graphing: How to maximize graphing speed?

查看:214
本文介绍了SciChart Android实时绘图:如何最大程度地提高绘图速度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Scichart for android编写实时绘图应用程序.我一直在使用

I'm writing a real time graphing app using Scichart for android. I've been using

FastLineRenderableSeries 作为我的数据系列的包装器

FastLineRenderableSeries as a wrapper for my data series

但是我想知道 Android SciChart还有哪些其他技术可以最大化绘图速度?

特别是当我使用 IXyDataSeries 并将x轴大小从10,000增加到100,000 pts时,性能下降了.在向 IXyDataSeries 添加约90,000点之前,绘图速度始终保持稳定.

Particularly I've noticed a decrease in performance when I use IXyDataSeries and increase the x axis size to 100,000 pts from 10,000. The speed of the graphing stays consistently fast until I've added about 90,000 points to my IXyDataSeries.

谢谢大家.我是stackoverflow的新手,而不是CS专家,更是一个机械工程师.

Thanks guys. I'm new to stackoverflow... more of a mechE than a CS person.

这是我的graphFragment类,该类将UDP传感器数据作为字符串接收,进行拼接并将其添加到IXyDataSeries.

Here is my graphFragment class which takes in UDP sensor data as a string, splices it and adds it to the IXyDataSeries.

public class GraphFragment extends Fragment { 

    //Various fields...
    //UDP Settings
    private UdpClient client;
    private String hostname;
    private int remotePort;
    private int localPort;

    //Use to communicate with UDPDataClass
    private Handler handler;

    private boolean listenerExists = false;
    private int xBound = 100000; //**Graphing Slows if xBound is TOO large**
    private int yBound = 5000;
    private boolean applyBeenPressed = false;

    private GraphDataSource dataSource; //Gets data from UDPDataClass
    private SciChartSurface plotSurface; //Graphing Surface
    protected final SciChartBuilder sciChartBuilder = SciChartBuilder.instance();

    //Data Series containers
    //Perhaps it would be better to use XyySeries here?
    private final IXyDataSeries<Double, Double> dataSeriesSensor1 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor2 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor3 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor4 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor5 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor6 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private ArrayList<IXyDataSeries<Double,Double>> dataSeriesList = new ArrayList<>(Arrays.asList(dataSeriesSensor1,dataSeriesSensor2,dataSeriesSensor3,dataSeriesSensor4, dataSeriesSensor5, dataSeriesSensor6));
    private ArrayList<Double> xCounters = new ArrayList<>(Arrays.asList(0.0,0.0,0.0,0.0,0.0,0.0));

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View frag = inflater.inflate(R.layout.graph_fragment, container, false);

    plotSurface = (SciChartSurface) frag.findViewById(R.id.dynamic_plot);

    dataSource = new GraphDataSource(); //Run the data handling on a separate thread
    dataSource.start();

    UpdateSuspender.using(plotSurface, new Runnable() {
        @Override
        public void run() {
            final NumericAxis xAxis = sciChartBuilder.newNumericAxis().withVisibleRange(0,xBound).build();
            final NumericAxis yAxis = sciChartBuilder.newNumericAxis().withVisibleRange(0,yBound).build();

            //These are wrappers for the series we will add the data to...It contains the formatting
            final FastLineRenderableSeries rs1 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor1).withStrokeStyle(ColorUtil.argb(0xFF, 0x40, 0x83, 0xB7)).build(); //Light Blue Color
            final FastLineRenderableSeries rs2 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor2).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0xA5, 0x00)).build(); //Light Pink Color
            final FastLineRenderableSeries rs3 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor3).withStrokeStyle(ColorUtil.argb(0xFF, 0xE1, 0x32, 0x19)).build(); //Orange Red Color
            final FastLineRenderableSeries rs4 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor4).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0xFF, 0xFF)).build(); //White color
            final FastLineRenderableSeries rs5 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor5).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0xFF, 0x99)).build(); //Light Yellow color
            final FastLineRenderableSeries rs6 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor6).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0x99, 0x33)).build(); //Light Orange color

            Collections.addAll(plotSurface.getXAxes(), xAxis);
            Collections.addAll(plotSurface.getYAxes(), yAxis);
            Collections.addAll(plotSurface.getRenderableSeries(), rs1, rs2, rs3, rs4, rs5, rs6);
        }
    });

    return frag;
    }

 //This class receives the UDP sensor data as messages to its handler
 //Then it splices the data
 //Adds the data to the IXySeries
 //Then the UpdateSuspender updates the graph
 //New data arrives approx every 50 ms (around 20x a second)
 //Graphing slows when xAxis is increased to ~100,000
 //X data is only counters...Only care about Y data
 public class GraphDataSource extends Thread{

    public void run(){
        Looper.prepare();
        //Get Data from UDP Data Class when its available
        handler = new Handler(){
            public void handleMessage(Message msg){
                String sensorData = msg.getData().getString("data"); //Data receiveds
                if(dataValid(sensorData)){
                    sensorData = sensorData.replaceAll("\\s", "");
                    final String[] dataSplit = sensorData.split(","); //split the data at the commas

                    UpdateSuspender.using(plotSurface, new Runnable() {    //This updater graphs the values
                            @Override
                            public void run() {
                                spliceDataAndAddData(dataSplit);
                            }
                        });
                }
            }
        };
        Looper.loop();
    }

    /**
     *
     * @param data string of the udp data
     * @return true if the data isn't corrupted..aka the correct length
     */
    private boolean dataValid(String data){
        return ((data.length() == 1350));
    }

    /**
     *
     * @param dataSplit String[] of the entire data
     *  Adds the each sensor data to the IXySeries representing the data
     */
    private void spliceDataAndAddData(String[] dataSplit){
        addToSensorSeries(dataSplit, 1);
        addToSensorSeries(dataSplit, 2);
        addToSensorSeries(dataSplit, 3);
        addToSensorSeries(dataSplit, 4);
        addToSensorSeries(dataSplit, 5);
        addToSensorSeries(dataSplit, 6);
    }

    /**
     *
     * @param dataSplit data to split into individual sensor array
     *                  must contain only string representations of numbers
     * @param sensorSeriesNumber which sensors to collect the data points of
     * Adds the data to the corresponding IXySeries 
     */
    private void addToSensorSeries(String[] dataSplit, int sensorSeriesNumber){
        sensorSeriesNumber -= 1;  //Adds each value individually to the series
        double xcounter = xCounters.get(sensorSeriesNumber);
        int i = sensorSeriesNumber;
        int dataSize = dataSplit.length - 1;
        String num = "";
        while(true){
            if(i < 6){ //This is the base case...add the first set of data
                num = dataSplit[i];
                try {
                    if(xcounter > xBound){
                        xcounter = 0;
                        dataSeriesList.get(sensorSeriesNumber).clear();
                    }
                    dataSeriesList.get(sensorSeriesNumber).append(xcounter, Double.parseDouble(num)); //appends every number...
                }catch (Exception e){
                    //Corrupt data
                }
            }else if((i) <= dataSize && i >= 6){ //Will start to get hit after the second time
                num = dataSplit[i];
                try {
                    if(xcounter > xBound){
                        xcounter = 0;
                        dataSeriesList.get(sensorSeriesNumber).clear();
                    }
                    dataSeriesList.get(sensorSeriesNumber).append(xcounter, Double.parseDouble(num));
                }catch (Exception e){
                    //Corrupt data
                }
            }else{
                break;
            }
            xcounter++;
            i += 6;
        }
        xCounters.set(sensorSeriesNumber,xcounter);
    }
}

推荐答案

我查看了您的代码,但不确定我们是否可以对此做些什么.您的示例包含6个XyDataSeries,其XRange从0到100000,这在屏幕上给出了600 000点,对于HTC One上的实时示例而言,这是相当不错的.在SciChart性能演示中,您只能看到3个XyDataSeries实例的用法,该实例允许在每个系列中绘制更多点.

I took a look on you code and I'm not sure if we can do something about it. Your example contains 6 XyDataSeries with XRange from 0 to 100000 this gives 600 000 points on screen which is pretty good for realtime example on HTC One. In SciChart performance demo you can see usage of only 3 XyDataSeries instances which allows to draw more points in each series

披露:我是SciChart Android团队的首席开发人员

但是我认为通过在代码中添加一些优化,您可以获得很少的额外FPS.实时图表中的主要问题在于更新图表的代码中-它经常被调用,因此,如果您在更新过程中创建了一些对象并且不保存它,则可能会由于Android中的GC而引起问题(GC传递缓慢且可以在GC收集所有未使用的对象时暂停所有应用程序的线程).所以我建议您下一步:

But I think you can get few extra FPS by adding some optimizations in your code. The main problem in realtime charts is in code which updates a chart - it is called very often so if you create some objects during update and don't save it then this could cause problem because of GC in Android (GC pass is slow and it can pause all application's threads while GC is collecting all unused objects). So I would suggest you to do next:

  • 我建议增加堆大小您的用途:更多的内存应用程序-如果您有效地使用内存,则它执行的GC会更少.
  • 尝试减少装箱/拆箱的数量,并在代码中频繁分配的对象(例如数据系列更新)中分配较少的对象.基本上,您需要忘记在更新数据系列的回调中创建任何对象.在您的代码中,我注意到很少有发生装箱/拆箱的地方.每秒都会调用此代码,并在循环中调用某些方法,因此装箱/拆箱的效果可能会严重影响应用程序的性能:
  • I would suggest to increase heap size in your aplication: more memory application has - less GC it performs if you use memory efficiently.
  • Try to reduce amount of boxing/unboxing and allocate less objects in code which is called frequntly(such as data series updates). Basically you need to forget about creation of any objects in callbacks which update data series. In your code I noticed few places where boxing/unboxing occurs. This code is called every second and some methods are called in a loop so effect of boxing/unboxing can significantly affect performance of your application:

dataSeriesList.get(sensorSeriesNumber).append(xcounter, Double.parseDouble(num));

double xcounter = xCounters.get(sensorSeriesNumber);

xCounters.set(sensorSeriesNumber,xcounter);

我建议您使用

I would suggest you to use append override which accepts IValues. Usage of append which accepts IValues allows to avoid unnecessary boxing/unboxing of primitive types when you append alot of data very frequently.

  • 我也建议使用Float或Integer,除非在创建XyDataSeries时确实需要Double.这样可以潜在地减少一半的内存消耗(8个字节用于存储双精度字节,而4个字节用于存储int/float),因此应用程序具有更多的可用内存,从而可以减少执行GC的频率.

希望这会对您有所帮助.

Hope this will help you.

这篇关于SciChart Android实时绘图:如何最大程度地提高绘图速度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆