Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 使用GHC最大化Haskell环路性能_Performance_Haskell_Ghc - Fatal编程技术网

Performance 使用GHC最大化Haskell环路性能

Performance 使用GHC最大化Haskell环路性能,performance,haskell,ghc,Performance,Haskell,Ghc,为了将性能与列表速度较慢的情况进行比较 我正在尽可能快地获得以下循环: {-# LANGUAGE BangPatterns #-} module Main (main) where import Control.Monad import Data.Word main :: IO () main = do loop (maxBound :: Word32) $ \i -> do when (i `rem` 100000000 == 0) $ print (fr

为了将性能与列表速度较慢的情况进行比较 我正在尽可能快地获得以下循环:

{-# LANGUAGE BangPatterns #-}

module Main (main) where

import Control.Monad
import Data.Word


main :: IO ()
main = do
  loop (maxBound :: Word32) $ \i -> do
    when (i `rem` 100000000 == 0) $
      print (fromIntegral i / fromIntegral (maxBound :: Word32))


loop :: Word32 -> (Word32 -> IO ()) -> IO ()
loop n f = go 0
  where
    go !i | i == n = return ()
    go !i          = f i >> go (i + 1)
使用
ghc-O loop.hs编译

但是,在我的计算机上运行此程序需要50秒,比同等的C程序慢10倍:

#include "limits.h"
#include "stdint.h"
#include "stdio.h"

int main(int argc, char const *argv[])
{
  for (uint32_t i = 0; i < UINT_MAX; ++i)
  {
    if (i % 100000000 == 0) printf("%f\n", (float) i / (float) UINT_MAX );
  }
  return 0;
}
#包括“limits.h”
#包括“stdint.h”
#包括“stdio.h”
int main(int argc,char const*argv[]
{
对于(uint32_t i=0;i
使用gcc-O2-std=c99-o testc test.c编译


使用新发布的GHC 7.8或使用
-O2
并没有改善性能

<>但是,使用<代码> -FLVLM FLAG(GHC版本)带来了<强> 10X < /强>速度提升,使性能与C.

相当。 问题:

  • 为什么GHC的原生codegen对于我的
    循环
    慢得多
  • 有没有办法改进我的循环,使它在没有
    -fllvm
    的情况下也能快速运行,或者这已经是
    Word32
    上最快的IO循环了

  • 一个简单的优化方法是使用
    Float
    除法,而不是默认的
    Double
    。只需编写一个方便的函数来替换integral中的

    w2f :: Word32 -> Float
    w2f = fromIntegral
    
    但是,像这样计算循环要快得多:

    main :: IO () 
    main = forM_ [0, 100000000 .. mb] $ \i ->
        print (fromIntegral i / fromIntegral mb :: Float))
        where mb = maxBound :: Word32
    

    让我们检查一下总成。我稍微修改了main函数,使输出变得更清晰(但性能保持不变)。我使用了GHC7.8.2和-O2

    main :: IO ()
    main = do
      loop (maxBound :: Word32) $ \i -> do
        when (i `rem` 100000000 == 0) $
          putStrLn "foo"
    
    这里有很多杂乱的东西,所以我尽量只包括有趣的部分:

    天然编码基因 LLVM 观察
    • 两个程序集中都不存在IO开销。零字节
      RealWorld
      状态标记明显缺失

    • 与LLVM相比,原生codegen并没有做太多的强度降低,LLVM很容易将模转换为乘法、移位和幻数

    • 原生codegen在每次迭代时都会重新检查堆栈空间,而LLVM则不会。然而,这似乎不是一个很大的开销

    • 本机codegen在循环和寄存器分配方面非常糟糕。它在寄存器之间来回移动,并在每次迭代中加载绑定。LLVM发出的代码整洁程度堪比手写代码

    关于你的问题:

    有没有一种方法可以改进我的循环,使它在没有-fllvm的情况下也很快,或者这>已经是Word32上最快的IO循环了

    我认为,在这里,你能做的最好的事情就是手动降低强度,尽管我个人认为这种选择是不可接受的。然而,在这样做之后,您的代码仍然会明显变慢。我还运行了以下简单循环,使用LLVM的速度是使用本机的两倍:

    import Data.Word
    main = go 0 where
        go :: Word32 -> IO ()
        go i | i == maxBound = return ()
        go i = go (i + 1)
    

    罪魁祸首再次是不必要的寄存器洗牌和绑定加载。除了切换到LLVM之外,实际上没有任何方法可以解决此类低级问题。

    胡乱猜测:在
    from integral
    之后添加
    ::Float
    ,以确保它们不会默认为其他内容。不过,这与LLVM选项无关。作为记录,我认为这些严格性注释是不必要的,因为
    (+)
    在这两个参数中都已经很严格了。一旦参数被求值,它就会被完全求值。我首先用
    putStrLn“Foo”
    替换了打印,类似地,在C代码中,删除了格式化数字所花费的任何时间。使用
    main=sequence\u$每100000000$复制一次(fromIntegral(maxBound::Word32))$putStrLn“Foo”
    时,速度提高了约30%,其中
    every
    获得第n个元素(由于跳过了第一个元素,所以不完全相同,但这不会对性能产生显著影响)。看起来你的问题的一部分是你试图在Haskell中编写一个C风格的循环,而你在monad中做循环逻辑,这需要额外的构造函数。@bheklir你使用
    replicate
    的想法很有趣,但我认为这并不能解释我观察到的1000%的速度差异。关于你关于构造函数的观点:我不认为IO monad中的代码会给你分配任何额外的构造函数——相反,你建议的列表通常会这样做(特别是在链接bug中)!我相信您看到的30%的加速是由于避免了
    rem
    @leventov,这显然不是格式化/打印IO问题,因为当您根本不打印任何内容时,也会发生这种情况。当然,与GHC bug的链接是“不相关的”,因为它是关于列表的,我在这里不使用它;我只是链接到这个bug来激发我为什么对这样一个循环感兴趣。我很乐意接受一个回答,从技术上解释你提出的任何问题的原因和方式。C代码也会使用浮点运算,但将循环改为只处理可被10^8整除的数字会使它们完全不可比;如果循环以更为Haskell风格的
    loop n f=forM_u[0..n]f
    重写,llvm的性能同样糟糕。感谢您的出色分析。一句话:我很肯定算术检查只是静态业务;IRC上的rwbarton暗示,
    leaq-16(%rbp),%rax
    是堆栈溢出检查(另一件应该从循环中得到优化的事情)。@nh2你说得对!我刚刚看了GHC页面上的STG教程,它似乎确实在检查堆栈空间。我会相应地编辑答案。我是根据你的答案创建的。@Sarah我怀疑
    表单
    速度慢,因为我在问题中提到了。
     Main_zdwa_info:
    /* code omitted: the same stack-checking stuff as in native */
    .LBB1_1:
        movl    $4294967295, %esi /* load the bound */
        movabsq $-6067343680855748867, %rdi /*load a magic number for the modulus */
        jmp .LBB1_2
    .LBB1_4:              
        incl    %ecx
    .LBB1_2:  
        cmpq    %rsi, %rcx
        je  .LBB1_6 /* check bound */
    
        /* do the modulus with two multiplications, a shift and a magic number */
        /* note : gcc does the same reduction */ 
        movq    %rcx, %rax
        mulq    %rdi
        shrq    $26, %rdx
        imulq   $100000000, %rdx, %rax  
        cmpq    %rax, %rcx
        jne .LBB1_4 
        /* Code omitted: print, then return to loop beginning */
    .LBB1_6:                       
        /* Code omitted: return from main */
    
    import Data.Word
    main = go 0 where
        go :: Word32 -> IO ()
        go i | i == maxBound = return ()
        go i = go (i + 1)