Haskell程序汇编体积远超等效C代码的原因及编译优化疑问
为何GHC生成的Haskell程序汇编远长于等价C代码?
此前我提问《为何简单“乘2”Haskell程序二进制体积远大于C++?》被标记为重复问题,本次聚焦代码生成的汇编而非二进制,提出新疑问。
等价功能的C程序及汇编
C代码
int main(int argc, char * argv[]) { int r{0}; int c{0}; while (argv[1][c] != '\0') { r *= 10; r += argv[1][c++] - '0'; } return r*2; }
该程序功能为:接收1个数字命令行参数,转为int后返回其2倍作为退出码。
生成的汇编(仅21行)
main: # @main mov rcx, qword ptr [rsi + 8] movzx edx, byte ptr [rcx] test dl, dl je .LBB0_1 inc rcx xor eax, eax .LBB0_3: # =>This Inner Loop Header: Depth=1 movsx edx, dl lea eax, [rax + 4*rax] lea eax, [rdx + 2*rax] add eax, -48 movzx edx, byte ptr [rcx] inc rcx test dl, dl jne .LBB0_3 add eax, eax ret .LBB0_1: xor eax, eax ret
等价功能的Haskell程序及汇编
Haskell代码
import Data.List (foldl') import System.Environment (getArgs) import System.Exit (exitWith, ExitCode (ExitFailure)) main :: IO Int main = do [str] <- getArgs exitWith $ ExitFailure $ (*) 2 $ foldl' step 0 str where step i c = 10 * i + fromEnum c - fromEnum '0'
生成的汇编(共257行,是C的12倍多)
核心疑问
- GHC编译器为何无法识别该main函数的简单逻辑,生成类似C的精简汇编?
- 是遗漏了编译参数,还是这类小代码的优化未被GHC重视?
- 或是受限于Haskell语言特性无法优化?
- 是否调整代码写法能提升优化效果?
有评论指出这是因为包含运行时环境、thunk求值逻辑等,但该程序逻辑简单,为何仍需这些?为何小代码无法得到优化,必须达到代码量临界值?
Clang的优化对比
Clang能将计算置位数量的C代码:
int foo(int a) { int count = 0; while (a) { ++count; a &= a - 1; } return count; }
直接优化为:
foo(int): # @foo(int) popcntl %edi, %eax retq
修改Haskell代码后的汇编变化
将代码中模式匹配改为head取参:
str <- getArgs exitWith $ ExitFailure $ (*) 2 $ foldl' step 0 $ head str
生成的汇编缩减至182行,但仍远长于C代码。我认为遍历惰性链表是Haskell的惯用写法,不应为优化放弃语言特性。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

