iOS性能優(yōu)化之CPU、GPU的優(yōu)化(一)

面試的時(shí)候總會遇到以下問題:

1.你在項(xiàng)目中是怎么優(yōu)化內(nèi)存的?
2.優(yōu)化你是從哪幾方面著手?
3.列表卡頓的原因可能有哪些?你平時(shí)是怎么優(yōu)化的?
4.遇到tableview卡頓嗎?會造成卡頓的原因大致有哪些?

一、CPU和GPU

在屏幕成像的過程中,CPU和GPU起著至關(guān)重要的作用。

CPU(中央處理器)
對象的創(chuàng)建和銷毀,對象屬性的調(diào)整、布局計(jì)算、文本的計(jì)算和排版、圖片格式轉(zhuǎn)碼和解碼、圖像的繪制(Core Graphics)
GPU(圖形處理器)
紋理的渲染(OpenGL)

那CPU和GPU是怎么協(xié)作呢?
一個(gè)app的展示會包含很多內(nèi)容,諸如,label,imageview,button等等。這些控件的位置,大小,顏色則都是由CPU來計(jì)算,計(jì)算完成后CPU會將這些數(shù)據(jù)提交給GPU來進(jìn)行渲染,只有經(jīng)過GPU的渲染才能顯示在屏幕上。GPU做的操作則是:將收到的數(shù)據(jù)轉(zhuǎn)成屏幕能顯示的數(shù)據(jù)格式,所以要進(jìn)行渲染的操作。渲染的操作是直接放在幀緩存(緩存區(qū))。然后視頻控制器從緩存區(qū) 讀取的數(shù)據(jù)顯示在屏幕上。就完成了一個(gè)顯示的操作。

顯示流程.png
在iOS中是雙緩存機(jī)制,有前幀緩存、后幀緩存

二、屏幕成像原理

在屏幕顯示過程中是有信號發(fā)送的。一幀一幀的。

發(fā)出垂直同步信號(VSync)時(shí),即將顯示一頁的數(shù)據(jù)。水平同步信號(HSync)發(fā)出時(shí),就一行一行的顯示。
信號.png

三、卡頓的原因

屏幕內(nèi)容是怎么顯示到屏幕上的?
CPU(紅色)——>GPU(藍(lán)色)
1.CPU完成計(jì)算,提交給GPU渲染,這是來個(gè)垂直同步信號,則會將渲染的內(nèi)容顯示到屏幕上。
2.CPU計(jì)算時(shí)間正常,CPU渲染時(shí)間短,等待VSync
3.CPU計(jì)算時(shí)間正?;蚵珿PU渲染時(shí)間長,這時(shí)來了VSync,而這一幀還沒有渲染完,那么就會出現(xiàn)掉幀現(xiàn)象,屏幕回去顯示上一幀的畫面。這樣就產(chǎn)生了卡頓。
4.而當(dāng)下一幀VSync出現(xiàn)時(shí),丟掉的那一幀畫面才會出現(xiàn)。


掉幀.png
卡頓解決的主要思路:
盡可能減少CPU、GPU資源的消耗。
按照60FPS的刷幀率,每隔16ms就會有一次VSync信號。

四、卡頓優(yōu)化-CPU

1.盡量用輕量級的對象,比如用不到事件處理的地方,可以考慮使用CAlayer取代UIView;能用基本數(shù)據(jù)類型,就別用NSNumber類型。
2.不要頻繁地跳用UIVIew的相關(guān)屬性,比如frame、bounds、transform等屬性,盡量減少不必要的修改
3.盡量提前計(jì)算好布局,在有需要時(shí)一次性調(diào)整對應(yīng)的布局,不要多次修改屬性
4.Autolayout會比直接設(shè)置frame消耗更多的CPU資源
5.圖片的size最好剛好跟UIImageView的size保持一致
6.控制一下線程的最大并發(fā)數(shù)量
7.盡量把耗時(shí)的操作放到子線程
8.文本處理(尺寸的計(jì)算,繪制)
9.圖片處理(解碼、繪制)

//圖片解碼的代碼
- (void)image
{
    UIImageView *imageView = [[UIImageView alloc] init];
    imageView.frame = CGRectMake(100, 100, 100, 56);
    [self.view addSubview:imageView];
    self.imageView = imageView;

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 獲取CGImage
        CGImageRef cgImage = [UIImage imageNamed:@"timg"].CGImage;

        // alphaInfo
        CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage) & kCGBitmapAlphaInfoMask;
        BOOL hasAlpha = NO;
        if (alphaInfo == kCGImageAlphaPremultipliedLast ||
            alphaInfo == kCGImageAlphaPremultipliedFirst ||
            alphaInfo == kCGImageAlphaLast ||
            alphaInfo == kCGImageAlphaFirst) {
            hasAlpha = YES;
        }

        // bitmapInfo
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
        bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;

        // size
        size_t width = CGImageGetWidth(cgImage);
        size_t height = CGImageGetHeight(cgImage);

        // context
        CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo);

        // draw
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);

        // get CGImage
        cgImage = CGBitmapContextCreateImage(context);

        // into UIImage
        UIImage *newImage = [UIImage imageWithCGImage:cgImage];

        // release
        CGContextRelease(context);
        CGImageRelease(cgImage);

        // back to the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = newImage;
        });
    });
}

五、卡頓優(yōu)化-GPU

1.盡量減少視圖數(shù)量和層次
2.GPU能處理的最大紋理尺寸是4096x4096,一旦超過這個(gè)尺寸,就會占用CPU資源進(jìn)行處理,所以紋理盡量不要超過這個(gè)尺寸
3.盡量避免段時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成一張圖片顯示
4.減少透明的視圖(alpha<1),不透明的就設(shè)置opaque為yes
5.盡量避免出現(xiàn)離屏渲染

在OpenGL中,GPU有2種渲染方式:

1.On-SCreen Rendering:當(dāng)前屏幕渲染,在當(dāng)前用語顯示的屏幕緩沖區(qū)進(jìn)行渲染操作。
2.Off-Screen Rendring: 離屏渲染,在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。

離屏渲染消耗性能的原因:

1.需要創(chuàng)建新的緩沖區(qū);
2離屏渲染的整個(gè)過程,需要多次切換上下文環(huán)境,先是從當(dāng)前屏幕切換到離屏;等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕

哪些操作會出發(fā)離屏渲染?

1.光柵化,layer.shouldRasterize = YES
2.遮罩,layer.mask
3.圓角,同時(shí)設(shè)置layer.maskToBounds = Yes,Layer.cornerRadis 大于0
考慮通過CoreGraphics繪制裁剪圓角,或者美工提供圓角圖片
4.陰影,layer.shadowXXX
如果設(shè)置了layer.shadowPath就不會產(chǎn)生離屏渲染

六.卡頓檢測

平時(shí)所說的“卡頓”主要是因?yàn)樵谥骶€程執(zhí)行了耗時(shí)的操作。
可以添加Observer到主線程RunLoop中,通過監(jiān)聽RunLoop狀態(tài)切換的耗時(shí),以達(dá)到監(jiān)聽卡頓的目的
推薦一個(gè)庫:LXDAppFluecyMonitor

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容