Charts: 性能增强

创建于 2015-04-14  ·  44评论  ·  资料来源: danielgindi/Charts

是否有计划以Philip使用MPAndroidCharts的方式来提高库的性能?
通过他的增强,现在可以在Android上流畅地渲染数千个数据点。
我快速浏览了ios-charts实现,并从中看到它基于原始的MPAndroidCharts实现,而没有最新的性能增强。

使其具有成千上万的点数,对于库来说也是巨大的,因为它将有效地使所有商业版本变得毫无用处(无论如何,其中大多数都是浪费时间和金钱的)。

enhancement help wanted

最有用的评论

我正在为iOS的折线图中加载13000多个记录。 但是,图表会在加载时冻结UI。 同样,在加载后,如果用户选择了任何一点,则也需要花费太多时间来突出显示该选择。

所有44条评论

Android版本中最初的性能瓶颈是渲染代码中有许多额外的(数组)分配。 不应在渲染代码中@ PhilJay已将该代码移动到预先分配的Buffer中,并在渲染时将渲染计算委托给Buffer类。

我选择不将渲染计算移至Buffer类,而只是以相同的方式预分配所需的内存-但将渲染计算与渲染代码置于同一循环中。 这样,管理代码要容易得多。

另外,在额外的函数中不进行渲染计算也可以提高性能,并且数据在渲染代码中仅循环一次,而不是两次。

顺便说一句,在Swift性能方面,FAR还是比Java更好,因此,即使不预先分配这些数组,您也会看到性能提高。

为了以图形方式对其进行解释,这是/正在渲染代码中执行的操作:

  • [开始渲染]
  • 遍历数据集
  • -为该数据集的点分配数组-现在已在两个平台中预分配
  • -计算渲染点-_已移至Android版本中的缓冲区_
  • -渲染这些点
  • [COMMIT RENDERING]

还要注意,在Java中,抽象(函数,类,继承)的价格很高。 Google自己写了一个重要的建议并非没有:

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.

总结一下:

  • 您可以轻松处理数千个数据点!
  • 我试图说服Phil将点计算移回渲染循环,以使其更易于维护;-)

感谢您的解释,丹尼尔。 不过,我不太确定该怎么做。
我所做的是设置了最基本的项目(请参见下面的代码),并且其性能无法与Android版本相提并论,甚至说实话也是如此。 一旦您接近显示最大数量的数据点(完全缩小),即使在大多数最新的iOS设备(如iPad Mini3和iPhone6)上,使用一千个数据点都会导致大量帧丢失。 正如我所说,MPAndroidcharts可以在速度较慢的设备上渲染数千个字符,而不会费力。

覆盖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;
}

我还没有测试所有图表的性能-所以我需要尽快进行测试。
瓶颈可能是我在某处错过的分配...我会测试一下,让您知道! :-)

非常感谢Daniel。 期待您可以从中得到什么。 在iOS上拥有MPAndroidCharts的可行伴侣真是太好了。

好吧,我在LineChartRenderer上玩了一点(与BarChartRenderer相比,它特别慢),Instruments说CGContext的drawPath是罪魁祸首。
我尝试通过改用UIBezierPath来缓解此问题,但是在路径上调用stroke()后,性能是相同的。 在此之前,绘图性能很好,但是路径充满了起点和终点,而不是我们想要的简单折线图。
希望您能找到更多解决方案的运气。

我现在正在对此进行测试,是的,似乎实际的CGContextStrokePath表现不佳。
虚线是主要的击球手之一。 禁用它时,性能会翻倍。 但是它仍然很穷,只有一千分。
我正在阅读Apple文档,以了解如何使CoreGraphics更快

是的,Core Graphics确实在缓慢地渲染路径。 这似乎是由于分辨率而发生的-新型Apple设备上的分辨率非常高,这意味着需要渲染更多像素。

我认为CG是在CPU而不是在GPU上渲染的。 也许有人知道改变这种状况的方法?

CG绝对是使用CPU进行渲染,据我所知,没有办法对此进行更改。
不幸的是,GPU渲染将意味着使用OpenGL。 糟糕的是,与Android在CPU上渲染这些东西相比,CG是如此之慢。

实际上是设备的CPU变慢了。 在大多数情况下,速度非常慢
与使用Android的快速CPU相比,CPU在iOS上提供的体验要好得多。

但是苹果公​​司没有考虑过没有最佳操作系统的情况。
足以保持快速。

我猜想当我们使用很多点时,我们只能避免使用动画-但是
我仍然希望找到一种提高Line性能的方法
基于图表。

似乎特别是路径绘制系统很慢,
玩“斜接和平整度”,并禁用虚线和
抗锯齿-在500-700点的分辨率下,性能会好得多
iPhone 6,但仍然生涩,只有1000

使用UIBezierPath可能会有机会。 正如我所说,只要您不调用“笔画”,该操作就足够快了,这实际上告诉系统该路径是笔画,不应从起点到终点进行填充。 我不知道背景是怎么回事,但这表明图形绘制本身并不是问题。

但是,由于我找不到合适的折线图使用UIBezierPath的方法,因此对于网格线来说这可能是一个可行的选择,因为它可以欺骗绘制路径而不会产生笔触。 即使从API的角度来看这是“错误的”,也应该会产生更好的性能。

UIBezierPath只是围绕CGPath,CGContextDrawPath,
等等。
在某些情况下观察到的性能差异是由于
首先使用的UIBezierPath属性设置CG
抗锯齿,斜接限制等

实际上,当您知道如何使用它时,绝对没有理由使用它
核心图形。

是的,我们确实需要调用“中风”,因为我们确实想要中风该路径...
不填补它。
在大多数情况下,抚摸对系统来说比填充固体更难
颜色,因为它需要照顾线条的宽度,所以连接
线和连接的形状之间。

2015年4月17日,星期五,上午10:50,AlBirdie [email protected]写道:

使用UIBezierPath可能会有机会。 就像我说的那样
只要不打“中风”就足够快
告诉系统路径是笔划,不应从路径中填充
端点的起点。 我不知道这是怎么回事
背景,但这表明图形绘制本身不是
本身发行。

但是,由于我找不到正确使用UIBezierPath的方法
折线图,对于网格线来说,这可能是一个可行的备选方案
可以在不打笔的情况下欺骗绘制路径。 即使那是
从API的角度来看“错误”,它应该会产生更好的性能。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/danielgindi/ios-charts/issues/29#issuecomment -93937181

@danielgindi您是否考虑过,如果动画使速度变慢,如何绘制没有动画的所有线条,并使用覆盖它的遮罩视图,然后对遮罩视图进行动画处理以模仿动画?

好吧,这是个好主意! :)

尽管有一个缺点:
它将削弱您可以使用的动画类型。 在我们允许的情况下
为Y轴设置动画,以使图表“增长”,以及线条填充的方式
在X轴上的位置也与只有笔触时的位置不同。

我的尝试清单是:

  1. 以某种方式增强CoreGraphics或将部分渲染提升到
    显卡

    1. 预渲染所有动画帧-它将延迟动画开始,并且

      消耗大量内存

    2. 您的遮罩创意-限制了动画种类

如果还有其他想法,我很想听听他们的意见!

丹尼尔(Daniel),您是否尝试过将工作转移到GPU?
我真的非常不满意我们目前正在使用的商业解决方案(糟糕的API,严重关闭的状态以及导致应用崩溃的大量错误),并且在MPAndroidCharts上取得了如此巨大的成功,我们希望切换到如果性能不相上下,iOS最终将成为图表。 放心,您的工作将得到回报。 ;)

尝试几种不同的技术,我将在几天之内为您提供更新!

2015年4月20日,星期一,下午12:38,AlBirdie [email protected]写道:

丹尼尔(Daniel),您是否尝试过将工作转移到GPU?
我真的非常不满意我们目前的商业解决方案
使用(糟糕的API,严重关闭,以及大量导致错误的错误,
该应用程序崩溃),并且在MPAndroidCharts上取得了如此巨大的成功,
如果性能可以与之媲美,我最终会切换到iOS图表。 休息
放心,您的工作将得到回报。 ;)

-
直接回复此电子邮件或在GitHub上查看
https://github.com/danielgindi/ios-charts/issues/29#issuecomment -94408574

@AlBirdie您遇到了什么样的性能瓶颈? 我目前拥有一种像ios-charts一样绘制图表的产品,我们已经在内部拥有一个图表库。 我还担心性能,目前,我们只加载100-1000个数据集,现在看来还可以。

我还考虑在将来可能的情况下更改为ios-charts,但是我们的库中的手势可能与ios-charts冲突。

性能麻烦在于,当必须执行以下操作时,动画的帧速率会变慢
在折线图中绘制500-1000条线。

关于手势-我们正在使用标准的UIGestureRecognizers-
可以禁用,修改或使用。 一切都标准化了。 :-)

2015年4月20日,星期一,下午12:53,Xuan [email protected]写道:

@AlBirdie https://github.com/AlBirdie什么样的表现
你遇到瓶颈了吗? 我目前拥有一款仅能绘制图表的产品
像ios-charts一样,我们内部已经有一个图表库。 我也很担心
关于性能,目前,我们只加载100-1000个数据集,似乎还可以
现在。

我还考虑在将来可能的情况下更改为ios-chart,
但是我们的库中的手势可能会与ios图表产生冲突。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/danielgindi/ios-charts/issues/29#issuecomment -94411060

@danielgindi好吧,我认为我们可以使用遮罩视图来克服动画效果...我们的折线图具有渐变层。 作为演示,您的动画可以同时执行X + Y方向,而我们只需执行X方向。 我不确定这把面具能不能帮到您。
我现在的想法是,如果我们有一个向量来描述您的X + Y方向,也许有机会使用遮罩把戏……期待您的结果!

我猜@ liuxuan30只是一般的性能下降。 动画不是问题,因为我不使用动画。 我正在研究财务图表,您需要在一个图表中包含多个数据集(多只股票+一系列指标)。 对于250个项目的数据范围,可以轻松地将多达数千个点加在一起,而在平移和收缩过程中需要一次渲染这些点。 我目前正在使用的商业解决方案可以很好地做到这一点(使用OpenGL,您可以渲染数千个点而不会导致CPU过载),但是我不是封闭源代码库的好朋友,在该库中,您必须等待几个月才能修复错误。
我对ios-charts感到更自在,尤其是因为MPAndroidCharts的API非常易于使用。

我知道,财务数据是灾难。 我们的服务器强制仅将多达1000个数据集发送到移动设备,因此减少了我们的过载。 有机会将OpenGL用于ios图表吗? @danielgindi

OpenGL支持将是王牌。 GPU渲染的折线图可能现在就足够了。
不幸的是,我对OpenGL完全一无所知,否则我很乐意提供帮助。

与使用路径相比,CGContextStrokeLineSegments有一个改进,因此您应该尝试一下。
仍在尝试其他改进方法:-)

谢谢Daniel,我将创建一个小型测试应用程序,将两个版本相互比较,以了解是什么。 使用新的LineSegments后,您获得了哪些性能提升?

@AlBirdie您觉得线段与众不同吗?

我也刚刚意识到:在android中,为了获得最佳性能,请将其设置为硬件层-在这种情况下,虚线不是虚线,它们都是固定的。

因此,首先,如果您在iOS上禁用了虚线,那么您的使用也会得到显着提高!

但是我想我可以在CIImage上使用OpenGL ES来绘制它,但是需要非常小心,因为如果在应用程序处于非活动状态时运行了一条GL行,它将崩溃。

另外,我仍然可以通过预渲染纹理来允许破折号。 这将需要一些工作,这不是我的主要任务,但是我正在开始一个OpenGL层的辅助项目,该项目可以无缝替换CGContext。

除了使用OpenGL进行绘制外,您还可以尝试将其降低到“核心动画”。 也许CAShapeLayer类可以工作。 Apple建议在渲染复杂路径时使用多个CAShapeLayer s,因此您可能需要将线段分成多个段。 使用Core Animation应该将工作移至GPU。

多年来,在我的日常工作中,我们一直使用@AlBirdie上面提到的商业产品来广泛使用OpenGL,从而绕过CoreGraphics处理注释以外的所有内容。 当然,他们似乎能够以相当平滑的渲染处理相当大的数据集,并且他们声称为此使用了GPU。 然而,他们的方法带来了巨大的麻烦,尤其是它们似乎无法被解决的错误,例如异步渲染调用在应用程序进入后台时在OpenGL中崩溃,愚蠢的类结构以及对可自定义性的疯狂限制。 我想要的东西今天不存在,但是在iOS图表中,我看到这可能是到达那里的方式。 无论如何,对我而言,可靠性每次都会胜过性能,但这是微不足道的,两者都很重要。

我认为ps是计算机无法选择以彩色或什至灰度渲染的日子的宿醉-除非我们将其渲染为纯黑白并制作看起来像旧的东西,否则不需要它们凸版印刷的教科书,当然可以吗? ;)因此,针对那些优化进行优化远不如针对一般的10K点情况进行优化重要。

绝对与您@onlyforart有关虚线。
鉴于您对您的商业图表库的描述,我想知道我们是否一直在使用相同的产品。 :)我们终于顺便抛弃了它。 我花了三个月的大部分时间来实施(即使那还不足以解决严重的错误和限制(例如,十字准线),而使用iOSCharts仅仅花了两个星期的时间(“愚蠢的类结构”;)。 浪费时间和金钱。

@AlBirdie是的。 甚至不提十字准线。 借助过多的Newcastle Brown Ale制成,并且毫无疑问地瞄准了企业市场(粗糙的API实际上是一个积极的销售因素,因为它使客户陷入了昂贵的支持/维护周期)。 你知道那些家伙的历史吗? 实际上(在很久以前)他们是软件行业的真正先驱。 无论如何,现在该继续前进,展望未来。
我们主要绘制烛台图(这些是金融行业应用程序)。 对于iOS和Android,我真正想要的是使用HTML5 / Javascript世界中的HighCharts / HighStock实际上非常容易的事情:

  1. 大量的数据点(10K或更多)
  2. 自动将OHLC数据合并为合理的比例尺(例如,当缩放系数合适时显示1h条,缩小时显示1m或1s条)
  3. 平滑平移和滚动
  4. 捏放大
  5. 覆盖多个数据系列以及注释和十字线,有的以编程方式,有的通过用户交互(例如设置限价)覆盖,所有这些都平滑呈现
  6. (这是踢球者-HighStock对此无济于事!尽管对我来说似乎很明显)(以超前方式)增量获取数据,例如,获取整个数据集的低分辨率数据,以及与地图应用程序一样,获取高分辨率数据(例如毫秒“刻度”数据)仅适用于图表的查看区域
    尽管我们不在“待办事项”列表中,但我们仍未使用iOS图表,因此它并没有排在首位,因此我还没有深入研究代码内部,但我会尽早查看-大概是下个月-我会研究所有这些。
    回到渲染,如果情况不尽如人意,OpenGL崩溃的趋势令人担忧。 GLKit可以帮助绘制GPU。 我还想知道不久前SpriteKit是否可以提供帮助(图表渲染距离制作游戏还不到一百万英里),但是现在GLKit似乎是一个更好的选择。

大声笑@onlyforart ,你在纽卡斯尔·布朗·艾尔大学有我。 :+1::)
关于您的要求,我是同一人(也从事金融产品),但是,是的,让我们回到这些图表的实际图形上。 期待您将来的发现。

@onlyforart@AlBirdie感谢您的见解:-)

看到人们为我们的图书馆放弃商业企业产品真是太好了,尽管我为他们感到难过……我很矛盾!

我很犹豫让OpenGL占有一席之地,因为我知道如果管理不当可能会导致崩溃,而在UIKit应用程序中这实际上是不可能的。 如果您使用OpenGL来创建游戏,则整个过程都是OpenGL画布,而不必担心UIKit调用会在后台导致OpenGL渲染的问题。

@onlyforart您关于虚线的观点是正确的,但不幸的是,我曾与要求使用虚线的客户有经验。 在Android上存在一种情况,要提高性能,开发人员必须将层更改为硬件层,并告诉客户虚线将变为实线,这就是成本。 当然,可以选择将所有绘图代码都移至OpenGL,但是随后绘制一条直线是一件令人头疼的事,但是您可以为虚线创建纹理并使用它。 他们不想为此付出代价,所以虚线当然不是那么关键,但是确实引起了很大的反响。

给自己的注释:尝试使用GLKit进行渲染。 走着瞧吧。

如果我接受您的清单:

  1. _“大量的数据点(10K或更多)” _-我们正在研究:)我不能说Android,因为将其移至GL将是更大的PITA,但截至目前,它已经具有相当不错的性能利用硬件层,这是在GPU上进行的所有绘制。
  2. _“将OHLC数据自动合并为合理的比例尺” _-我们将很快使用近似滤波器,因此我认为可以解决这一问题。 而不是拥有许多您无法阅读的1m条,您将获得大约1小时条等。
  3. _“流畅的平移和滚动” _-完成:-)
  4. _“捏放大” _-完成:-)
  5. _“覆盖多个数据系列以及注释和十字线” _-我不知道我是否理解正确,但是目前存在可以合并多个图表类型的组合图表,并且每种图表类型都可以接受多个数据集。 此外,您始终可以连接到ViewPortHandler并获取坐标,以便可以将所需的任何内容叠加在char上
  6. 增量获取数据-我们计划在抽象数据源的同时,仍旧使用旧的API来设置静态数据集(它只是与之兼容的内置数据源)。 我想这也符合您的要求,但我们尚不知道何时达成

@danielgindi ,不要为他们感到

关于第二个要求,如果你们要实现这一点,它必须是可选的。 我们在其中一种图表产品中进行了这种合并,而客户正在稳步转向固定频率。 事实证明,在缩放过程中更改频率不仅会使普通的财务图表用户感到困惑,而且使高级用户感到烦恼,因为他们想要固定的频率。

我们从来没有想过将其强加给用户! 您可以看到代码已经包含过滤器,并且在MPAndroidCharts中可以看到,过滤器历史上是由设置过滤器(任何自定义过滤器或内置过滤器)的属性启用的,但由于结构更改后来被删除了。

当我们实现它时,功能将保持不变-这将是另一个很酷的功能:-)

@AlBirdie Re“关于第二个要求,如果你们要实现这一点,它必须是可选的。我们在其中的一种图表产品中已经进行了这种合并,而客户正在稳步向固定频率过渡。更改事实证明,缩放期间的频率不仅使普通的财务图表用户感到困惑,而且使高级用户感到恼火,因为他们想要固定的频率。” 完全同意-这是供信息/非交易用户使用的图表。 交易用户有不同的需求。

@danielgindi关于“抽象数据源”,如果有时间的话,这可能是我们可以做的事情。 没有承诺,但我将其添加到我们的积压中。

是否有人玩过将绘图代码拆分为单独的线程(CALayer.drawsAsynchronously)? 如果这允许绘制额外的图表,每个数据集,网格和轴,则可能会有所帮助。 鉴于我对CoreGraphics的非常不起眼的经验(阅读;一无所获;-)),我还没有进行任何实验,只是在寻找GLKit以及如何改善图表性能时才发现它。

我需要显示一个包含10k数据点的烛台图,因此我在CandleStickChartRenderer.drawDataSet()上进行了一些时间测量。

事实证明,大部分时间都花在了调用dataSet.entryIndex(第76,77行)上。

我可能会弄错,但data_setup.entryIndex()调用似乎是多余的,因为_minX,_maxX值始终等于从dataSet.entryIndex()返回的minx,maxx

我已经成功制作了一个烛光图表,其中包含了10k个数据点的平移/缩放。 通过改变
CandleStickChartRenderer.swift,第76,77行来自:

var minx = max(dataSet.entryIndex(entry:entryFrom,isEqual:true),0);
var maxx = min(dataSet.entryIndex(entry:entryTo,isEqual:true)+ 1,entry.count);

var minx = max(_minX,0);
var maxx = _maxX + 1;

我对LineChartRenderer进行了相同的更改,并能够显示带有2个数据系列的组合烛台/折线图(每个数据点10k数据点)

哇, @dorsoft可以大大提高

我已经测试过了,我简直不敢相信自己的眼睛。 即使在具有4个组合图的iPad 2上最多显示三个数据集(每个数据集各有250个数据点)和y轴的自动最小/最大计算,我们现在也能够以相当平滑的方式同时平移和缩放所有图表。 它不是60fps,而是接近。 这种旧设备和WAY(!)的速度比我们之前讨论的商用OpenGL解决方案快,给人留下深刻的印象。

我想我必须尝试一下:)并且还需要与Phil讨论以
了解是否有任何影响...

我刚刚读了这篇,看起来很有趣,但是需要进行一些严格的测试才能确定它是否真正适用于所有场景:-)

我创建了具有蜡烛图性能增强功能的PR。
使用自动定标的最小/最大和零值对增强进行了彻底测试。

我发现了可能的瓶颈。 在一个实时图表中,每秒向图表中添加许多条目,我注意到在每个EACH values.append(e)之后,都会自动调用数据集的calcMinMax。

calcMinMax函数使用.forEach确定最小值和最大值,这导致CPU在循环使用这些值时花费了60%以上,而且在主线程中。

我正在执行一些实验来禁用最小/最大计算或使用本机for循环而不是array .forEach

@danielgindi请看一下,真的需要所有值calcMinMax吗? 我在代码中手动计算了最小/最大Y值,并且仅对可见值进行了计算,因此也许您可以暴露一些布尔值以启用/禁用最小/最大自动计算,并提高性能,删除forEach函数并避免函数调用。

```
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) }
}

```

@samueleperricone请将此票单独

@jjatie谢谢,刚打开#3166

我正在为iOS的折线图中加载13000多个记录。 但是,图表会在加载时冻结UI。 同样,在加载后,如果用户选择了任何一点,则也需要花费太多时间来突出显示该选择。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

guanyanlin picture guanyanlin  ·  3评论

heumn picture heumn  ·  3评论

brytnvmg picture brytnvmg  ·  4评论

newbiebie picture newbiebie  ·  3评论

cilasgimenez picture cilasgimenez  ·  4评论