iOS / Core-Animation:性能调优 [英] iOS / Core-Animation: Performance tuning

查看:119
本文介绍了iOS / Core-Animation:性能调优的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在iPad上运行了自己的应用。但它的表现非常糟糕 - 我的速度低于15fps。任何人都可以帮我优化吗?

I have my app running on my iPad. but it is performing very badly -- I am getting below 15fps. can anyone help me to optimise?

它基本上是一个包含12个按钮(来自UIControl)的轮子(来自UIView)。

It is basically a wheel (derived from UIView) containing 12 buttons (derived from UIControl).

作为用户旋转它,按钮动态扩展和收缩(例如,12点钟位置的那个应该总是最大的)

As the user spins it, the buttons dynamically expand and contract (e.g. the one at the 12 o'clock position should always be the biggest)

所以我的车轮包含:

- (void) displayLinkIsCallingBack: (CADisplayLink *) dispLink 
{
    :
    // using CATransaction like this goes from 14fps to 19fps
    [CATransaction begin];
    [CATransaction setDisableActions: YES];

    // NEG, as coord system is flipped/fucked
    self.transform = CGAffineTransformMakeRotation(-thetaWheel);

    [CATransaction commit];

    if (BLA)
        [self rotateNotch: direction];  
}

...从最近的触摸输入计算车轮的新旋转。这里已经存在一个性能问题,我在一个单独的线程上追求: iOS核心动画:CATransaction / Interpolating转换矩阵的性能问题

… which calculates from recent touch input the new rotation for the wheel. There is already one performance issue here which I am pursuing on a separate thread: iOS Core-Animation: Performance issues with CATransaction / Interpolating transform matrices

此例程还检查轮子是否已完成另外1/12的旋转,并且if if指示所有12个按钮调整大小:

This routine also checks whether the wheel has completed another 1/12 rotation, and if so instructs all 12 buttons to resize:

// Wheel.m
- (void) rotateNotch: (int) direction
{
    for (int i=0; i < [self buttonCount] ; i++)
    {
            CustomButton * b = (CustomButton *) [self.buttons objectAtIndex: i];

    // Note that b.btnSize is a dynamic property which will calculate
    // the desired button size based on the button index and the wheels rotation.
            [b resize: b.btnSize];
    }
}

现在,对于实际的调整大小代码,请在button.m中:

Now for the actual resizing code, in button.m:

// Button.m

- (void) scaleFrom: (float) s_old
                to: (float) s_new
              time: (float) t
{
    CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath: @"transform.scale"];

    [scaleAnimation setDuration:  t ];
    [scaleAnimation setFromValue:  (id) [NSNumber numberWithDouble: s_old] ];
    [scaleAnimation setToValue:  (id) [NSNumber numberWithDouble: s_new] ];
    [scaleAnimation setTimingFunction:  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut] ];
    [scaleAnimation setFillMode: kCAFillModeForwards];
    scaleAnimation.removedOnCompletion = NO;

    [self.contentsLayer addAnimation: scaleAnimation 
                              forKey: @"transform.scale"];

    if (self.displayShadow && self.shadowLayer)
        [self.shadowLayer addAnimation: scaleAnimation 
                                forKey: @"transform.scale"];    

    size = s_new;
}

// - - - 

- (void) resize: (float) newSize
{
    [self scaleFrom: size
                 to: newSize
               time: 1.];
}

我想知道问题是否与多个transform.scale操作的开销有关排队 - 每个按钮调整大小需要一整秒才能完成,如果我快速旋转车轮,我可能每秒旋转几转;这意味着每个按钮每秒调整大小24次。

I wonder if the problem is related to the overhead of multiple transform.scale operations queueing up -- each button resize takes a full second to complete, and if I am spinning the wheel fast I might spin a couple of revolutions per second; that means that each button is getting resized 24 times per second.

**创建按钮的图层**

** creating the button's layer **

我猜的最后一块拼图是看看按钮的内容层。但我试过了

The final piece of the puzzle I guess is to have a look at the button's contentsLayer. but I have tried

contentsLayer.setRasterize = YES;

应该有效地将其存储为位图。因此,设置代码实际上会动态调整12位图。

which should effectively be storing it as a bitmap. so with the setting the code is in effect dynamically resizing 12 bitmaps.

我无法相信这会使设备超出其极限。然而,核心动画乐器却告诉我;当我旋转滚轮时(通过用手指拖动圆圈),它报告〜15fps。

I can't believe this is taxing the device beyond its limits. however, core animation instrument tells me otherwise; while I am rotating the wheel (by dragging my finger in circles), it is reporting ~15fps.

这不好:我最终需要在里面放一个文本层每个按钮,这将进一步拖累性能(...除非我使用上面的.setRasterize设置,在这种情况下它应该是相同的)。

This is no good: I eventually need to put a text layer inside each button and this is going to drag performance down further (...unless I am using the .setRasterize setting above, in which case it should be the same).

一定有些事情我做错了!但是什么?

There must be something I'm doing wrong! but what?

编辑:这是负责生成按钮内容层的代码(即带阴影的形状):

here is the code responsible for generating the button content layer (ie the shape with the shadow):

- (CALayer *) makeContentsLayer
{
    CAShapeLayer * shapeOutline = [CAShapeLayer layer];
    shapeOutline.path = self.pOutline;

    CALayer * contents = [CALayer layer];

    // get the smallest rectangle centred on (0,0) that completely contains the button
    CGRect R = CGRectIntegral(CGPathGetPathBoundingBox(self.pOutline)); 
    float xMax = MAX(abs(R.origin.x), abs(R.origin.x+R.size.width));
    float yMax = MAX(abs(R.origin.y), abs(R.origin.y+R.size.height));
    CGRect S = CGRectMake(-xMax, -yMax, 2*xMax, 2*yMax);
    contents.bounds = S; 

    contents.shouldRasterize = YES; // try NO also

    switch (technique)
    {
        case kMethodMask:
            // clip contents layer by outline (outline centered on (0, 0))
            contents.backgroundColor = self.clr; 
            contents.mask = shapeOutline;
            break;

        case kMethodComposite:
            shapeOutline.fillColor = self.clr;
            [contents addSublayer: shapeOutline];
            self.shapeLayer = shapeOutline;
            break;

        default:
            break;
    }

    if (NO)
        [self markPosition: CGPointZero
                   onLayer: contents ];

    //[self refreshTextLayer];

    //[contents addSublayer: self.shapeLayerForText];

    return contents;
}

你可以看到,我正在尝试一切可能的方法,我正在尝试制作形状的两种方法,另外我切换.shouldRasterize

as you can see, I'm trying every possible approach, I am trying two methods for making the shape, and separately I am toggling .shouldRasterize

**损害UI设计以获得可容忍的帧速率**

** compromising the UI design to get tolerable frame rate **

编辑:现在我已经尝试禁用动态调整大小行为,直到轮子进入新位置,然后设置wheel.setRasterize = YES。所以它有效地旋转了一个预先渲染的UIView(它肯定占据了大部分屏幕)在手指下面(它很高兴地做~~ 60fps),直到轮子停下来,此时它执行这个滞后的大小调整动画( @< 20fps)。

Now I have tried disabling the dynamic resizing behaviour until the wheel settles into a new position, and setting wheel.setRasterize = YES. so it is effectively spinning a single prerendered UIView (which is admittedly taking up most of the screen) underneath the finger (which it happily does @~60fps), until the wheel comes to rest, at which point it performs this laggy resizing animation (@<20fps).

虽然这给出了一个可以容忍的结果,但我不得不以这种方式牺牲我的UI设计。我确信我一定做错了。

while this gives a tolerable result, it seems nuts that I am having to sacrifice my UI design in such a way. I feel sure I must be doing something wrong.

编辑:我刚试过手动调整按钮大小的实验;即在每个按钮中放置一个显示链接回调,并动态计算该给定帧的预期大小,使用与我对方向盘相同的CATransaction显式禁用动画,设置新的变换矩阵(从预期大小生成的缩放变换)。添加到此我已设置按钮内容层shouldRasterize = YES。所以它应该简单地将每帧12个位图缩放到一个本身正在旋转的UIView上。令人惊讶的是,它已经死得很慢,它甚至会让模拟器停下来。它肯定比使用核心动画的动画功能自动慢10倍。

I have just tried as an experiment to resize buttons manually; ie put a display link callback in each button, and dynamically calculate the expected size of this given frame, explicitly disable animations with CATransaction the same as I did with the wheel, set the new transformation matrix (scale transform generated from the expected size). added to this I have set the buttons content layer shouldRasterize = YES. so it should be simply scaling 12 bitmaps each frame onto a single UIView which is itself rotating. amazingly this is dead slow, it is even bringing the simulator to a halt. It is definitely 10 times slower than doing it automatically using core animation's animation feature.

推荐答案

我没有开发iPad应用程序的经验但是我确实有一些优化视频游戏。所以,我无法给出确切的答案,但我想提供一些优化建议。

I have no experience in developing iPad applications but I do have some in optimizing video games. So, I cannot give an exact answer but I want to give some tips in optimization.


不要猜。配置文件。

您似乎在尝试进行更改而不分析代码。更改一些可疑代码并交叉手指并不真正有效。您应该通过检查每个任务采用 long 的方式以及他们需要运行 的方式来描述您的代码。尝试分解您的任务并放置分析代码以测量时间频率。如果您可以测量每个任务使用多少内存,或者有多少其他系统资源,那就更好了。根据证据找出你的瓶颈,而不是你的感觉。

It seems you are trying to make changes without profiling the code. Changing some suspicious code and crossing your fingers does not really work. You should profile your code by examining how long each task takes and how often they need to run. Try to break down your tasks and put profiling code to measure time and frequency. It's even better if you can measure how much memory are used for each task, or how many other system resources. Find your bottleneck based an evidence, not your feeling.

针对您的具体问题,您认为当您的调整大小工作开始时,程序会变得非常慢。但是,您确定吗?它可能是别的东西。在得到实际的分析数据之前,我们不确定。

For your specific problem, you think the program gets really slow when your resizing work kicks in. But, are you sure? It could be something else. We don't know for sure until we have actual profiling data.


在进行更改之前,尽量减少有问题的区域并测量实际性能。

在进行性能分析后,您有一个瓶颈候选者。如果您仍然可以将原因拆分为几个小任务,那么请执行此操作并对其进行分析,直到您无法再将其拆分为止。然后,尝试通过重复运行几千次来测量它们的精确性能。这很重要,因为您需要在进行任何更改之前了解性能(速度和空间),以便将其与未来的更改进行比较。

After profiling, you have a candidate for your bottleneck. If you can still split the cause to several small tasks, do it and go to profile them until you cannot split it anymore. Then, try to measure their precise performance by running them repeatedly like a few thousand times. This is important because you need to know the performance (speed & space) before making any changes so that you can compare it to future changes.

针对您的具体问题,如果调整大小确实是问题,请尝试检查它的执行方式。执行一次调整大小调用需要多长时间?您需要多长时间调整一次工作来完成工作?

For your specific problem, if resizing is really the issue, try to examine how it performs. How long it takes to perform one resize call? How often you need to do resize work to complete your job?


改善它。怎么样?这取决于。

现在,您有了有问题的代码块及其当前性能。你现在必须改进它。怎么样?好吧,这实际上取决于问题所在。你可以搜索更好的算法,你可以做延迟提取如果你可以延迟计算,直到你真的需要执行,或者,如果过于频繁地执行相同操作,则可以通过缓存某些数据来执行过度评估。你越了解原因,就越容易改进它。

Now, you have the problematic code block and its current performance. You now have to improve it. How? well, it really depends on what the problem is. you could search for better algorithms, you could do lazy fetching if you can delay calculations until you really need to perform, or you could do over-eager evaluation by caching some data if you are doing the same operation too frequently. The better you know the cause, the easier you can improve it.

针对您的具体问题,可能只是OS ui功能的限制。通常,调整大小按钮不仅仅是调整按钮本身的大小,而且还会使整个区域或其父窗口小部件无效,以便可以正确呈现每个ui内容。调整大小按钮可能很昂贵,如果是这种情况,您可以通过简单地使用基于图像的方法而不是基于OS ui的系统来解决问题。如果OS API中的图像操作不够,您可以尝试使用OpenGL。但是,再次,我们不知道,直到我们实际尝试它们和 profile 这些替代方案。祝你好运! :)

For your specific problem, it might be just the limit of OS ui function. Usually, resizing button is not just resizing button itself but it also invalidates a whole area or its parent widget so that every ui stuff can be rendered properly. Resizing button could be expensive and if that's the case, you could resolve the issue by simply using image-based approach instead of OS ui-based system. You could try to use OpenGL if image operations from OS API are not enough. But, again, we don't know until we actually try them out and profile these alternatives. Good luck! :)

这篇关于iOS / Core-Animation:性能调优的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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