FFI可以以不增加太多开销的方式使用。考虑以下程序(完整代码可在这里找到):
foreign import ccall unsafe "mean" c_mean :: Ptr CInt -> CUInt -> IO CFloat
main :: IO ()
main = do
buf <- mallocBytes (bufSize * sizeOfCInt)
fillBuffer buf 0
m <- c_mean buf (fromIntegral bufSize)
print $ realToFrac m
这个C调用被编译成以下的Cmm代码:
s2ni_ret() { ... }
c2qy:
Hp = Hp + 12;
if (Hp > I32[BaseReg + 92]) goto c2qC;
_c2qD::I32 = I32[Sp + 4];
(_s2m3::F32,) = foreign "ccall"
mean((_c2qD::I32, PtrHint), (100,));
这里是汇编代码:
s2ni_info:
.Lc2qy:
addl $12,%edi
cmpl 92(%ebx),%edi
ja .Lc2qC
movl 4(%ebp),%eax
subl $4,%esp
pushl $100
pushl %eax
ffree %st(0)
ffree %st(4)
call mean
因此,如果您将C导入标记为unsafe
并在测量之前进行所有封送处理,那么您的C调用基本上只是一个内联call
指令 - 就像您在C中执行所有基准测试一样。 当我对一个什么也不做的C函数进行基准测试时,这是Criterion报告的内容:
benchmarking c_nothing
mean: 13.99036 ns, lb 13.65144 ns, ub 14.62640 ns, ci 0.950
std dev: 2.306218 ns, lb 1.406215 ns, ub 3.541156 ns, ci 0.950
found 10 outliers among 100 samples (10.0%)
9 (9.0%) high severe
variance introduced by outliers: 91.513%
variance is severely inflated by outliers
这大约比我的机器预估的时钟分辨率小400倍(约为5.5微秒)。为了比较,这里是一个计算100个整数算术平均值函数的基准数据:
benchmarking c_mean
mean: 184.1270 ns, lb 183.5749 ns, ub 185.0947 ns, ci 0.950
std dev: 3.651747 ns, lb 2.430552 ns, ub 5.885120 ns, ci 0.950
found 6 outliers among 100 samples (6.0%)
5 (5.0%) high severe
variance introduced by outliers: 12.329%
variance is moderately inflated by outliers
exec
开销,并查看它添加了多少噪音。 - Mikhail GlushenkovByteString
,你可以使用unsafeUseAsCString
。或者在测量之前进行编组。 - Mikhail Glushenkov