[英]Rewriting as a practical optimization technique in GHC: Is it really needed?
我正在閱讀Simon Peyton Jones等人撰寫的論文。 命名為“遵循規則:重寫為GHC中的實用優化技術” 。 在第二部分,即他們寫的“基本思想”:
考慮熟悉的
map
函數,它將函數應用於列表的每個元素。 寫在Haskell中,map
看起來像這樣:
map f [] = []
map f (x:xs) = f x : map f xs
現在假設編譯器遇到以下
map
:
map f (map g xs)
我們知道這個表達式相當於
map (f . g) xs
(其中“。”是函數組合),我們知道后一個表達式比前者更有效,因為沒有中間列表。 但是編譯器沒有這樣的知識。
一個可能的反駁是編譯器應該更聰明---但程序員總是會知道編譯器無法弄清楚的事情。 另一個建議是:允許程序員將這些知識直接傳遞給編譯器。 這是我們在這里探索的方向。
我的問題是,為什么我們不能讓編譯器變得更聰明? 作者說“但程序員總是會知道編譯器無法弄清楚的東西”。 但是,這不是一個有效的答案,因為編譯器確實可以確定map f (map g xs)
等同於map (f . g) xs
,這里是如何:
map f (map g xs)
map g xs
與map f [] = []
統一。
因此map g [] = []
。
map f (map g []) = map f []
。
map f []
與map f [] = []
統一。
因此map f (map g []) = []
。
map g xs
與map f (x:xs) = fx : map f xs
統一。
因此, map g (x:xs) = gx : map g xs
。
map f (map g (x:xs)) = map f (gx : map g xs)
。
map f (gx : map g xs)
與map f (x:xs) = fx : map f xs
統一。
因此, map f (map g (x:xs)) = f (gx) : map f (map g xs)
。
因此我們現在有了規則:
map f (map g []) = []
map f (map g (x:xs)) = f (g x) : map f (map g xs)
正如你所看到f (gx)
只是(f . g)
和map f (map g xs)
被遞歸調用。 這正是map (f . g) xs
的定義。 這種自動轉換的算法似乎很簡單。 那么為什么不實現這個而不是重寫規則呢?
積極的內聯可以得出許多重寫規則是短手的等同。 不同之處在於內聯是“盲目的”,因此您事先並不知道結果是好還是壞,或者即使它會終止。
然而,根據關於程序的更高級別的事實,重寫規則可以完成非顯而易見的事情。 將重寫規則想象為向優化器添加新公理。 通過添加這些,您可以應用更豐富的規則集,從而使復雜的優化更容易應用。
例如, 流融合改變了數據類型表示。 這不能通過內聯來表達,因為它涉及表示類型更改(我們根據Stream
ADT重新構建優化問題)。 容易在重寫規則中陳述,單獨內聯是不可能的。
在我的學生Johannes Bader的學士論文中調查了那個方向的東西: 在功能程序中找到方程式 ( PDF文件 )。
在某種程度上它肯定是可能的,但是
然而,在其他變換(例如內聯和各種形式的融合)之后進行清理是有用的。
這可以被視為在特定情況下平衡期望與在一般情況下平衡它們之間的平衡。 這種平衡會產生一些有趣的情況,你可以知道如何更快地制作某些東西,但如果你不這樣做,那么對於一般的語言來說它會更好。
在您給出的結構中的地圖的特定情況下,計算機可以找到優化。 但是,相關結構呢? 如果函數不是map怎么辦? 如果有一個額外的間接層,例如返回map的函數,該怎么辦? 在這些情況下,編譯器無法輕松優化。 這是一般情況問題。
如果您對特殊情況進行優化,則會出現兩種結果之一
鑒於開發人員需要在一般情況下考慮這種優化,我們希望看到開發人員在簡單的情況下進行這些優化,首先減少對優化的需求!
現在,如果事實證明您感興趣的特定情況占Haskell中世界代碼庫的2%,那么應用特殊情況優化會有更強大的理由。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.