微吼云上線多路互動直播服務(wù) 加速多場景互動直播落地
842
2025-04-02
題目:游戲編程中的高效率緩存替換
在內(nèi)存有限的游戲中,自定義的媒體緩存被用來擴展場景的數(shù)據(jù)量,同時只使用很小的內(nèi)存區(qū)域而不是一次性將所有媒體裝入內(nèi)存。使用緩存系統(tǒng)時,最困難的因素是:當緩存填充達到其上限時,選擇適當?shù)奶鎿Q頁面(victim page)來騰空。當緩存未命中時,頁面替換算法的選擇至關(guān)重要——這種選擇和你游戲的硬件內(nèi)存使用的性能與效率直接相關(guān)。不好的算法往往會破壞游戲的性能,而實現(xiàn)得好的算法在不影響其性能的同時,還能成倍地提高游戲質(zhì)量。流行的緩存替換算法(如最近最少使用算法,LRU)在它們的目標環(huán)境下工作得很好,但在需要更多的數(shù)據(jù)來準確地選擇替換頁面的情形下,它們往往顯得力不從心。本文將介紹年齡和成本指標可以作為評估值,應(yīng)用于構(gòu)建最符合游戲需求的高速緩存替換算法。
1 概述
當從主存請求數(shù)據(jù)時,操作系統(tǒng)將讀出的數(shù)據(jù)放到一個臨時的內(nèi)存區(qū)域中(稱為高速緩存或緩存,cache),它可以以比主存更快的速度進行存取。高速緩存本身有一個預定義的尺寸,它被分割成較小的稱為頁面的內(nèi)存集合。當內(nèi)存存取時,緩存本身會被填滿,此時,操作系統(tǒng)必須從緩存中選擇一個頁面來存放新來的頁面數(shù)據(jù)。這種情況被稱為高速緩存未命中。當需要的頁面數(shù)大大超過緩存的大小時(通常為2倍或更多),緩存抖動(thrash)就發(fā)生了,也就是說,為了給新來的整個數(shù)據(jù)集騰出空間,整個緩存都將被丟棄。緩存抖動被認為是任何緩存算法的最壞情況,也是任何緩存替換性能測試的焦點。
存儲器處理程序要從主存獲取信息時,每次都有相關(guān)的消耗。通常是因為采用了額外的、靠近處理器本身的內(nèi)存芯片,所以從緩存讀取的消耗較小。因而,如果發(fā)生緩存未命中,在決定清除存儲器的哪個頁面時,其中的一個主要目標是:選擇一個在緩存中并不需要有效槽的頁面。例如,如果在故障發(fā)生的過程中隨機選擇一個頁面來刪除,但是,這個頁面被刪除后恰好立即被需要,這將引起另外一個從主存重新獲取數(shù)據(jù)的性能開銷。我們的目標是建立一個算法,它能最好地描述要從緩存刪除的理想頁面,以減少緩存未命中和性能負擔。
這些類型的算法被稱為犧牲(victim)頁面確定或頁面替換算法,它們是20世紀60年代和70年代的一個熱門話題,并在最近最少使用(LRU)算法(以及其他工作集系統(tǒng))的引入后達到高峰。自那時起,為了解決LRU在特定問題領(lǐng)域使用時的一些問題,產(chǎn)生了這些算法的衍生算法。例如,[O'Neil93]描述的一個被稱為LRU-K的LRU分支,被認為能在軟件數(shù)據(jù)庫系統(tǒng)中更有效地工作。自適應(yīng)替換緩存(ARC)是IBM開發(fā)的一個算法,它被使用在硬件控制器以及其他流行的數(shù)據(jù)庫系統(tǒng)中[Megiddo03](見文末的“參考文獻”,以獲取更多信息)。
在游戲開發(fā)中,程序員必須和硬件和軟件層的緩存打交道,尤其是在游戲機領(lǐng)域,程序員不停地努力增加游戲內(nèi)容,同時還得滿足內(nèi)存限制。如同許多其他的為解決一個具體問題而定做的替換算法,有些共同的游戲和圖形系統(tǒng)需要一個更類似于內(nèi)存模式和使用模式的替換系統(tǒng)。本精粹將介紹兩種緩存頁面指標,它們可以以更好地融入視頻游戲開發(fā)環(huán)境的方式交織在一起。
2 緩存替換算法
緩存替換系統(tǒng)自產(chǎn)生起就是一個活躍的研究領(lǐng)域,由此產(chǎn)生了大量的被自定義調(diào)整來解決問題空間下各種情況的不同算法。為了有一個參照基準,這里會涉及一些最常用的算法。 Belady的Min(OPT)
最有效的替換算法將永遠是取代那些被緩存逐出后,將在最長的一段時間不再需要的頁面。在工作系統(tǒng)中,執(zhí)行這一類算法需要預先知道系統(tǒng)的使用情況,而這是不可能確定的。在已知隨時間變化的輸入的測試情形下,執(zhí)行OPT的結(jié)果可用做測試其他算法的基準。
在線程之間有生產(chǎn)者和消費者結(jié)構(gòu)的多線程環(huán)境中,如果生產(chǎn)者線程比消費者線程超前好幾幀,就有可能使用生產(chǎn)者的信息來得到接近于OPT的結(jié)果。 最近最少使用(LRU)
LRU算法取代在最長的時間內(nèi)還沒有使用的頁面。當一個新的頁面加載到緩存中時,按每頁來保存描述了給定頁面有多久沒被使用的數(shù)據(jù)。一旦緩存未命中,被替換的頁面就是那個在最長的時間跨度內(nèi)未曾被使用的頁面。LRU做了一些很酷的事情,但很容易過度抖動。也就是說,在緩存中,你將始終有一個最舊的頁面,這意味著除非你很小心,否則當工作量很大時,實際上可能會清除所有緩存。 最近使用的(MRU)
MRU替換剛剛被替換的頁面,也就是說緩存中的最新頁面。MRU將不會取代整個緩存,在嚴重抖動時,它將永遠選擇去取代同一個頁面。雖然沒有像LRU那么流行和魯棒,但MRU有它的用途。
在[Carmack00],John Carmack列出了一個不錯的、介于LRU和MRU的紋理緩存頁面替換的混合系統(tǒng)。簡而言之,它的使用形式是,大部分時間內(nèi)用LRU,當達到每一幀都必須驅(qū)逐一個條目(entry)時,就切換到MRU替換策略。如前所述,LRU的問題是,如果沒有足夠的可用空間,它將可能抖動整個緩存。在緩存開始抖動的那一點,系統(tǒng)就切換到MRU,并在內(nèi)存中創(chuàng)建一個頁面暫存區(qū)而讓大多數(shù)緩存安然無恙。如果在各幀之間使用的紋理數(shù)量相對較低,這可能是有用的。當用戶處理額外需要的頁面是可用緩存大小兩倍的情形時,此方法就退化了,這可能使渲染過程停止。 不經(jīng)常使用的(NFU)
NFU頁面替換算法改變了訪問啟發(fā)式,為緩存中的每個頁面保存一個訪問計數(shù)器。任何在當前時間間隔內(nèi)被訪問的頁面,它們的計數(shù)器(從零開始)將增加1,其結(jié)果是一個關(guān)聯(lián)頁面有多頻繁地被使用的數(shù)字限定詞。然后,要替換一個頁面,必須在當前時間間隔內(nèi)尋找那個有最小計數(shù)器的頁面。
這個系統(tǒng)最嚴重的問題是:計數(shù)器指標不跟蹤訪問模式。也就是說,一個在加載時被頻繁使用、而以后再也沒被用的頁面,可能和在同一時間間隔內(nèi)每隔一幀就被使用的頁面有相同的數(shù)量。區(qū)分這兩個使用模式所需的信息是無法從一個單一的變量得到的。下一小節(jié)中提出的年齡指標是一個改變了如何表達計數(shù)器的NFU的變形,于是,在抖動時,用戶就可以從它獲取額外的信息。
如果需要一個更綜合的算法清單,在你最愛的搜索引擎中搜索“Page Replacement Algorithms”(頁面替換算法),或者參閱你在大學用過的操作系統(tǒng)教科書。
3 年齡和成本指標
為了說明這一點,本精粹的剩余部分參照了在當代硬件上大多數(shù)游戲面臨的一個問題,那就是設(shè)計一個自定義的紋理緩存。要做到這一點,用戶將保留一個靜態(tài)的一維數(shù)組緩存頁面,可以用來加載和卸載數(shù)據(jù)。假設(shè)用戶正在使用一個多維紋理緩存,即放在緩存的數(shù)據(jù)來自紋理,因為各種大小的多紋理可以容納在一個單一的緩存頁面中,所以從這個意義上講,緩存是多維的。例如,如果緩存頁面可容納一個256×256的紋理,那么,你還可以支持4個64×64的紋理、16個32×32的紋理等,包括可以和諧地存在于同一個頁面的每個尺寸的倍數(shù)。
即使在這個簡單的例子中,你也已經(jīng)奠定了讓標準替換函數(shù)表現(xiàn)不佳的基礎(chǔ)。考慮多維緩存的情況,你需要插入一個新的256×256的頁面到一個緩存,而這個緩存只是用一些32×32的紋理完全填滿。簡單的LRU/MRU方案不具備必要的數(shù)據(jù)來合適地計算哪個緩存頁面是最佳替換頁面。同時,因為訪問模式在很大程度上遠不止依賴于頁面最后被替換的時間,所以它也不能合適地計算哪組32×32的紋理需要被清空。因此,在這種情況下,為了更好地分析最佳替換頁面,本文提出了一套新的替換指標。 年齡算法
OPT算法知道一個頁面在緩存中的使用數(shù)量,并替換那個在最遠的時間才會被使用的頁面。大多數(shù)替換算法盡其所能地用各種數(shù)據(jù)訪問模式來效仿這一算法。為了最好地預測未來的用法,年齡算法通過保留一個在前幾幀中使用情況的概念來模擬這個過程。也就是說,你需要掌握,在一個時間窗口內(nèi)一個頁面被訪問了多少次。為了完成這項任務(wù),在緩存中的每個頁面保留一個32位的整數(shù)變量,當一個頁面首次進入緩存時,此變量被初始化為1(0x00000001)。
對于每一幀,在緩存中的所有活躍頁面左移一位,標志著它隨著時間的折舊。如果一個活躍的頁面在此幀被使用,那么年齡變量的最低有效位設(shè)置為1,否則設(shè)置為0。這個移位(Shift)和設(shè)置模式允許你對過去的32幀保留一個使用評估。
例如,一個每兩幀被使用一次的頁面將有一個年齡變量0xAAAAAAAA(010101010…01),而一個首次加載時被大量使用但以后再也沒有被用的頁面,將有年齡變量0xFFFF0000(1111…1100000…00)。
為表明年齡變量隨著時間的變化,在一個8幀的時間窗口,考慮一個在第一、第三、第四和第八幀被使用的頁面。年齡變量將進行如下改變:
第1幀 — 00000001(使用)
第2幀 — 00000010(不使用)
第3幀 — 00000101(使用)
第4幀 — 00001011(使用)
第5幀 — 00010110(不使用)
第6幀 — 00101100(不使用)
第7幀 — 01011000(不使用)
第8幀 — 10110001(使用)
有了這樣的信息結(jié)構(gòu),你可以計算出指定窗口的年齡百分比費用(APC)。通過用頁面被使用過的幀數(shù)(位為1的個數(shù))除以年齡變量中的總幀數(shù),得到一個頁面被使用及未被使用的平均幀數(shù)。這些數(shù)據(jù)可以用匯編和處理器啟發(fā)式得到,而不需要高級代碼。雖然可以用自己的方式表達這個數(shù)據(jù),但是,提出的APC作為一個單位化的在[0,1]之間的單一值存在,如“年齡和成本”一部分所述,此值可以作為一個相對于其他指標的標量。
當使用年齡來確定目標替換頁面時,你企圖選擇在一定的時間內(nèi)還沒有被使用的頁面,及一般不常使用的頁面。例如,在一個時間窗口,每幀都被使用的一個頁面將有一個100%的APC,它將幾乎是不可能被取代的,而一個有25%的APC的頁面被取代的機會會較高。
我喜歡年齡變量是因為它有一些隱性的好處。
※ 它不利于舊的紋理,迫使紋理去證明它們是被場景所需要的。一旦證明了這一事實,它們就被保留。一旦獲得一個50%以上的APC,它就會很難從緩存中釋放。
※ 它建立了一個暫存區(qū)。還沒有證明它們是有用的新的紋理被轉(zhuǎn)化為暫存區(qū),這是一件好事,因為新的紋理往往是暫時的,在下一幀消失的概率較高。
※ 這是一個修改的NRU(最近沒有使用)方法。在很短的時間內(nèi),有較高可見頻率的紋理,可以很容易地跳到50%的APC,但然后再掉下來,在多幀后,它們的APC緩慢回落。年齡提供了訪問變量的一個修改了的表示法,并允許額外的分析。因此,如果APC>60%,但紋理在最近的X幀沒有被使用過,那么可以檢查此情況并早些清除該紋理。
迄今為止提出的APC變量是強大的,但并非沒有毛病——緩存中的多個頁面可以有完全不同的訪問模式,但具有相同的APC值。也就是說,0xAAAAAAAA和0xFFFF0000有相同的使用比例,但很容易看到,這兩個年齡變量的使用模式明顯不同。隨后的基本年齡變量的二進制數(shù)據(jù)的分析模式可以幫助區(qū)分這些有相似APC值的頁面(如分析子窗口來得到二級APC值),但這也存在類似的問題。 擴展的年齡算法
前面提出的年齡算法假設(shè):你想要保持相當?shù)偷挠脕硗茢囗撁嫘畔⒌膬?nèi)存消耗。因此,在一個32幀的窗口,對每幀存儲一個使用/未使用的標志。應(yīng)當指出的是,在所需頁面數(shù)額相當大的情況下,年齡算法就像任何其他的算法一樣退化。例如,如果有在每一幀都被使用的50個紋理,但只有12個緩存頁面來放置它們,這樣在緩存中就不會有足夠的空間來同時保留整個內(nèi)存占用,每一幀都將抖動整個緩存,且替換每個頁面。
在這種情況下,頁面和紋理的不斷加載或者重新加載,會使每個頁面都有一個設(shè)置成1的年齡計數(shù)器值,因此,這將缺乏任何其他有助于具體替換頁面識別的信息。為了幫助解決這一問題,年齡變量可以每幀存儲,比只是一個使用/未用位更多的信息,且事實上存儲紋理的使用數(shù)量。因此,你可以存儲一系列數(shù)字,每個數(shù)字存儲該頁面在這一幀中被使用的頻度,而不是只在一個32位的整數(shù)變量儲存一個0/1。這將類似于一個[1,18,25,6,0,0,…,1],而不是01001010011…1的清單。在退化的情況下,這一額外的信息特別有用,因為你現(xiàn)在有更多的數(shù)據(jù)來協(xié)助替換頁面的識別。
例如,考慮同時加載到緩存的兩個頁面(TextureA和TextureB),TextureA被使用在場景中50%的物體上,而TextureB只使用在10%的物體上。在這一點,兩個頁面有相同的APC值,但很顯然,你可以確定這兩個頁面有著非常不同的使用數(shù)量。當必須找出一個替換頁面時,應(yīng)該考慮到,在當前幀TextureA被使用了較多次的事實,增加了它將在隨后幀也被使用的可能性,因此,較少使用的紋理(TextureB)應(yīng)該被替代。
通過存儲這一額外的每幀數(shù)據(jù),你提供了其他的統(tǒng)計分析操作,來幫助確定從緩存清除的最佳頁面。
※ 用時間窗口內(nèi)的非零幀的數(shù)量除以總幀數(shù),APC變量仍然可以從擴展的年齡算法得到。
※ 在給定的幀窗口尋找最少使用的頁面,將找出一般而言最少使用的頁面,這將有助于確定替換頁面。
※ 利用MAX分析,你可以確定在窗口內(nèi)訪問最多的頁面,來幫助它們避免從緩存中 清除。
※ 在你的窗口內(nèi),找到AVG的使用和得到類似于APC的第二個簡單化了的變量一樣簡單。
取決于你的實現(xiàn)需要和數(shù)據(jù)格式,簡單的年齡算法或者擴展的年齡算法都是可行的。最好的主意就是坐下來分析你的數(shù)據(jù)來決定,對你的游戲,哪個是最有效的和最有用的方法。 做替換的成本
大多數(shù)替換頁面識別算法只使用一個單一的啟發(fā)式。也就是說,它們的算法是專門針對導致最少的緩存頁面未命中的訪問模式而量身定做的。例如,LRU只保留最舊頁面的信息。然而,自定義軟件緩存常常有涉及緩存未命中的第二個啟發(fā)式——用新數(shù)據(jù)填充緩存頁面的成本。對于大多數(shù)硬件緩存,都有一個恒定的成本。這個成本與存儲器處理程序訪問主存及讀取所需數(shù)據(jù)相關(guān)。
然而,對于你的軟件需要,這個成本通常可以在頁面本身之間波動。因此,替換頁面識別考慮到,實際上用一個給定內(nèi)存塊來填充一個頁面的性能打擊量是很明智的。這個性能消耗(或只是消耗)可以有多種來源,它可以用一個外部數(shù)據(jù)集來手動定義(例如,一個定義哪些紋理被真正使用的XML文件),或者它可以用填充頁面的實際費用來定義。
考慮到在前面的示例中,傳入的紋理頁面是通過從光盤驅(qū)動器連續(xù)傳送而生成的。因為較大的紋理有更多的信息要從媒體中傳送,而較小的或簡單的紋理只是那些消耗的一小部分,所以較大的紋理有較長的、涉及把它們放到緩存的性能時間。在這種情況下,在替換頁面識別過程中,考慮涉及有可能取代內(nèi)存中一個頁面的成本將是非常明智的。如果你替換的頁面具有較高的相關(guān)成本,且接下來的幾幀需要該頁面,那么將導致不必要的開銷。相反,如果替換了一個有較低成本的頁面,錯誤地把它從緩存中刪除的性能打擊則低得多。概括地說,把成本作為頁面替換的一個變量讓你可以回答問題“清除5個較小的紋理給1個大紋理騰出空間會更便宜嗎?”
回顧一下,成本可讓用戶關(guān)注將一個給定頁面重新裝入緩存會如何損害性能。如果需要,這個系統(tǒng)的一個擴展允許替換頁面識別功能更加關(guān)心緩存未命中的性能成本,而不是幀之間的連貫性。
就成本本身而言,它具有其他緩存替換算法的相同問題。當抖動發(fā)生時,你在緩存中找到并清除最便宜的紋理。因為總有一個便宜的頁面存在,如果負荷足夠大,整個緩存可能抖動。該算法也有這樣的問題:它可以把非常昂貴的頁面無限期地留在緩存。如果像天空盒紋理這樣的東西被裝載到緩存,那么這是一個很好的特性,因為天空盒在每一幀都將是活躍的,而且由于它的大尺寸,我們不太可能想把它從緩存中刪除。在大多數(shù)情況下,這是一個糟糕的特征,需要不斷地關(guān)注。
和其他啟發(fā)式相結(jié)合,成本是一個功能強大的同盟。通過用替換指標來偏向訪問模式算法的替換識別,你允許緩存找到一個在頁面替換需求和抖動之間的折中。此外,訪問模式的識別幫助消除純粹的成本指標所涉及的問題,當高費用的項目到達已經(jīng)不再需要的狀態(tài)時,讓它們最終從緩存中清除。 年齡和成本(A&C)
前面的例子假定:在紋理緩存的每個頁面有一個相關(guān)的APC和相對成本(RC),它們在每一幀被更新。本示例假定:RC是一個更重要的指標,并允許該值是一個沒有上限的整數(shù)變量。例如,如果通過從磁盤連續(xù)傳送紋理至緩存,RC值可能是紋理尺寸除以從媒體連續(xù)傳送一小部分紋理所花費的時間。把APC當作一個在(0,1)之間的單位化的浮點變量。
在一個簡單的實現(xiàn)中,用戶可以組合這兩個值成一個單一的結(jié)果,其中,APC作為RC的一個標量,從而使ThrashCost=RC×APC。總的來說,這表現(xiàn)為一個非常好的、用來識別適當?shù)奶鎿Q頁面的啟發(fā)式。為了證實這一點,我已經(jīng)提供了幾個APC/RC比率的例子及替換模式的說明。對以下的數(shù)據(jù),假定RC的最高值可能是10。
正如在此表所見的,使用簡單的RC × APC值可能會導致頁面有不同的APC/RC值,但具有相同的ThrashCost。這將意味著,APC/RC關(guān)系為0.5/100的紋理A,可以和APC/RC為1.0/50的紋理B有相同的成本。這里的問題是你如何確定要取代的頁面。從理論上講,這兩個頁面的任何一個都是一個有效的目標,都包含相同的潛在替代數(shù)值權(quán)重。紋理A有較高的成本,如果下一幀需要它,替換它將更加昂貴。紋理B的成本較低,但有一個100%的APC值,因此立即需要此頁面的可能性將很大。
在實踐中,我發(fā)現(xiàn):當多個頁面返回相同的值時,替換一個有較低APC值的效果要好得多。事實上,這就是要使用年齡指標的原因。通過分析使用模式和成本,你可以看到,雖然頁面更加昂貴,但是它較少地被使用,它錯誤地被緩存抖動的概率就較低。在這些情況下,重新掃描緩存來找到一個有較高ThrashCost及較低APC值的頁面是一個好主意。如果沒有找到,可以安全地假設(shè),為了緩存,這個頁面可能需要被替換掉。然而,取決于你的系統(tǒng),更好的被替換頁面可能會有所不同。
此表中提到了另一個需要討論的實例。如上所述,A&C系統(tǒng)有能力引入一個可能成為靜態(tài)的頁面。如果你的緩存包含從每一個鏡頭角度都可見的紋理,如天空盒紋理或虛擬角色的皮膚貼圖,這也可能是一件好事。但是,如果緩存引入太多這樣的頁面,有效的工作空間就會大規(guī)模地縮小,造成更多的緩存未命中和緩存的整體低效率。在這種情況下,對這些靜態(tài)紋理生成一個單獨的緩存也許是明智的。
4 結(jié)論
對任何高性能環(huán)境,定制媒體緩存系統(tǒng)是至關(guān)重要的。由于外存媒體的使用增加,在游戲環(huán)境中,有一個精確的控制模型的需要也增加了。由于舊的替換算法考慮到操作系統(tǒng)的內(nèi)存管理和硬件內(nèi)存訪問模式而設(shè)計,它們?nèi)狈σ恍╆P(guān)鍵的、允許它們評估可能存在的更復雜情形的屬性。年齡和成本的結(jié)合引入了大量的其他信息,而且只需要非常低的開銷,這很適合游戲環(huán)境并且運作良好。成本指標引入了頁面載入的整體性能的概念,對于隨時需要的外在系統(tǒng),頁面載入可以成為一個主要瓶頸。年齡指標允許一個更基于每幀的使用模式的視角,這比傳統(tǒng)的指標更容易地和游戲模擬的概念相聯(lián)系,它也包含了足夠的、在任何特定環(huán)境的臨界情形下創(chuàng)建有效替換情形的可用信息。由于緩存替換需求在模擬過程中改變,這也允許大量的定制和二次分析,來評估最好的替換頁面而獲得最佳的效果。
利用這一組強大指標的優(yōu)勢是:緩存頁面替換性能的全面增加,從而促成一個較低的抖動開銷和填充緩存所需費用。在每天結(jié)束的時候,這真是你所想要的。
5 致謝
對于在“擴展的年齡算法”一節(jié)的指標,由衷地感謝Blue Shift的John Brooks,以及他讓所有這些得以進行的幫助。
6 參考文獻
[Carmack00] Carmack, J. “Virtualized Video Card Local Memory Is the Right Thing,” Carmack .Plan file, March 07, 2000.
[Megiddo03] Megiddo, Nimrod and Modha, Dharmendra S. “ARC: A Self-Tuning, Low Overhead Replacement Cache,” USENIX File and Storage Technologies (FAST), March 31, 2003, San Francisco, CA.
[O’Neil93] O’Neil, Elizabeth J. and others. “The LRU-K Page-Replacement Algorithm for Database Disk Buffering,” ACM SIGMOD Conf., pp. 297–306, 1993.
本文節(jié)選自《游戲編程精粹 7 》。
內(nèi)容簡介
本書是游戲編程精粹系列的最新一本,內(nèi)容涉及通用編程、數(shù)學和物理、人工智能、音頻、圖形學、網(wǎng)絡(luò)和多人游戲、腳本和數(shù)據(jù)驅(qū)動系統(tǒng)等內(nèi)容,具有較強的先進性和通用性。隨書附帶光盤中提供了本書的源程序、演示程序以及需要的各種游戲開發(fā)的第三方工具。
因此,無論你是一個剛剛起步的游戲開發(fā)新手,還是資深業(yè)界專家,都能夠在本書中找到靈感,增強洞察力及開發(fā)的技能。將書中介紹的開發(fā)經(jīng)驗和技巧應(yīng)用于實際項目中,將縮短開發(fā)時間,提高效率。
本文轉(zhuǎn)載自異步社區(qū)。
軟件開發(fā)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。