objective-c – 重用一个导致奇怪性能损失的CGContext

我的课堂是在屏幕外面渲染图像。我认为重复使用CGContext而不是一次又一次地为每个图像创建相同的上下文将是一件好事。我设置了一个成员变量_imageContext,所以我只需要创建一个新的上下文,如果_imageContext是这样的:

if(!_imageContext)
    _imageContext = [self contextOfSize:imageSize];

代替:

CGContextRef imageContext = [self contextOfSize:imageSize];

当然,我不再发布CGContext了。

这些是我做出的唯一的改变,结果是重用上下文将渲染速度从10ms延迟到60ms。我错过了什么吗?在再次绘制之前,我必须清除上下文吗?还是重新创建每个图像的上下文的正确方法

编辑

找到最奇怪的连接..

当我在搜索应用程序的内存在应用程序开始渲染图像时令人难以置信的原因,我发现问题是我将渲染的图像设置为NSImageView。

imageView.image = nil;
imageView.image = [[NSImage alloc] initWithCGImage:_imageRef size:size];

看起来ARC并没有释放以前的NSImage。第一种避免这种情况的方法是将新的形象画成旧的形象。

[imageView.image lockFocus];
[[[NSImage alloc] initWithCGImage:_imageRef size:size] drawInRect:NSMakeRect(0,size.width,size.height) fromrect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
[imageView.image unlockFocus];
[imageView setNeedsdisplay];

内存问题已经消失,CGContext重用问题发生了什么?
不重用上下文现在需要20ms而不是10ms – 当然,绘制图像比仅仅设置它更长。
重用上下文也需要20ms而不是60ms。但为什么?我看不到有任何连接,但是我可以通过设置NSImageView的图像而不是绘制它来再现旧的状态,重复使用需要更多的时间。

解决方法

我调查了这一点,我观察到同样的放缓。查看仪器设置为采样内核调用以及用户调用显示了罪魁祸首。 @ RyanArtecona的评论是正确的。我在两个测试运行(一个重用上下文,另一个重新创建一个新的上下文)中,将最下面的用户名调用CGSColorMaskcopyARGB8888_sse集中在一起,然后将生成调用树反转。在上下文不被重用的情况下,我看到最重的内核跟踪是:

Running Time    Self            Symbol Name
668.0ms   32.3% 668.0           __bzero
668.0ms   32.3% 0.0              vm_fault
668.0ms   32.3% 0.0               user_trap
668.0ms   32.3% 0.0                CGSColorMaskcopyARGB8888_sse

这是通过CGSColorMaskcopyARGB8888_sse访问它们而导致内存归零的内存页面。这意味着CGContext将VM页面映射到位图上下文,但内核实际上并没有执行与该操作相关联的工作,直到有人实际访问该内存。实际的映射/故障发生在第一次访问。

现在我们来看看最重的内核跟踪,当我们重用上下文时:

Running Time            Self            Symbol Name
1327.0ms   35.0%        1327.0          bcopy
1327.0ms   35.0%        0.0              user_trap
1327.0ms   35.0%        0.0               CGSColorMaskcopyARGB8888_sse

这是内核复制页面。我的钱将是这样的底层写作机制,提供@RyanArtecona在他的评论中所谈论的行为:

In the Apple docs for CGBitmapContextCreateImage,it says the actual
bit-copying operation doesn’t happen until more drawing is done on the
original context.

在设计的情况下,我曾经测试过,非重用案例执行了3392ms,重用案例花费了4693ms(明显慢)。考虑到每种情况下只有单一最重的踪迹,内核跟踪表明我们在第一次访问时花费668.0ms零填充新页面,在图像获取参考后,首次写入时写入写入页上的页面为1327.0ms到那些页面。这是659ms的差异。两者之间的差距仅占这两个差距的50%。

所以,为了减少一点,非重用的上下文更快,因为当您创建上下文时,它知道这些页面是空的,并且没有其他人引用这些页面来强制它们在写入时被复制他们。当您重复使用上下文时,页面由其他人(您创建的映像)引用,并且必须在第一次写入时复制,以便在上下文的状态更改时保留映像的状态。

您可以通过在调试器中逐步查看进程的虚拟内存映射来进一步探索这里发生的情况。 vmmap是有用的工具。

实际上,您应该每次都可以创建一个新的CGContext。

相关文章

我正在用TitaniumDeveloper编写一个应用程序,它允许我使用Ja...
我的问题是当我尝试从UIWebView中调用我的AngularJS应用程序...
我想获取在我的Mac上运行的所有前台应用程序的应用程序图标....
我是一名PHP开发人员,我使用MVC模式和面向对象的代码.我真的...
OSX中的SetTimer在Windows中是否有任何等效功能?我正在使用...
我不确定引擎盖下到底发生了什么,但这是我的设置,示例代码和...