14

我正在开发一个将数据可视化为折线图的 iOS 应用程序。CGPath该图以全屏自定义方式绘制UIView,最多包含 320 个数据点。数据经常更新,需要相应地重新绘制图表——10/秒的刷新率会很好。

到目前为止很容易。然而,我的方法似乎需要大量的 CPU 时间。以每秒 10 次的速度用 320 段刷新图表会导致 iPhone 4S 上进程的 CPU 负载为 45%。

也许我低估了引擎盖下的图形工作,但对我来说,这项任务的 CPU 负载似乎很大。

下面是我的drawRect()函数,每次准备好一组新数据时都会调用它。N保存点的数量,points是一个CGPoint*带有要绘制坐标的向量。

- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

    // set attributes
    CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
    CGContextSetLineWidth(context, 1.f);

    // create path
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddLines(path, NULL, points, N+1);

    // stroke path
    CGContextAddPath(context, path);
    CGContextStrokePath(context);

    // clean up
    CGPathRelease(path); 
}

我尝试先渲染到离线 CGContext 的路径,然后按照此处的建议将其添加到当前层,但没有任何积极的结果。我还摆弄了一种直接绘制到 CALayer 的方法,但这也没什么区别。

有什么建议可以提高这项任务的性能吗?还是我意识到,渲染对 CPU 的工作量更大?OpenGL有什么意义/区别吗?

谢谢/安迪

更新:我也尝试使用UIBezierPath而不是CGPath. 这里的这篇文章很好地解释了为什么这没有帮助。调整CGContextSetMiterLimit等。也没有带来很大的缓解。

更新 #2:我最终切换到了 OpenGL。这是一个陡峭且令人沮丧的学习曲线,但性能提升令人难以置信。然而,CoreGraphics 的抗锯齿算法比 OpenGL 中的 4 倍多重采样做得更好。

4

5 回答 5

9

这里的这篇文章很好地解释了为什么这没有帮助。

它还解释了为什么您的drawRect:方法很慢。

每次绘制时都会创建一个 CGPath 对象。你不需要这样做;您只需在每次修改点集时创建一个新的 CGPath 对象。将 CGPath 的创建移至仅在点集更改时调用的新方法,并在对该方法的调用之间保留 CGPath 对象。只需drawRect:检索它。

您已经发现渲染是您正在做的最昂贵的事情,这很好:您不能让渲染更快,对吗?事实上,drawRect:理想情况下,应该只做渲染,所以你的目标应该是把渲染时间尽可能接近 100%——这意味着尽可能地把其他所有东西都从绘图代码中移出

于 2012-01-03T18:59:33.217 回答
6

根据您制作路径的方式,绘制 300 条单独的路径可能比绘制 300 个点的路径更快。这样做的原因是,绘图算法通常会寻找重叠线以及如何使交叉点看起来“完美” - 当您可能只希望线不透明地相互重叠时。许多重叠和交叉算法的复杂度都在 N**2 左右,因此绘制速度与一条路径中点数的平方成正比。

这取决于您使用的确切选项(其中一些是默认选项)。你需要尝试一下。

于 2012-02-27T16:08:55.270 回答
2

tl;dr:您可以设置drawsAsynchronously底层的属性CALayer,您的 CoreGraphics 调用将使用 GPU 进行渲染。

有一种方法可以控制 CoreGraphics 中的渲染策略。默认情况下,所有 CG 调用都是通过 CPU 渲染完成的,这对于较小的操作来说很好,但对于较大的渲染作业来说效率非常低。

在这种情况下,只需设置drawsAsynchronously底层的属性即可CALayer将 CoreGraphics 渲染引擎切换为基于 Metal 的 GPU 渲染器,从而大大提高性能。在 macOS 和 iOS 上都是如此。

我进行了一些性能比较(涉及几个不同的 CG 调用,包括CGContextDrawRadialGradientCGContextStrokePath和 CoreText 渲染CTFrameDraw),对于更大的渲染目标,性能大幅提升了 10 倍以上。

正如可以预料的那样,随着渲染目标的缩小,GPU 的优势逐渐减弱,直到某个时候(通常对于小于 100x100 左右像素的渲染目标),CPU 实际上实现了比 GPU 更高的帧速率。YMMV,当然这将取决于 CPU/GPU 架构等。

于 2016-11-19T20:48:50.267 回答
0

Have you tried using UIBezierPath instead? UIBezierPath uses CGPath under-the-hood, but it'd be interesting to see if performance differs for some subtle reason. From Apple's Documentation:

For creating paths in iOS, it is recommended that you use UIBezierPath instead of CGPath functions unless you need some of the capabilities that only Core Graphics provides, such as adding ellipses to paths. For more on creating and rendering paths in UIKit, see “Drawing Shapes Using Bezier Paths.”</p>

I'd would also try setting different properties on the CGContext, in particular different line join styles using CGContextSetLineJoin(), to see if that makes any difference.

Have you profiled your code using the Time Profiler instrument in Instruments? That's probably the best way to find where the performance bottleneck is actually occurring, even when the bottleneck is somewhere inside the system frameworks.

于 2012-01-03T17:12:52.863 回答
0

我不是这方面的专家,但我首先要怀疑的是,更新“点”而不是渲染本身可能需要时间。在这种情况下,您可以简单地停止更新点并重复渲染相同的路径,看看它是否需要几乎相同的 CPU 时间。如果没有,您可以专注于更新算法来提高性能。

如果真的是渲染的问题,我认为 OpenGL 肯定会提高性能,因为理论上它会同时渲染所有 320 行。

于 2012-01-03T17:16:04.500 回答