Objective c 频繁绘制CGP路径时的性能

Objective c 频繁绘制CGP路径时的性能,objective-c,ios,core-graphics,cgpath,Objective C,Ios,Core Graphics,Cgpath,我正在开发一个iOS应用程序,它将数据可视化为一个折线图。图形在全屏自定义ui视图中绘制为CGPath,最多包含320个数据点。数据经常更新,图形需要相应地重新绘制–刷新率为10/秒就好了 到目前为止很容易。然而,我的方法似乎占用了很多CPU时间。在iPhone4S上,以每秒10次的速度刷新320个段的图形会导致该进程45%的CPU负载 也许我低估了引擎盖下的图形工作,但对我来说,CPU负载对于这项任务来说似乎很大 下面是我的drawRect()函数,每次新数据集准备就绪时都会调用该函数N保存点

我正在开发一个iOS应用程序,它将数据可视化为一个折线图。图形在全屏自定义
ui视图中绘制为
CGPath
,最多包含320个数据点。数据经常更新,图形需要相应地重新绘制–刷新率为10/秒就好了

到目前为止很容易。然而,我的方法似乎占用了很多CPU时间。在iPhone4S上,以每秒10次的速度刷新320个段的图形会导致该进程45%的CPU负载

也许我低估了引擎盖下的图形工作,但对我来说,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
。这篇文章很好地解释了为什么这没有帮助。调整CGContextSetMiterLit等也没有带来很大的缓解


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

您尝试过使用UIBezierPath吗?UIBezierPath在引擎盖下使用CGPath,但看看性能是否因为某种微妙的原因而有所不同会很有趣。发件人:

要在iOS中创建路径,建议使用UIBezierPath 而不是CGPath函数,除非您需要某些功能 这只有核心图形提供,例如向路径添加椭圆。 有关在UIKit中创建和渲染路径的详细信息,请参见“绘制形状” 使用贝塞尔路径。”

我还会尝试在CGContext上设置不同的属性,特别是使用
CGContextSetLineJoin()
设置不同的行连接样式,看看这是否有什么不同


您是否使用Instruments中的时间分析器工具分析了代码?这可能是找到性能瓶颈实际发生位置的最佳方法,即使瓶颈在系统框架内。

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

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

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

它还解释了
drawRect:
方法速度慢的原因

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


你已经发现渲染是你正在做的最昂贵的事情,这是好事:你不能让渲染更快,不是吗?实际上,
drawRect:
理想情况下应该只进行渲染,因此您的目标应该是将渲染所花费的时间尽可能接近100%——这意味着将所有其他内容尽可能从绘图代码中移出。

取决于您的路径,可能绘制300条单独的路径比绘制一条包含300个点的路径要快。这样做的原因是,绘图算法通常会找出重叠线,以及如何使交点看起来“完美”——而您可能只希望这些线彼此不透明地重叠。许多重叠和相交算法的复杂度为N**2左右,因此绘制速度与一条路径中点数的平方成正比


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

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

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

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

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

正如可以预期的那样,当渲染目标收缩时,GPU advantage会逐渐消失,直到在某个点(通常对于小于100x100左右像素的渲染目标),CPU实际上会停止