1. 程式人生 > >效能優化:空間換時間

效能優化:空間換時間

問題背景

在程式開發過程中,我們對於資料的處理,會有一些校驗。 
       校驗分為兩種:簡單校驗複雜校驗

       對於一些簡單的校驗,如使用者是否存在,密碼是否正確等等。這種校驗,可以說幾乎不耗時的。所以也沒必要在這裡做優化。 
       對於複雜的校驗,需要進行聯合查詢,通過查詢很多次之後,才可以得出 資料的正確性與否。當然這種校驗執行會很慢。

       對於程式開發來說,時間複雜度和空間複雜度是可以相互轉化的。說通俗一點,就是:對於執行的慢的程式,可以通過消耗記憶體(即構造新的資料結構)來進行優化。而消耗記憶體的程式,也可以多消耗時間來降低記憶體的消耗。 
       前者使用的是最多的。很少有人會為了節省記憶體而浪費時間。

       感興趣的同學,請仔細看完這個例子。看如何是如何消耗記憶體來提高效能的。如果有不正確的地方,還請指出來。
先舉例一個場景。來分別 看一下  正常思路的處理方法和優化過後的處理方法:

       比如說給學生 排課。 學生 和 課程 是一個多對多的關係。 


       按照正常的邏輯 應該有一個關聯表來維護 兩者之間的關係。 

現在,新增一個約束條件 用於 校驗。如:張三 上學期 學過 的課程,在排課的時候不應該再排這種課程。 

 所以需要出現一個約束表(即:歷史成績表)。

即:學生選課表,需要 學生成績表作為約束。

處理方式對比

方案一:正常的處理方式:

       當一個學生進行再次選課的時候。需要查詢學生選課表看是否已經存在。

即有如下校驗:

//查詢 學生code和課程code分別為  A 和 B的資料是否存在

//list集合中存放  學生選課記錄全部的資料
List<StudentRecordEntity> ListStudentRecord=service.findAll();   
//查詢資料,看是否已經存在
StudentRecordEntity enSr=ListStudentRecord.find(s=>s.學生Code==A && s.課程Code==B);
If(enSr==null){
    //學生沒有選該課程
    //....
}else{
    //學生已經選過該課程
    //....
}


對於上面這種程式碼的寫法,非常的簡練。而且也非常易懂。 
       首先,假設有5000個學生,100門課程。那麼對於學生選課的資料集中,資料量將是5000*100.資料量會是十萬級別的數量級。

       在十萬條資料中,查詢 學生=A 課程=B的 一條記錄。執行的效率會很低。因為find方法的查詢也就是where 查詢,即通過遍歷資料集合 來查詢。 

所以,使用上面的程式碼。在資料量逐漸增長的過程中,程式的執行效率會大幅度下降。 
       (ps:資料量增長,在該例子中並不太適合。例子可能不太恰當。總之,大概就是這個意思。)


方案二:使用記憶體進行優化效率:

       這種做法,需要消耗記憶體。或者說把校驗的工作向前做(資料的初始化,在部署系統的過程中進行)。即:在頁面載入的時候資料只調用提供的public方法進行校驗。
//學生Code  到   陣列索引
Private Dictionary<string,int> _DicStudentCodeToArrayIndex;
//課程Code  到   資料索引
Private Dictionary<string,int> _DicCourseCodeToArrayIndex;

//所有學生
List<StudentEntity> ListStudent=service.findAllStudent();
//所有課程
List<CourseEntity> ListCourse=service.findAllCourse();
//所有 學生選課記錄
List<StudentCourseEntity> ListStudentRecord=service.finAll();

Private int[,] _ConnStudentRecord=new int[ListStudent.count,ListCourse.count];

//構造 學生、課程的  陣列 用於快速查詢字典索引
Private void GenerateDic(){
    For(int i=0;i<ListStudent.Count;i++)
        _DicStudentCodeToArrayIndex.Add(ListStudent[i].code,i)
    }
    For(int i=0;i<ListCourse.Count;i++){
        _DicCourseCodeToArrayIndex.Add(ListCourse[i].code,i)
    }
}

//構造學生選課 匹配的 二維陣列。 1表示 學生已選該課程
Private void GenerateArray(){

    Foreach(StudentRecordEntity sre in ListStudentRecord){
        Int x=_DicStudentCodeToArrayIndex[sre.學生Code];
        Int y=DicCourseCodeToArrayIndex[sre.課程Code];
        ConnStudentRecord[x,y]=1;
    }
}

//對外公開的方法:根據學生Code 和課程Code  查詢 選課記錄是否存在
/// <returns>返回1 表示存在。返回0表示不存在</returns>
Public void VerifyRecordByStudentCodeAndCourseCode(String pStudentCode,String pCourseCode){
    Int x=_DicStudentCodeToArrayIndex[pStudentCode];
    Int y=_DicCourseCodeToArrayIndex[pCourseCode];

    Return ConnStudentRecord[x,y];
}

效能分析

分析一下第二種方案的表象。 
       1、方法很多。 
       2、使用的變數很多。

       首先要說一下。該優化的目的,是提高 學生在選課的時候,所出現的卡頓現象(校驗資料量大)。

分別對以上兩種方案進行分析:

       假設學生為N,課程為M

第一種方案: 
       時間複雜度很容易計算 第一種方案最小為O(NM) 
第二種方案: 
       1、程式碼多。但是給使用者提供的只有一個VerifyRecordByStudentCodeAndCourseCode方法。 
       2、變數多,因為該方案就是要使用記憶體提高效率的。 
       這個方法執行流程:1、在Dictionary中使用Code找Index 2、使用Index查詢陣列。

       第一步中,Dictionary中查詢是使用的Hash查詢演算法。時間複雜度為O(lgN) 時間比較快。第二步,時間複雜度為O(1),因為陣列 是連續的 使用索引 會直接查詢對應的地址。 
       所以,使用第二種方案進行校驗,第二種方案時間複雜度為O(lgN+lgM) 

小結

       通過上面的分析,可以看出,記憶體的付出是可以提高程式的執行效率的。以上只是一個例子,優化的好壞取決於使用的資料結構。


相關推薦

效能優化空間時間

問題背景 在程式開發過程中,我們對於資料的處理,會有一些校驗。         校驗分為兩種:簡單校驗和複雜校驗。        對於一些簡單的校驗,如使用者是否存在,密碼是否正確等等。這種校驗,可以

空間時間,超大資料表的查詢效率優化

原文出處:http://www.cnblogs.com/wesley/archive/2012/04/23/2466982.html 在開發論壇程式的時候,我借鑑了目前一些論壇的資料規模,10年的積累大概在2000萬~5000萬左右,因此決定,最低承載力設計要求至少是 9 位數。於是在開發完第一

連載編寫高效程式碼(8) 空間時間——我們總是在走,卻忘了停留

         時間和空間的關係,是霍金這種智商的人要研究的東西,我們只需要知道,在程式設計時,空間是可以換時間的,時間也是可以換空間的。          李開復在他的自傳《世界因你不同》中描述了他小時候在美國學校裡的一個故事,老師出了道題:“誰知道1/7等於多少?”小

ListView空間時間優化

public View getView(int position, View convertView, ViewGroup parent) {   View view = null;   if(convertView==null){    Log.i(TAG,"第"+pos

leetcode-383-Ransom Note(以空間時間

ini from each 小寫字母 自己的 hat bmi 第一個字符 BE 題目描述: Given an arbitrary ransom note string and another string containing letters from all the m

效能優化」首屏時間從12.67s到1.06s,我是如何做到的?

——本文是對之前同名文章的修正,將所有webpack3的內容更新為webpack4,以及加入了筆者近期在公司工作中學習到的自動化思想,對文章內容作了進一步提升。 0.引言 作為網際網路專案,最重要的便是使用者體驗。在舉國“網際網路+”的熱潮中,使用者至上也已經被大多數企業所接收

Python效能優化PyPy、Numba 與 Cython。PyPy的安裝及對應pip的安裝

  效能優化討論見參考1:大概意思是,PyPy內建JIT,對純Python專案相容性極好,幾乎可以直接執行並直接獲得效能提升;缺點是對很多C語言庫支援性不好。Numba是一個庫,可以在執行時將Python程式碼編譯為本地機器指令,而不會強制大幅度的改變普通的Python程式碼。Cython是一種Python

【Android】效能優化電量消耗統計

電量的消耗和使用對於移動裝置非常重要,一項調查問卷顯示,電池的容量和壽命是手機最重要的營銷點:所謂“the one thing that you can't do without”。 硬體 從硬體的角度看,Android電量的消耗主要來自螢幕,CPU,網路裝置和各樣的感測器:指紋,亮度

Wordpress效能優化使用crontab+wp-cli代替wp-cron

wp-cron的問題     Wordpress內建wp-cron的模組,可以用來執行定時任務,比如定時檢查更新,定時釋出文章等都需要用到,屬於必備功能。 但是該模組的特點是:它只能在使用者發起請求時檢查定時任務。這個特點導致了一個問題:沒有使用者訪問時,那定時

轉 Spark效能優化資源調優篇

前言 在開發完Spark作業之後,就該為作業配置合適的資源了。Spark的資源引數,基本都可以在spark-submit命令中作為引數設定。很多Spark初學者,通常不知道該設定哪些必要的引數,以及如何設定這些引數,最後就只能胡亂設定,甚至壓根兒不設定。資源引數設定的不合理,可能會導致沒

ExtJs效能優化tab的資料延遲載入

今天碰到一個問題,當點選某一行資料,顯示詳情時,由於詳情又有四個子tab,每個子tab都是一個表格,有各種各樣的請求,當點選該行資料顯示詳情時,所有的資料同時載入,導致頁面卡頓,此時做ExtjS的效能優化是很重要的。通過研究,瞭解了一下ExtJs的效能優化和前端的效

Spark效能優化開發調優篇

      在大資料計算領域,Spark已經成為了越來越流行、越來越受歡迎的計算平臺之一。Spark的功能涵蓋了大資料領域的離線批處理、SQL類處理、流式/實時計算、機器學習、圖計算等各種不同型別的計算操作,應用範圍與前景非常廣泛。   然而,通過Spark開發出高效能的大

Spark效能優化資源調優篇

在開發完Spark作業之後,就該為作業配置合適的資源了。Spark的資源引數,基本都可以在spark-submit命令中作為引數設定。很多Spark初學者,通常不知道該設定哪些必要的引數,以及如何設定這些引數,最後就只能胡亂設定,甚至壓根兒不設定。資源引數設定的不合理,可能會

效能優化之App啟動時間

App啟動模式分類 1.冷啟動 冷啟動狀態:系統不存在該應用的程序。啟動應用才能創建出應用的程序。 一般是中應用在開機後或者系統停止後的第一次啟動過程。因為系統和應用在冷啟動時需要做跟多的工作 所以減少

網站效能優化雅虎35條軍規及其可測的23條規則

注:加粗項為可測的23條規則 1.Minimize HTTP Requests 減少HTTP請求 圖片、css、script、flash等等這些都會增加http請求數,減少這些元素的數量就能減少響應時間。把多個JS、CSS在可能的情況下寫進一個檔案,頁面裡直接寫入

Spark效能優化優化資料結構

如何優化資料結構? 1、優先使用陣列以及字串,而不是集合類。也就是說,優先用array,而不是ArrayList、LinkedList、HashMap等集合。 比如,有個List list = new

Spark效能優化高效能序列化類庫

一、資料序列化概述: 在任何分散式系統中,序列化都是扮演著一個重要的角色的。如果使用的序列化技術,在執行序列化操作的時候很慢,或者是序列化後的資料還是很大,那麼會讓分散式應用程式的效能下降很多。所以,進行Spark效能優化的第一步,就是進行序列化的效能優化。 S

Spark效能優化資料傾斜調優

資料傾斜調優 調優概述   有的時候,我們可能會遇到大資料計算中一個最棘手的問題——資料傾斜,此時Spark作業的效能會比期望差很多。資料傾斜調優,就是使用各種技術方案解決不同型別的資料傾斜問題,以保證Spark作業的效能。 資料傾斜發生時的現象   1、絕大多數ta

JAVA效能優化細節決定成敗

程式碼優化的目標是:減小程式碼的體積;提高程式碼執行的效率 程式碼優化細節 1、儘量指定類、方法的final修飾符 帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String,整個類都是final的。為

Web前端效能優化減少DNS查詢

DNS(Domain Name System): 負責將域名URL轉化為伺服器主機IP。 DNS查詢流程:首先檢視瀏覽器快取是否存在,不存在則訪問本機DNS快取,再不存在則訪問本地DNS伺服器。所以DNS也是開銷,通常瀏覽器查詢一個給定URL的IP地址要花費20-120ms,在DNS查詢完成前