[譯]性能概述

本文選譯自《Performance Overview》欣簇。

導(dǎo)語

性能作為所有軟件產(chǎn)品的重要設(shè)計(jì)標(biāo)準(zhǔn)闸盔。如果程序運(yùn)行緩慢或顯示轉(zhuǎn)圈的光標(biāo)家破,用戶很可能對(duì)產(chǎn)品感到失望并尋求替代品韭寸。維持軟件達(dá)到一個(gè)可靠的性能水平需要你的努力,你越早考慮這些問題喷楣,就越容易找到并解決性能問題趟大。

誰該閱讀這份文檔

性能概述是針對(duì)新入軟件性能分析領(lǐng)域開發(fā)人員的基本指導(dǎo)。本文檔提供了管理性能和定位及解決常見性能問題的一個(gè)概覽抡蛙。它還向你介紹了用于定位和解決性能問題的工具和文檔护昧。

本文結(jié)構(gòu)

本文檔包含以下章節(jié):

  • 為了性能而開發(fā)描述了影響性能的關(guān)鍵因素和在軟件中實(shí)現(xiàn)最佳性能的方法。
  • 性能開發(fā)基本建議描述了代碼中需要分析的常見區(qū)域和提供某些基礎(chǔ)性的技術(shù)粗截。
  • 性能開發(fā)工具描述了程序中可用于性能分析的工具。
  • 做初步的性能評(píng)估帶你了解某些重要工具的基礎(chǔ)和如何使用其解決性能問題捣炬。

為了性能而開發(fā)

性能做為軟件設(shè)計(jì)時(shí)需要時(shí)承懿考慮的因素并變得日益緊迫。如果你等到開發(fā)周期行將結(jié)束時(shí)才去做性能優(yōu)化湿酸,這樣對(duì)于完成關(guān)鍵的改進(jìn)未免有點(diǎn)晚了婿屹。性能在設(shè)計(jì)初期就應(yīng)當(dāng)考慮并需要在整個(gè)開發(fā)周期持續(xù)地改進(jìn)。

當(dāng)然推溃,為了完成好的性能設(shè)計(jì)昂利,首先要理解性能到底是什么。這已章節(jié)將提供影像性能的背景知識(shí)铁坎,即OS X和iOS中這些因素的清單蜂奸,以及如何對(duì)這些因素進(jìn)行檢測(cè)。

何為性能硬萍?

“性能”一詞對(duì)于不同的人意味著不同的事物扩所。所以在著手對(duì)你的應(yīng)用進(jìn)行性能改進(jìn)探秘之前,最好是在當(dāng)下理解這一詞條的含義朴乖。

許多人將性能等同于速度祖屏。誠(chéng)然,如果程序在一秒鐘內(nèi)完成復(fù)雜的操作买羞,你可能會(huì)認(rèn)為該程序具有良好的性能袁勺。雖然,就其本身而言速度可能是具有誤導(dǎo)性的測(cè)量畜普。在復(fù)雜的軟件系統(tǒng)中期丰,操作的速度并不是固定值。如果在不同條件下執(zhí)行相同的操作,需要完成該操作的時(shí)間都會(huì)有很大的不同咐汞。這是因?yàn)槌绦騼H僅是在本地系統(tǒng)上眾多處理共享資源的進(jìn)程中的一個(gè)盖呼,這些資源的使用(或?yàn)E用)會(huì)影響所有其他的進(jìn)程。

下面的部分從兩個(gè)不同的方面解釋性能:資源的有效使用和性能感知化撕。這兩個(gè)概念都會(huì)對(duì)你的應(yīng)用程序設(shè)計(jì)和實(shí)現(xiàn)產(chǎn)生重要的影響几晤,只有了解如何結(jié)合使用它們才可以創(chuàng)造更好的整體性能。

資源的有效使用

計(jì)算機(jī)為所有運(yùn)行的進(jìn)程提供了有限數(shù)量的資源植阴。在最低的層級(jí)蟹瘾,這些資源分為以下幾類:

  • CPU時(shí)間
  • 內(nèi)存空間
  • 磁盤空間

所有的數(shù)據(jù)都存儲(chǔ)在內(nèi)存中,或是在某種大容量存儲(chǔ)設(shè)備上掠手,必須由中央處理器進(jìn)行操作憾朴。高效的應(yīng)用程序會(huì)小心地使用所有的這些資源。下面的章節(jié)提供了關(guān)于每一種類型資源的詳細(xì)說明及其對(duì)程序的影響喷鸽。

CPU時(shí)間

CPU時(shí)間由系統(tǒng)分配所以你必須盡可能地利用好這些時(shí)間众雷。由于OS X和iOS都實(shí)現(xiàn)了對(duì)稱多處理,系統(tǒng)中的每個(gè)線程都會(huì)分配的時(shí)間片(最多10毫秒)來運(yùn)行做祝。在那段時(shí)間的末端(或者多數(shù)情況下)系統(tǒng)會(huì)回收CPU的控制并把它分配給其他線程砾省。

在有許多活動(dòng)線程的典型系統(tǒng)中,如果每個(gè)線程都用完了所分配的時(shí)間混槐,性能將非常糟糕编兄。這就導(dǎo)致產(chǎn)生了編寫應(yīng)用程序的最為重要的一個(gè)目標(biāo):

如果你對(duì)程序無事可做,它就不應(yīng)該消耗CPU時(shí)間声登。

完成這一目標(biāo)的最佳方式是使用基于事件的模型狠鸳。使用現(xiàn)代化的基于事件處理的系統(tǒng),例如Cocoa以及Cocoa Touch悯嗓,這意味著程序的線程只在有工作時(shí)才運(yùn)行件舵。

當(dāng)你的應(yīng)用有工作可以完成時(shí),它應(yīng)該盡可能高效地利用時(shí)間绅作。這意味著需要為期望處理的數(shù)據(jù)采用合適的算法芦圾。同樣意味著對(duì)于其他資源的使用,例如完成特定操作的可用的向量單元(OS X中的AltiVec和SSE)或者圖形處理器俄认,這產(chǎn)生了以下目標(biāo):

盡可能地將工作移出CPU个少。

獲取如何高效地使用CPU時(shí)間,請(qǐng)參見基本優(yōu)化建議眯杏。獲取關(guān)于改進(jìn)繪圖操作的建議夜焦,請(qǐng)參見繪制代碼

內(nèi)存空間

現(xiàn)代計(jì)算機(jī)硬件中的內(nèi)存正逐漸由速度較慢(但容量卻更大)的存儲(chǔ)器構(gòu)成岂贩。最快的存儲(chǔ)器是CPU的寄存器茫经。接下來是L1級(jí)緩存,再接著是L2和L3級(jí)緩存。再接下來稍快的存儲(chǔ)器是內(nèi)存卸伞。最慢的存儲(chǔ)器是OS X磁盤上的虛擬內(nèi)存頁面組成抹镊,且必須在完成分頁后才能使用。

在理想的情況下荤傲,每一個(gè)應(yīng)用都足夠的小以能夠裝進(jìn)系統(tǒng)最快的高速緩存中垮耳。不幸的是,大多數(shù)應(yīng)用程序的代碼和數(shù)據(jù)存放在內(nèi)存或者磁盤上遂黍。因此终佛,最大程度的減少應(yīng)用程序的代碼和數(shù)據(jù)在這些較慢媒體上花費(fèi)的時(shí)間顯得尤為重要,這產(chǎn)生了以下目標(biāo):

減少程序的內(nèi)存占用雾家。

減少程序的內(nèi)存占用可以顯著地提高其性能铃彰。小的內(nèi)存占用通常有兩個(gè)優(yōu)點(diǎn)。首先芯咧,你的程序越小牙捉,它所占用的內(nèi)存頁就越少。較少的內(nèi)存頁唬党,通常意味著更少的分頁鹃共。其次,代碼通常較小是作為大量?jī)?yōu)化和更好結(jié)構(gòu)的結(jié)果驶拱。于是,只需要較少的指令來執(zhí)行既定任務(wù)以及任務(wù)的所有代碼都能聚集在同一組內(nèi)存頁中晶衷。

除了降低應(yīng)用程序的內(nèi)存占用蓝纲,還應(yīng)該盡量減少應(yīng)用程序中的可寫內(nèi)存頁的占用∩稳遥可寫內(nèi)存頁存儲(chǔ)應(yīng)用程序的全局或已分配的數(shù)據(jù)税迷。在OS X中,如果需要的話锹漱,這樣的頁面可以被寫入磁盤箭养,但這樣做是比較慢的。在iOS中哥牍,這些頁面的內(nèi)容必須由應(yīng)用程序本身清除毕泌,在以后可能需要應(yīng)用程序重新創(chuàng)建這些在頁面中的數(shù)據(jù)。在這兩種情況下嗅辣,系統(tǒng)會(huì)努力地本應(yīng)該用于執(zhí)行應(yīng)用程序代碼的時(shí)間來釋放內(nèi)存撼泛。

獲取有關(guān)如何減少程序內(nèi)存占用的基本信息,請(qǐng)參見應(yīng)用內(nèi)存占用澡谭。獲取關(guān)于更加高效地使用內(nèi)存的建議愿题,請(qǐng)參見內(nèi)存分配代碼

磁盤空間

文件系統(tǒng)的性能對(duì)于任何計(jì)算機(jī)都是重要的,因?yàn)閹缀跛械臇|西都以文件的方式保存潘酗。你的應(yīng)用程序杆兵、數(shù)據(jù),以及操作系統(tǒng)本身都存在于文件中仔夺,相較于系統(tǒng)的其他部分琐脏,從設(shè)備上加載這些文件到內(nèi)存則顯得不可思議的慢。文件系統(tǒng)囚灼,無論是本地的還是基于網(wǎng)絡(luò)的骆膝,都是性能上的最大瓶頸。這就產(chǎn)生了另一個(gè)目標(biāo):

消除不必要的文件操作并在其他的信息實(shí)際需要時(shí)才進(jìn)行操作灶体。

通過消除和延遲文件的操作來解決這個(gè)性能瓶頸阅签,對(duì)于改善應(yīng)用的整體性能非常重要。數(shù)以百萬計(jì)的處理器周期包括文件的數(shù)據(jù)請(qǐng)求的時(shí)間到程序?qū)嶋H找到數(shù)據(jù)的時(shí)間蝎抽。如果你的程序訪問了大量的文件政钟,它可能在接收所有請(qǐng)求數(shù)據(jù)之前等待數(shù)秒。

另一個(gè)重要的事情是你的應(yīng)用程序和它創(chuàng)建的任何文件可能是在網(wǎng)絡(luò)上樟结,而不是在本地硬盤养交。尤其是OS X中,大量的使用了網(wǎng)絡(luò)瓢宦,所以你不應(yīng)該對(duì)文件的位置進(jìn)行假設(shè)碎连。

獲取關(guān)于如何改進(jìn)程序中文件性能的基本信息,請(qǐng)參見文件訪問代碼驮履。

速度的感知

即時(shí)你為優(yōu)化性能調(diào)整了應(yīng)用程序的代碼鱼辙,這完全有可能讓你應(yīng)用的行為在用戶面前表現(xiàn)得緩慢。這樣的問題是不可避免的:如果你有大量工作要完成玫镐,你就需要CPU時(shí)間和資源來完成相關(guān)工作倒戏。你唯一可以做的就是讓應(yīng)用程序來表現(xiàn)速度,這形成了下面的目標(biāo):

讓你的程序響應(yīng)用戶恐似。

對(duì)于用戶來講響應(yīng)能力通常比絕對(duì)的速度更加重要杜跷。只要程序能夠及時(shí)地響應(yīng)命令,用戶才會(huì)愿意接受任務(wù)需要更長(zhǎng)時(shí)間完成的事實(shí)矫夷。速度的感知是讓用戶在程序后臺(tái)處理數(shù)據(jù)時(shí)能夠繼續(xù)操作葛闷。提高應(yīng)用中并發(fā)任務(wù)執(zhí)行數(shù)量會(huì)是使其響應(yīng)用戶的絕好辦法。并發(fā)通常使用GCD或者線程來實(shí)現(xiàn)口四。當(dāng)你的應(yīng)用主線程響應(yīng)用戶時(shí)孵运,分發(fā)隊(duì)列或者后臺(tái)線程則執(zhí)行計(jì)算或者耗時(shí)的任務(wù)處理。

另一種讓你的應(yīng)用更快的常規(guī)方式是改進(jìn)其啟動(dòng)時(shí)間蔓彩。一個(gè)應(yīng)用程序花費(fèi)一秒或兩秒來啟動(dòng)都顯得浪費(fèi)了治笨。在那個(gè)時(shí)間段驳概,它沒有必要去響應(yīng)用戶及去加載不是立即需要的(或是根本不使用的)資源,這完全就是浪費(fèi)旷赖。

獲取有關(guān)改進(jìn)啟動(dòng)時(shí)間的信息顺又,請(qǐng)參見啟動(dòng)時(shí)初始化代碼。獲取如何改進(jìn)程序感知性能的信息等孵,請(qǐng)參見利用性能感知稚照。

性能跟蹤

保證高性能的唯一方式是在產(chǎn)品的整個(gè)開發(fā)過程中包含性能指標(biāo)并按照指標(biāo)來對(duì)產(chǎn)品進(jìn)行測(cè)量。高性能不是你在開發(fā)周期行將結(jié)束時(shí)移植到代碼中的功能俯萌,它與整個(gè)開發(fā)周期密切相關(guān)果录。在編寫代碼時(shí),清楚代碼對(duì)程序整體性能所產(chǎn)生多大的影響非常重要咐熙。如果你盡早地檢測(cè)到性能問題弱恒,你就很有機(jī)會(huì)在為時(shí)已晚之前修復(fù)它。

確定你已經(jīng)完成或者超過預(yù)定目標(biāo)的方法就是收集數(shù)據(jù)棋恼。蘋果提供了多個(gè)工具來監(jiān)測(cè)和分析程序的性能返弹。你也可以直接在你的代碼中創(chuàng)建測(cè)量工具來幫助自動(dòng)化地收集數(shù)據(jù)。不管你使用何種方法爪飘,你都需要定期體驗(yàn)這些工具并分析其結(jié)果类咧。

建立性能基線

你需要做的第一件事就是決定一組需要測(cè)定的指標(biāo)的性能基線燃辖。選擇對(duì)你的用戶最重要的任務(wù)并定義一系列的執(zhí)行限制來執(zhí)行任務(wù)断箫。例如汇歹,你也許希望應(yīng)用程序在1秒內(nèi)加載和啟動(dòng)它的初始窗口,或者你想要保持總的內(nèi)存使用在一個(gè)既定的目標(biāo)范圍內(nèi)犁罩。

被你選來評(píng)測(cè)的功能應(yīng)該能反應(yīng)用戶的需求穷蛹。你的市場(chǎng)部門應(yīng)該能夠幫助你找到一系列和用戶相關(guān)的任務(wù)。如果你有一個(gè)既定的產(chǎn)品昼汗,請(qǐng)和你的用戶交流并找出他們認(rèn)為慢的以及可以改進(jìn)的功能作為你計(jì)劃更新的部分。

一旦你有了想要追蹤的任務(wù)列表鬼雀,你需要為每一個(gè)任務(wù)設(shè)定性能指標(biāo)顷窒。對(duì)于已經(jīng)存在的產(chǎn)品,你可以簡(jiǎn)單地在之前版本的基礎(chǔ)上改進(jìn)性能源哩。你同樣也可以試著去測(cè)量競(jìng)品的性能并設(shè)定達(dá)到或超過它性能的指標(biāo)鞋吉。如果你有一個(gè)新產(chǎn)品,你可能不得不體驗(yàn)大量的產(chǎn)品來找到合適的值励烦∥阶牛或者,你想要建立激進(jìn)的基線值來試圖盡可能的接近那些產(chǎn)品坛掠。

正如其他性能指標(biāo)一樣赊锚,一致性非常重要治筒。建立基線指標(biāo)的過程應(yīng)該包括你收集這些指標(biāo)的系統(tǒng)信息。記錄系統(tǒng)的硬件和軟件配置舷蒲,并在同一個(gè)配置上運(yùn)行測(cè)試耸袜。嘗試使用盡可能低的硬件配置來建立你的基線。在一臺(tái)快速機(jī)器上的測(cè)量可能會(huì)導(dǎo)致你相信軟件執(zhí)行得很好牲平,但許多用戶會(huì)在較慢處理器和更少內(nèi)存的計(jì)算機(jī)上運(yùn)行軟件堤框。

盡早測(cè)量并經(jīng)常測(cè)量

性能數(shù)據(jù)不是你收集一次就希望在應(yīng)用中找到所有的性能瓶頸。如果你長(zhǎng)時(shí)間維護(hù)著應(yīng)用的性能纵柿,就越容易找到問題蜈抓。維護(hù)歷史使得容易觀察應(yīng)用的性能是提升了還是下降了。如果下降了昂儒,你可以在產(chǎn)品發(fā)布前采取措施來解決問題沟使。

另一個(gè)需要時(shí)常測(cè)量性能的原因是你可以關(guān)聯(lián)代碼與測(cè)試結(jié)果。如果性能在特定的里程碑下降了荆忍,你可以復(fù)查那個(gè)階段的代碼并且試圖從中找到原因格带。同樣的,如果性能提升了刹枉,你可以使用近期的代碼作為好的編程實(shí)踐模型并鼓勵(lì)你的團(tuán)隊(duì)使用相同的技術(shù)叽唱。

當(dāng)你完成部分程序的部分功能時(shí)就應(yīng)該著手做性能的測(cè)量。當(dāng)新功能完成添加時(shí)微宝,你可以為這些新的功能進(jìn)行測(cè)量棺亭。將一套自動(dòng)診斷程序引入你的程序,這使得團(tuán)隊(duì)中的成員能夠更加容易地立即查看到結(jié)果蟋软。有了隨時(shí)可用的信息將使他們更方便地在檢查代碼之前解決性能問題镶摘。

分析結(jié)果

收集數(shù)據(jù)是定位性能瓶頸最為重要的步驟。但是當(dāng)你有了數(shù)據(jù)岳守,如何使用它找到問題同樣重要凄敢。分析性能數(shù)據(jù)不是簡(jiǎn)單地看輸出結(jié)果就能夠立即找到問題的。你也許幸運(yùn)地快速找到了問題湿痢,但是某些微妙的問題需要更加細(xì)心的分析才能發(fā)現(xiàn)涝缝。

幫助分析結(jié)果的一種方法是將其繪制成圖形∑┲兀可視化的性能數(shù)據(jù)可以幫助你查看趨勢(shì)拒逮,這比數(shù)據(jù)在電子表格中或者基于文本的媒介中來得更快。例如臀规,你可以繪制完成一個(gè)單獨(dú)操作的時(shí)間對(duì)于特定的版本滩援,以確定性能在個(gè)版本間是提升還是下降。在相同里程碑下繪制多個(gè)數(shù)據(jù)集也能夠揭示性能趨勢(shì)塔嬉,并提供為什么性能增加或減少的洞察力玩徊。

分析更高級(jí)的算法

當(dāng)你分析性能數(shù)據(jù)時(shí)租悄,請(qǐng)對(duì)問題所在的抽象層次保持一個(gè)開發(fā)的思維。假設(shè)你的數(shù)據(jù)暗示一個(gè)特定的函數(shù)內(nèi)花費(fèi)了很多時(shí)間佣赖。這有可能說明函數(shù)中的代碼需要優(yōu)化以便使其執(zhí)行更快恰矩,但這是造成問題的真正原因嗎?再次運(yùn)行你的程序憎蛤,但這次檢查該函數(shù)被調(diào)用的次數(shù)外傅。如果這個(gè)函數(shù)調(diào)用了一百萬次,問題可能發(fā)生最初調(diào)用它的更高級(jí)別的算法中俩檬。如果函數(shù)被調(diào)用一次萎胰,那么問題的所在就是這函數(shù)本身。

注意:Instruments是一個(gè)分析代碼運(yùn)行時(shí)行為的強(qiáng)大工具棚辽。Instruments可以為你記錄多個(gè)指標(biāo)并依此顯示它們技竟,使得更加容易查看趨勢(shì)。Instruments的數(shù)據(jù)挖掘能力是另一種快速地在更高級(jí)別算法中定位問題的好方法屈藐。獲取更多關(guān)于Instrument及其他蘋果提供的相關(guān)工具的信息榔组,請(qǐng)參見性能工具

在分析數(shù)據(jù)時(shí)联逻,你需要了解和考慮到性能工具本身有一定的局限性搓扯。例如,采樣程序可能會(huì)采集應(yīng)用程序花費(fèi)了大量時(shí)間的地方包归,但你應(yīng)該明白這些工具得出眾多結(jié)論之前如何收集數(shù)據(jù)锨推。采樣工具并不跟蹤每一個(gè)函數(shù)調(diào)用。相反公壤,它們提供了基于在固定的時(shí)間間隔采樣的程序的統(tǒng)計(jì)分析换可。使用這些工具的輸出作為指南,但確保將你記錄的其他數(shù)據(jù)與之關(guān)聯(lián)厦幅。

其他分析技術(shù)

如果你對(duì)某個(gè)性能問題的真正原因有疑問沾鳄,請(qǐng)避免對(duì)問題的原因做出假設(shè)。相反确憨,通過關(guān)注數(shù)據(jù)收集工作的相關(guān)代碼來改進(jìn)你的分析洞渔。嘗試使用不同的工具來收集新的信息。不同的工具可能提供一個(gè)揭示了更多的實(shí)際問題的獨(dú)特視角缚态。

一些額外可以用來分析程序的方法如下:

  • 在調(diào)試器中查看代碼。在調(diào)試器中走查代碼可以揭示導(dǎo)致代碼的速度減慢的邏輯錯(cuò)誤堤瘤。
  • 為代碼添加有代碼執(zhí)行的相關(guān)的日志輸出的斷點(diǎn)玫芦。添加斷點(diǎn)追蹤初始化代碼的例子,請(qǐng)參見啟動(dòng)時(shí)初始化代碼本辐。
  • 嘗試編碼時(shí)使用解決問題的替代方案并看看它們釋放遭遇類似問題桥帆。

性能開發(fā)基本建議

本章為如何優(yōu)化你的程序提供了實(shí)際建議医增。它提供了你應(yīng)該使用性能工具監(jiān)控的區(qū)域的建議,也提供了一系列改善性能的實(shí)際建議老虫。

常見區(qū)域監(jiān)測(cè)

許多性能問題可以在程序中的特定部位被追蹤到叶骨。當(dāng)你設(shè)計(jì)和實(shí)現(xiàn)你的代碼時(shí),你應(yīng)該監(jiān)視那些區(qū)域以確保它們滿足你設(shè)定的性能目標(biāo)祈匙。

程序的關(guān)鍵任務(wù)代碼

當(dāng)你設(shè)計(jì)你的程序時(shí)忽刽,考慮用戶使用最多的任務(wù)或工作流。在實(shí)現(xiàn)階段夺欲,確保監(jiān)視那些任務(wù)的代碼并保證它們的性能不低于預(yù)期水平跪帝。如果遇到了性能問題,你應(yīng)該立即采取措施來修復(fù)這些問題些阅。

程序的關(guān)鍵任務(wù)在各個(gè)程序之間各有不同伞剑。例如,一個(gè)文字處理工具可能需要在文本輸入和顯示上表現(xiàn)得很快市埋,然而一個(gè)文件管理程序卻需要在硬盤上掃描文件及目錄時(shí)很快黎泣。這取決于你決定哪一項(xiàng)任務(wù)是用戶最希望實(shí)現(xiàn)的。

繪制代碼

大多數(shù)程序都完成一定數(shù)量的圖形繪制工作缤谎。如果你對(duì)程序只使用標(biāo)準(zhǔn)的窗口和控制器抒倚,那你也許不需要太過于擔(dān)心圖形繪制的性能。然而弓千,如果你做任何自定義的繪制操作衡便,你需要監(jiān)視你對(duì)圖形繪制代碼并確保其運(yùn)行在可以接受的水平。特別的洋访,如果你支持下面的功能镣陕,你需要探究?jī)?yōu)化圖形繪制代碼的方法。

  • 自動(dòng)縮放姻政。
  • 自定義的視圖繪制代碼呆抑,尤其是部分視圖的更新而不是整個(gè)視圖。
  • 文本的圖形化汁展。
  • 完全的透明視圖鹊碍。
啟動(dòng)時(shí)初始化代碼

啟動(dòng)時(shí)間作為初始化程序數(shù)據(jù)結(jié)構(gòu)以及應(yīng)用準(zhǔn)備接收用戶輸入的時(shí)機(jī)。然而食绿,許多程序在啟動(dòng)時(shí)完成了超過其所需的大量工作侈咕。在大多數(shù)情況下,啟動(dòng)時(shí)執(zhí)行的任務(wù)可以被推遲到應(yīng)用已經(jīng)顯示界面以及開始處理事件的時(shí)候器紧。這樣的延遲會(huì)讓你的用戶感覺應(yīng)用很快耀销,這會(huì)留下一個(gè)好的印象。

對(duì)于運(yùn)行在OS X 10.3.3及其早期版本上的應(yīng)用程序铲汪,另一種改善啟動(dòng)時(shí)間的方式是預(yù)綁定應(yīng)用熊尉。預(yù)綁定涉及到庫(kù)地址范圍的預(yù)計(jì)算并將這些值存儲(chǔ)到應(yīng)用的二進(jìn)制文件中罐柳。這一步驟消除了啟動(dòng)時(shí)動(dòng)態(tài)加載器(dyld)對(duì)于地址范圍的計(jì)算。在OS X 10.3.4及其以后版本中dyld的預(yù)綁定改進(jìn)將不再必須狰住。同樣的张吉,iOS也不需要預(yù)綁定。

文件訪問代碼

文件系統(tǒng)是講信息傳入到內(nèi)存和CPU中的重要部分催植。在訪問一個(gè)文件的時(shí)候肮蛹,會(huì)執(zhí)行數(shù)以千萬計(jì)的指令。因此查邢,檢查你的程序使用文件的方式并確保這些文件確實(shí)是必要的且使用得當(dāng)顯得尤為必要蔗崎。

盡量減少使用文件的數(shù)量是提高文件相關(guān)性能的一種方法。當(dāng)你訪問文件時(shí)扰藕,頭腦中要記住以下幾點(diǎn):

  • 了解系統(tǒng)如何緩存文件數(shù)據(jù)缓苛,并知道如何優(yōu)化這些緩存的使用。避免自己緩存數(shù)據(jù)除非你打算不止一次使用它邓深。
  • 盡可能順序地讀寫數(shù)據(jù)未桥。在一個(gè)文件中跳躍需要花去額外的時(shí)間去尋找新的位置。
  • 盡可能從文件中讀取稍大的數(shù)據(jù)塊芥备,記住一次性讀取過多的數(shù)據(jù)可能會(huì)導(dǎo)致不同的問題冬耿。例如,讀取一個(gè)32 MB字節(jié)文件的全部?jī)?nèi)容可能在操作完成之前觸發(fā)這些內(nèi)容的分頁萌壳。
  • 避免非必要地關(guān)閉和重新打開文件亦镶。如果在允許緩存的情況下,這樣做可能造成即使在數(shù)據(jù)不改變的情況下緩存等待刷新袱瓮。
應(yīng)用內(nèi)存占用

代碼的容量大小可以對(duì)系統(tǒng)的性能產(chǎn)生巨大的影響缤骨。程序使用的內(nèi)存頁面越多,系統(tǒng)及其他程序的可用內(nèi)存將會(huì)更少尺借。內(nèi)存壓力會(huì)實(shí)際地造成分頁以及整個(gè)系統(tǒng)運(yùn)行放緩绊起。

管理代碼的內(nèi)存占用主要關(guān)乎于代碼和數(shù)據(jù)結(jié)構(gòu)的組織。你需要在內(nèi)存中擁有正確的區(qū)段以及不會(huì)造成任何的內(nèi)存分頁和不必要的讀取燎斩。造成內(nèi)存的大量占用有以下原因:

  • 代碼中包含未使用的代碼虱歪。編譯器通常會(huì)按照編譯模塊來組織代碼,這并不總是最好的技術(shù)栅表∷癖桑基于代碼的功能來組織代碼模塊會(huì)是更好的選擇。
  • 靜態(tài)或常量數(shù)據(jù)存儲(chǔ)在可寫內(nèi)存頁怪瓶。在內(nèi)存分頁時(shí)局装,數(shù)據(jù)會(huì)被不必要地寫入磁盤。盡其所能地將這些數(shù)據(jù)移到不可寫的內(nèi)存頁以便能夠快速地銷毀。
  • 程序?qū)С龆嘤趯?shí)際需要的符號(hào)文件铐尚。符號(hào)文件會(huì)占用空間并僅被應(yīng)用中需要調(diào)用的外部模塊代碼使用。移除任何不在外部使用的符號(hào)文件哆姻。
  • 代碼未被編譯器和鏈接器正確優(yōu)化宣增。確保嘗試編譯器的優(yōu)化設(shè)置并觀察哪一個(gè)對(duì)應(yīng)用效果最好。
  • 程序中引入太多的框架矛缨。只加載程序中實(shí)際使用的框架爹脾。
內(nèi)存分配代碼

程序會(huì)分配內(nèi)存來存儲(chǔ)固定的及臨時(shí)的數(shù)據(jù)結(jié)構(gòu)。每次內(nèi)存的分配都與CPU及內(nèi)存的消耗相關(guān)聯(lián)箕昭。了解程序何時(shí)分配內(nèi)存以及內(nèi)存如何使用將幫助你降低上述的性能消耗灵妨。

了解程序中內(nèi)存的用處可以幫助你決定減少內(nèi)存使用的方法。你可以使用可用的性能工具在造成大量?jī)?nèi)存分頁之前來找出自動(dòng)釋放的Objective-C對(duì)象是否被釋放掉落竹。你也可以使用這些工具來找到由代碼缺陷造成的內(nèi)存泄漏泌霍。觀察調(diào)用malloc的次數(shù)也許可以找出你可以重用的內(nèi)存塊而不是創(chuàng)建新的內(nèi)存空間。

一個(gè)內(nèi)存分配的重要法則是保持“懶惰”述召。延遲內(nèi)存的分配直到你真正地使用這部分內(nèi)存時(shí)朱转。獲取更多延遲內(nèi)存分配的信息,請(qǐng)參見保持懶惰积暖。

獲取更多優(yōu)化內(nèi)存分配的信息藤为,請(qǐng)參見《Memory Usage Performance Guidelines》

基本優(yōu)化建議

在你開始實(shí)現(xiàn)新的程序之前夺刑,有幾個(gè)你需要考慮的性能強(qiáng)化的點(diǎn)缅疟。盡管你不會(huì)在所有情況下利用好全部的優(yōu)化,但你至少應(yīng)該在設(shè)計(jì)階段考慮它們遍愿。

保持懶惰

一個(gè)改善應(yīng)用性能的簡(jiǎn)單方式就是保證應(yīng)用不去做任何不必要的工作存淫。應(yīng)用程序的每一時(shí)刻都應(yīng)當(dāng)被用來響應(yīng)用戶的當(dāng)前請(qǐng)求,而不是預(yù)測(cè)將來的請(qǐng)求错览。如果你不是立即需要某個(gè)資源纫雁,比如包含屬性窗口的nib文件,那就不要加載它倾哺。類似的行為會(huì)耗費(fèi)時(shí)間來執(zhí)行轧邪,因?yàn)樗L問了文件系統(tǒng),并且如果用戶并沒有打開這個(gè)屬性窗口羞海,加載nib文件的過程就是在浪費(fèi)時(shí)間忌愚。

需要遵循的基本法則就是等待用戶向你的應(yīng)用程序請(qǐng)求,然后使用必須資源來滿足其請(qǐng)求却邓。只有在可測(cè)量的對(duì)性能有益的情況下才去緩存數(shù)據(jù)硕糊。基于應(yīng)用程序會(huì)變快的假設(shè)去提前做緩存事實(shí)上會(huì)在低內(nèi)存情況下反而降低性能。在這種情況下简十,你緩存的數(shù)據(jù)在其使用前也許會(huì)被交換到磁盤上檬某。如此,之前緩存數(shù)據(jù)所得到的任何好處都變成了性能的損失螟蝙,因?yàn)檫@些數(shù)據(jù)必須在試用期兩次從磁盤上讀取恢恼。如果你真的想要緩存數(shù)據(jù),請(qǐng)?jiān)谕瓿梢淮尾僮髦筮M(jìn)行胰默。

其他一些保持“懶惰”的注意事項(xiàng)如下:

  • 延遲內(nèi)存的分配直到實(shí)際使用該部分內(nèi)存時(shí)场斑。
  • 不要零初始化(zero-initialize)內(nèi)存塊。調(diào)用calloc函數(shù)來完成牵署。
  • 給系統(tǒng)懶加載代碼的機(jī)會(huì)漏隐。配置和組織你的代碼以便系統(tǒng)只加載當(dāng)前操作所需的代碼。
  • 延遲讀取文件的內(nèi)容直到實(shí)際使用這些信息的時(shí)候奴迅。
利用性能感知

在多數(shù)情況下性能的預(yù)估和實(shí)際性能同樣有效青责。許多程序的任務(wù)可以在后臺(tái)執(zhí)行,使用分布隊(duì)列(dispatch queue)或者空轉(zhuǎn)時(shí)間(idle time)半沽。這樣做可以讓應(yīng)用的主線程去自由地處理同用戶的交互并使用戶感覺程序界面更具響應(yīng)性爽柒。當(dāng)你設(shè)計(jì)你的程序時(shí),考慮哪些任務(wù)可以有效地移到后臺(tái)去者填。例如說浩村,如果你的程序需要掃描一些文件或者做長(zhǎng)度的統(tǒng)計(jì),請(qǐng)使用分布隊(duì)列來做占哟。

另一個(gè)提高性能感知能力的方法是讓你的應(yīng)用快速啟動(dòng)心墅。在啟動(dòng)時(shí),延遲任何不立即用于應(yīng)用程序界面展示的任務(wù)榨乎。例如怎燥,延遲創(chuàng)建大量的數(shù)據(jù)直到應(yīng)用程序完成啟動(dòng)后并將其顯示在主窗口。如果你的主窗口顯示的某些數(shù)據(jù)需要在啟動(dòng)時(shí)計(jì)算或者獲取蜜暑,在窗口上顯示一個(gè)進(jìn)度指示器或者其他的狀態(tài)信息來提示數(shù)據(jù)正在被加載铐姚。對(duì)于那些使用插件的應(yīng)用,避免加載插件直到代碼真正需要插件時(shí)肛捍。

使用基于事件的處理

所有現(xiàn)代的Mac應(yīng)用都應(yīng)該使用Cocoa事件系統(tǒng)或者Carbon事件管理器隐绵。(同樣的,iPhone應(yīng)用必須使用由UIKit框架提供的基于觸摸的事件系統(tǒng)拙毫。)你的應(yīng)用程序絕不應(yīng)該向系統(tǒng)輪詢事件依许。這樣做相當(dāng)?shù)氐托АJ聦?shí)上缀蹄,當(dāng)沒有事件處理時(shí)峭跳,輪詢代碼百分之百地是浪費(fèi)CPU時(shí)間”焐簦現(xiàn)代化得基于事件的API都被設(shè)計(jì)成能提供以下優(yōu)點(diǎn):

  • 它們使你的應(yīng)用程序?qū)τ脩舾唔憫?yīng)性。
  • 它們降低了應(yīng)用程序的CPU消耗蛀醉。
  • 它們最小化了應(yīng)用的工作集-在任何時(shí)間加載進(jìn)內(nèi)存的代碼頁數(shù)量悬襟。
  • 它們使得系統(tǒng)持續(xù)地管理電源(省電)。

包括用戶的事件拯刁,你同樣應(yīng)該避免去輪詢古胆。OS X和iOS中的線程會(huì)使用run loop來提供對(duì)定時(shí)器、網(wǎng)絡(luò)事件以及其他收入數(shù)據(jù)的按需響應(yīng)筛璧。許多框架同樣會(huì)使用針對(duì)特殊任務(wù)的異步編程模型,并在任務(wù)完成時(shí)通知指定的回調(diào)函數(shù)或方法惹恃。在OS X 10.6及其以后版本中夭谤,分發(fā)源(dispatch sources)同樣提供了異步接收事件并在分布隊(duì)列上執(zhí)行的功能。

改進(jìn)任務(wù)的并發(fā)能力

在多核計(jì)算機(jī)上巫糙,并發(fā)是另一種同時(shí)改進(jìn)程序的感知性能和實(shí)際性能的方式朗儒。一個(gè)可以同時(shí)執(zhí)行多個(gè)任務(wù)的程序可以并行地在多核計(jì)算機(jī)執(zhí)行這些任務(wù)。甚至當(dāng)計(jì)算機(jī)只有一個(gè)核心時(shí)参淹,可以將你的代碼分解為多個(gè)部分且異步的任務(wù)醉锄,使之快速且正確地執(zhí)行。特別地浙值,你應(yīng)該在分布隊(duì)列上執(zhí)行自定義的任務(wù)并讓你的主線程有時(shí)間主要去處理用戶事件以及更新用戶界面恳不。

在你開始添加并發(fā)支持前,請(qǐng)確保你的程序如何正確地實(shí)現(xiàn)任務(wù)的完成开呐。將你的代碼分解成不同的任務(wù)需要考慮到程序的數(shù)據(jù)結(jié)構(gòu)及代碼路徑烟勋。執(zhí)行共享數(shù)據(jù)的任務(wù)可能需要在串行的分布隊(duì)列上同步地訪問這些數(shù)據(jù)。

獲取更多在應(yīng)用如何實(shí)現(xiàn)并發(fā)的信息筐付,請(qǐng)參見《Concurrency Programming Guide》卵惦。

使用Accelerate Framework

如果你的應(yīng)用程序執(zhí)行大量的數(shù)學(xué)運(yùn)算,你應(yīng)當(dāng)考慮使用Accelerate Framework(Accelerate.framework)來加速這些運(yùn)算瓦戚。Accelerate Framework利用了處理器中的任何可用向量單元比如Intel x86的SSE擴(kuò)展)來執(zhí)行并行的多重運(yùn)算沮尿。通過該框架編碼,而不是直接使用這些向量單元较解,可以避免為每個(gè)平臺(tái)架構(gòu)單獨(dú)單獨(dú)的代碼畜疾。Accelerate Framework在所有OS X支持的架構(gòu)中得到了高度的優(yōu)化。

類似Instruments 的工具可以幫助你找到程序中能受益于Accelerate Framework框架的部分哨坪。關(guān)于更多如何使用這些工具的信息庸疾,請(qǐng)參見性能工具

使應(yīng)用現(xiàn)代化

如果你的程序最初被設(shè)計(jì)運(yùn)行在舊版本的Mac OS上当编,你應(yīng)該更新你的代碼以支持OS X届慈。尤其徒溪,你應(yīng)該避免使用舊的技術(shù),如Carbon或QuickDraw類似的傳統(tǒng)技術(shù)金顿。相反的臊泌,你應(yīng)該使用Cocoa或Cocoa Touch來構(gòu)建應(yīng)用。同時(shí)你需要更新二進(jìn)制形式到Mach-O揍拆。Mach-O格式僅支持基于Intel的Macintosh和基于iOS的設(shè)備渠概。

獲取iOS中可用技術(shù)列表,請(qǐng)參見《iOS Technology Overview》嫂拴。

性能工具

有幾個(gè)用于性能數(shù)據(jù)收集的圖形工具和命令行工具播揪。一些工具和Xcode同時(shí)安裝。其他的可用工具可以從蘋果開發(fā)者網(wǎng)站下載筒狠。

關(guān)鍵應(yīng)用

盡管Xcode自帶多個(gè)收集性能數(shù)據(jù)的工具猪狈,但這里只有少數(shù)你會(huì)經(jīng)常用到。

Instruments

Instruments由一系列提供圖形界面的高性能分析工具組成辩恼,這些工具為你的應(yīng)用在運(yùn)行時(shí)的行為提供前所未有的信息雇庙。并非某一時(shí)刻只顯示你的程序的一個(gè)方面,你可以使用多個(gè)Instruments工具來配置每一次分析會(huì)話灶伊,而每一中工具都收集了特定性能的數(shù)據(jù)疆前。所有Instruments工具的數(shù)據(jù)都會(huì)并排顯示,這使得關(guān)聯(lián)數(shù)據(jù)到某一個(gè)工具和檢測(cè)應(yīng)用行為趨勢(shì)更加容易聘萨。

你可以使用Instruments工具收集如下類型的數(shù)據(jù):

  • 基于Core Data的應(yīng)用程序性能竹椒。
  • 文件系統(tǒng)讀、寫及其他操作匈挖。
  • 垃圾回收代碼的相關(guān)統(tǒng)計(jì)碾牌。
  • 圖形操作和性能相關(guān)的信息。
  • 關(guān)于對(duì)象及內(nèi)存分配的統(tǒng)計(jì)儡循。
  • 內(nèi)存泄漏信息舶吗。
  • 應(yīng)用程序運(yùn)行時(shí)統(tǒng)計(jì)采樣。
  • 關(guān)于進(jìn)程和系統(tǒng)級(jí)活動(dòng)的信息择膝。
  • 關(guān)于Java線程活動(dòng)的信息誓琼。
  • 關(guān)于Cocoa分發(fā)的事件信息。

獲取如何快速使用Instruments的方法肴捉,請(qǐng)參見使用Instruments腹侣。欲了解詳細(xì)信息,請(qǐng)參見《Instruments User Guide》齿穗。

分析工具

Instruments不僅是收集應(yīng)用性能數(shù)據(jù)的分析工具傲隶。一些分析工具還面向?qū)ふ姨囟愋偷男阅軉栴}。表3-1列出了Xcode中以及安裝的和可供下載的分析工具窃页。

表3-1 分析工具

工具 描述
OpenGL Driver Monitor 收集GPU相關(guān)的性能數(shù)據(jù)跺株,包括顯存相關(guān)的數(shù)據(jù)复濒、視頻總線交通以及硬件間的stall(譯者注:stall是停止運(yùn)轉(zhuǎn)的意思,發(fā)生在當(dāng)CPU執(zhí)行時(shí)乒省,所需要的數(shù)據(jù)卻不在寄存器或緩存中巧颈,需要去裝載內(nèi)存的數(shù)據(jù),這期間有一個(gè)等待袖扛,這里叫做stall砸泛。)你可以使用這些信息來找到OpenGL應(yīng)用中造成偶然 地變慢的原因。該工具作為Xcode中圖形工具的一部分蛆封,也可以在蘋果開發(fā)者網(wǎng)站下載唇礁。
OpenGL Profiler 為你的OpenGL應(yīng)用創(chuàng)建運(yùn)行時(shí)文件。你可以查看函數(shù)統(tǒng)計(jì)和應(yīng)用中OpenGL的調(diào)用棧歷史惨篱。該工具作為Xcode中圖形工具的一部分垒迂,也可以在蘋果開發(fā)者網(wǎng)站下載
heap 列出指定進(jìn)程中所有堆內(nèi)存malloc的區(qū)域妒蛇。該工具安裝在“/usr/bin/”目錄下。
leaks 在進(jìn)程的內(nèi)存區(qū)間中搜尋所有已分配卻為被引用的內(nèi)存塊楷拳。該工具安裝在“/usr/bin/”目錄下绣夺。
vmmap 顯示分配給指定進(jìn)程的虛擬內(nèi)存區(qū)域。你可以使用該工具來分析進(jìn)程的內(nèi)存使用欢揖。該工具安裝在“/usr/bin/”目錄下陶耍。

檢測(cè)工具

檢測(cè)工具作為自動(dòng)收集數(shù)據(jù)的被動(dòng)工具。為使用這些工具她混,請(qǐng)?jiān)谶\(yùn)轉(zhuǎn)程序功能的時(shí)候同時(shí)保持這些工具運(yùn)行烈钞。然后,你就可以分析這些工具產(chǎn)生的數(shù)據(jù)以更好地理解你的程序性能特征坤按。某些程序可能會(huì)一直運(yùn)行毯欣。其他大多數(shù)的程序會(huì)在收集完性能數(shù)據(jù)后終止運(yùn)行。表3-2列舉了OS X中的和可供下載的檢測(cè)工具臭脓。

表3-2 檢測(cè)工具

工具 描述
Activity Monitor 顯示當(dāng)前活動(dòng)進(jìn)程常用的內(nèi)存使用和CPU使用相關(guān)的統(tǒng)計(jì)數(shù)據(jù)酗钞。你同樣可以從當(dāng)前應(yīng)用的進(jìn)程進(jìn)行采樣。該工具提供的信息和top工具類似来累。該工具安裝在“/Applications/Utilities/”目錄下砚作。
Quartz Debug 顯示被重繪屏幕的實(shí)時(shí)更新。你可以使用該工具來分析你的繪圖行為嘹锁。該工具作為Xcode中圖形工具的一部分葫录,也可以在蘋果開發(fā)者網(wǎng)站下載
fs_usage 當(dāng)生成頁面故障及調(diào)用文件系統(tǒng)函數(shù)時(shí)领猾,顯示正在進(jìn)行的文件系統(tǒng)活動(dòng)列表米同。你可以使用該工具來理解程序的文件訪問模式骇扇。該工具安裝在“/usr/bin/”目錄下。
sc_usage 顯示正在進(jìn)行的系統(tǒng)調(diào)用和頁面故障統(tǒng)計(jì)數(shù)據(jù)窍霞。該工具安裝在“/usr/bin/”目錄下匠题。
top 顯示當(dāng)前活動(dòng)進(jìn)程常用的內(nèi)存使用和CPU使用相關(guān)的統(tǒng)計(jì)數(shù)據(jù)。該工具會(huì)動(dòng)態(tài)更新信息但金,以便在運(yùn)行時(shí)看到趨勢(shì)韭山。該工具安裝在“/usr/bin/”目錄下。

硬件分析工具

CHUD工具包括一些OS X中用于硬件及低等級(jí)軟件分析的補(bǔ)充工具冷溃。(這些工具不支持iOS钱磅。)表3-3列舉了這些工具中的一部分,也可以在蘋果開發(fā)者網(wǎng)站下載似枕。

表3-3 CHUD工具

名稱 描述
Reggie SE 讓你檢查并修改CPU和PCI的配置盖淡。
acid 分析TT6E指令追蹤和細(xì)節(jié)分析的命令行工具。你可以使用該工具來檢測(cè)隊(duì)列中的錯(cuò)誤指令凿歼,如操作數(shù)未對(duì)齊褪迟,數(shù)據(jù)依賴等待及加載溢出。
simg4 Motorola 7400 處理器上的精確累加模擬器的命令行工具答憔。(譯者注:不解釋了味赃,OS X 10.5及更早版本可用)
simg5 IBM 970 處理器上的精確累加模擬器的命令行工具。(譯者注:不解釋了虐拓,OS X 10.5及更早版本可用)

其他命令行工具

表3-4列舉了OS X中你可以用來檢測(cè)和分析性能的其他命令行工具心俗。(這些工具在iOS中不可用。)這些工具全部位于“/usr/bin/”目錄下且必須以命令行方式運(yùn)行蓉驹。大多數(shù)工具隨Xcode工具安裝城榛。獲取更多有關(guān)這些工具的信息,請(qǐng)參見OS X的man幫助頁态兴。

表3-4 命令行工具

名稱 描述
atos 在正在運(yùn)行的可執(zhí)行文件中狠持,將符號(hào)名稱和該符號(hào)的數(shù)字地址之間的轉(zhuǎn)換。
c2ph 根據(jù)對(duì)象文件中成員的偏移值顯示其C結(jié)構(gòu)瞻润。
gprof 基于對(duì)程序執(zhí)行分析來創(chuàng)建執(zhí)行配置文件工坊。
malloc_history 顯示指定進(jìn)程malloc的行為歷史。
nm 顯示一個(gè)或多個(gè)對(duì)象文件的符號(hào)表信息敢订。
otool 以可讀性更高的方式顯示Mach-O執(zhí)行文件的內(nèi)容王污。
pagestuff 顯示Mach-O執(zhí)行文件的邏輯頁信息。
pstruct 根據(jù)對(duì)象文件中成員的偏移值解析并顯示其C結(jié)構(gòu)楚午。
sample 基于對(duì)程序執(zhí)行分析來創(chuàng)建一個(gè)執(zhí)行配置文件昭齐。
vm_stat 顯示Mach的虛擬內(nèi)存統(tǒng)計(jì)數(shù)據(jù),包括活動(dòng)的矾柜、非活動(dòng)的阱驾、線性的(譯者注:線性指一定時(shí)間內(nèi)不能被交換的存儲(chǔ)區(qū)域)以及空閑的內(nèi)存頁統(tǒng)計(jì)就谜。該工具同樣顯示頁面故障和其他活動(dòng)信息。

初步性能評(píng)估

以下內(nèi)容由于可讀價(jià)值不高里覆,譯者并不會(huì)進(jìn)行翻譯丧荐。對(duì)于下面的一些工具和命令的使用,如top命令喧枷,可以在終端中使用man top命令來查看幫助文檔虹统;Instruments的使用,可以參考《Instruments User Guide》隧甚;Quartz Debug已經(jīng)不在Xcode工具中提供车荔,需要去單獨(dú)下載Graphics Tools才能使用。

使用top

使用Instruments

使用Quartz調(diào)試

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戚扳,一起剝皮案震驚了整個(gè)濱河市忧便,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帽借,老刑警劉巖珠增,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異砍艾,居然都是意外死亡切平,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門辐董,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人禀综,你說我怎么就攤上這事简烘。” “怎么了定枷?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵孤澎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我欠窒,道長(zhǎng)覆旭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任岖妄,我火速辦了婚禮型将,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荐虐。我一直安慰自己七兜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布福扬。 她就那樣靜靜地躺著腕铸,像睡著了一般惜犀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狠裹,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天虽界,我揣著相機(jī)與錄音,去河邊找鬼涛菠。 笑死莉御,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的碗暗。 我是一名探鬼主播颈将,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼言疗!你這毒婦竟也來了晴圾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤噪奄,失蹤者是張志新(化名)和其女友劉穎死姚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勤篮,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡都毒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碰缔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片账劲。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖金抡,靈堂內(nèi)的尸體忽然破棺而出瀑焦,到底是詐尸還是另有隱情,我是刑警寧澤梗肝,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布榛瓮,位于F島的核電站,受9級(jí)特大地震影響巫击,放射性物質(zhì)發(fā)生泄漏禀晓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一坝锰、第九天 我趴在偏房一處隱蔽的房頂上張望粹懒。 院中可真熱鬧,春花似錦顷级、人聲如沸崎淳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拣凹。三九已至森爽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嚣镜,已是汗流浹背爬迟。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菊匿,地道東北人付呕。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跌捆,于是被迫代替她去往敵國(guó)和親徽职。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,307評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理佩厚,服務(wù)發(fā)現(xiàn)姆钉,斷路器,智...
    卡卡羅2017閱讀 134,711評(píng)論 18 139
  • 雛形 剩下的就是加入狀態(tài)啦抄瓦,回調(diào)支持reject啦 注意resolve中的setTimeout潮瓶,是為了解決reso...
    KrisLeeSH閱讀 299評(píng)論 0 0
  • 秒針在不停地奔跑 車海在快速地流動(dòng) 我們匆匆穿行在人海中 有一點(diǎn)光,就夠燦爛 不必?fù)碛袡淮把e的新衣 也不必...
    在這寬大明亮的世界上閱讀 181評(píng)論 0 0