SPL 代碼優化技巧

SPL是一種面向結構化數據計算的程序設計語言,集算器是SPL語言的java實現,採用網格式編程形式提供了編碼和調試的IDE環境,語法比Java和SQL更爲簡單易懂,開發效率更高。本文將從集算器的實現原理出發列舉一些能夠提高計算性能的小技巧。java

1數據類型

1.1 數值

        SPL裏的數值類型有Integer、Long、Double、BigDecimal。其中BigDecimal雖然可以表示任意精度的數據,但計算速度比其它數類型慢不少,佔用的內存也大不少,所以在其它數字類型可以知足精度要求時,使用其它數類型代替BigDecimal可以顯著提高計算效率。數據庫

        實際案例中,在使用JDBC讀取數據庫數據時,有些數據庫的JDBC對於低精度數值也返回BigDecimal,這樣,在作性能優化時就能夠檢查一下是否能夠轉爲其它類型,從而提高性能。編程

1.2 字符串

         Java的字符串對象String佔用空間較大,一個長度爲0的字符串佔用40多個字節,而Integer、Long只佔用16個字節。同時字符串的比較運算、哈希運算也比Integer、Long慢。數組

         另外,數據從硬盤讀入生成java對象,其佔用的內存大小每每是其佔用的硬盤大小的數倍甚至十倍以上(若是硬盤存儲使用了壓縮技術差距會更大)。這種狀況可能直接致使不太大的數據文件在讀成java對象時發生內存溢出,這時若是不能減小內存佔用量就只能使用外存計算了。而一般外存計算的複雜度遠大於內存計算,同時也致使性能會降低不少。性能優化

        那麼,有沒有什麼方法可以減小內存佔用同時又能提升計算效率呢?網絡

        一個經常使用的方法就是枚舉串序號化,好比下面一個事實表的數據:數據結構

image.png

        性別、地區這類枚舉型的字段,能夠創建一個對應表把性別、地區值轉換爲序號一、二、…,這樣事實表中性別字段就能夠只保存對應的序號,地區也是同樣。轉換後數據以下:函數

image.png

image.png

image.png

        這樣一來,咱們就能夠作到減小內存佔用,而且提升計算效率,由於數字的比較、分組等操做比字符串的要快不少。在輸出結果時,能夠根據須要再把序號轉化爲串,也就是使用序號直接按位置到代碼表中找到相應的記錄進行替換。性能

2序表結構

2.1 行追加

         序表相似數據庫中的表,可是倒是有順序的。序表數據在內存中用一個連續的數組保存。通常狀況下,爲序表分配內存時會多預留一些空間來應付可能的增加,以避免每次追加數據時都從新分配內存,不過也不可能預留太多空間而浪費內存。優化

        基於這個原來,爲序表頻繁地追加記錄,會致使這個數組長度不斷地變長,原先爲這個數組分配的空間也要擴大。而擴大內存分配不是一件很簡單的事情,須要分配一塊更大的空間,而後將原空間內的數據複製過來。尋找空間和複製數據都要佔用 CPU 時間,並且經常比運算自己的消耗都大。

        所以,若是事先知道行數,一次性把序表建立出來,那隻須要在一開始分配一次內存就好了。即使序表中的字段值須要一些步驟才能計算出來,那也應該先new出序表後再去修改記錄的字段值,而不要計算一行插入一行。而對於修改記錄字段值的方法SPL提供了不少途徑。

        假設咱們想生成一個20行 2列的斐波那契數列序表,第一列key爲行號,即 1,2,3,…;第二列 value 爲值。斐波那契數列數列的規則是:第 一、第 2 行取值爲 1,從第 3 行起,取值爲前兩行之和。這個運算須要一步步實現,動態追加數據就是很天然的想法了:

image.png

不過,序表一次性產生性能更好,即便計算自己仍然須要一步步實現:

image.png

2.2 列追加

        擴充序表,除了一行行追加數據,還有可能會改變數據的結構,增長每行數據中的字段,也就是所謂的列追加。列追加比行追加要更爲複雜,序表自己是一個大數組,其中的每一行是一條記錄,物理實現上也是一個數組。由於數據結構不多改變,建立序表時不會在生成每行的數組時預留空間,不然內存浪費就太多了(由於每一行都要預留)。基於這種實現原理,若是出現列追加,就會發生前面說的從新分配空間的狀況,並且要針對每一行記錄進行,再將原記錄數據抄過來,能夠想見,這個動做的時間成本有多大,甚至常常會遠遠超過追加那個列後要作的計算。

        SPL爲序表提供了追加列的功能,這會帶來方便性,但在關注性能時卻要慎用。不得不用時,也應該如上所述,一次性把須要追加的列都加上,不要一遍遍地追加。對於當時沒法計算出字段值的列能夠先填成空值,之後再用其它函數去修改字段值。

        最多見的狀況,從數據庫取出的序表後,若是事先知道要再derive出新的一列xxx,那麼能夠在寫SQL時多寫一個null as xxx,這樣在query時就直接把所需的字段都產生了,不用再作一次derive了。

        例如,要從數據表sales中取出字段ORDERDATE,AMOUNT並按ORDERDATE排序,而後追加一列計算AMOUNT的累計值。通常先讀出再追加列的天然寫法:

image.png

而用 SQL 語句先把列生成好的寫法:

image.png

2.3 引用記錄

        針對前面兩種調整序表結構的優化思路,出發點都是減小new、derive函數中抄字段值的動做。除此以外,SPL還支持對象引用,字段取值能夠是另外一條記錄。這樣,在SPL中,大多數狀況不必像SQL那樣在新結果集中把字段抄一遍,爲了保持原有整條記錄一塊兒參與運算,只要用引用方式來寫就能夠了。這樣不只性能更好並且空間佔用也少。

        上面用derive追加AMOUNT累計值的要求能夠用new函數實現,new建立一個新序表,SRC字段引用原紀錄,CUMULATE字段存儲累計值,寫法以下:

image.png

3循環函數

3.1 用循環函數代替循環語句

        SPL的網格程序提供了循環語句for和分支語句if來實現複雜的運算邏輯。運行時,因爲網格的執行次序是動態解釋的,所以大量使用循環,會致使執行的網格過多,在網格的動態解釋上就要花費大量的時間。

        除了循環語句,SPL還提供了循環函數,能夠對付大多數須要使用for語句的場景。對於計算步驟不太複雜,對性能要求高的運算應該儘可能使用循環函數來完成。相似地,能用if 函數的場景也儘可能不要用if語句。

        1.2節中列舉的計算斐波那契數列的例子能夠改寫爲以下:

image.png

        其中#表示當前循環到哪條記錄,第一條記錄對應的#是1,依次遞增。value[-1]表示上一條記錄的value值,value[-2]表示上前數第二條記錄的value值。

        eval函數每次執行都要把參數指定的表達式字符串解析成表達式,而後再執行,若是eval函數在循環裏執行,過多地把表達式字符串解析成表達式會花費大量的時間,若是表達式字符串不是變的則可使用宏替換代替eval。

3.2 常量放在循環外

        把循環裏常量的產生放到循環外,也能夠對性能優化提供幫助。例如選出北京, 上海, 深圳地區的銷售記錄,比較「天然」的寫法是:

image.png

        由於SPL的序列是能夠被修改的,因此表達式["北京","上海","深圳"]每計算一次都會產生一個新序列。若是像上面這樣把["北京","上海","深圳"]放在循環函數select裏,那麼在執行時將會產生A2長度個序列。若是循環次數多,這些沒必要要的運算將消耗大量時間。所以,注重性能的寫法應該以下:

image.png

3.3 警戒循環套循環

        警戒循環函數中再有循環函數,這些代碼看起來很簡單,但幾層循環下來,實際計算量會以幾何級數放大。這雖然是個常識,但有時也會被忽略,所以能在循環外作的事不要放到循環內。特別地,尤爲要警戒在循環內讀文件和訪問數據庫這種超級耗時的動做。

4代碼習慣

4.1 釋放內存

         Java在內存不足時性能會急劇降低。因此要及時釋放內存,SPL沒有刪除變量釋放內存的語句,只需把變量或單元格的值設爲空便可,也能夠用clear語句清除一片格子。例子以下:

image.png

         以=開頭單元格是計算格,表達式的返回值會保存在單元格上,以>開頭的單元格是執行格,表達式的返回值不會保存。cs.select和cs.join是給遊標附加運算,不會產生新的遊標因此返回值能夠不用保存,A7格爲釋放讀出的PART數據,也能夠用clear語句把A1到A5之間的單元格值都清空,只須要把A7格代碼替換以下:

image.png

4.2 代碼緊湊

         for 和if的代碼塊,能夠直接寫到同一行上,沒有必要像Java那樣換一行再寫。SPL的網絡已經可以清晰地拆分出這些語句了。解釋器掃描空白格也須要時間,所以對於含有循環語句的程序,若是循環次數特別多,應該讓代碼緊湊一些,刪除空白的行和列以減小格子數量,從而提升解釋器的效率。

         下面以獲取天天第一條銷售記錄爲例,介紹一下SPL的代碼塊規則,sales是銷售記錄遊標參數,按ORDERDATE有序。

image.png

        單元格的代碼塊爲單元格所在行及其正下和左下單元格都爲空白格的行,上面例子中A2格for的代碼塊爲[B2:F5]。B2格if的代碼塊爲[C2 : F2],if代碼塊的下一行和if所在格同列的單元格B3爲else,而且B3左面的格子都是空白格,則B3格爲B2格的else分支,B3格的代碼塊爲[C3 : F5]。else也能夠和對應的if同行,寫在if右面的單元格上。

相關文章
相關標籤/搜索