Haskell FFI / C的性能考虑?

如果使用Haskell作为我的C程序调用的 ,调用它会对性能产生什么影响? 例如,如果我有一个说20kB数据的问题世界数据集,并且我想运行如下所示的内容:

// Go through my 1000 actors and have them make a decision based on
// HaskellCode() function, which is compiled Haskell I'm accessing through
// the FFI.  As an argument, send in the SAME 20kB of data to EACH of these
// function calls, and some actor specific data
// The 20kB constant data defines the environment and the actor specific
// data could be their personality or state
for(i = 0; i < 1000; i++)
   actor[i].decision = HaskellCode(20kB of data here, actor[i].personality);

这里会发生什么 - 我可能会将20kB的数据作为Haskell代码访问的全局不变引用保留下来,还是必须每次都创建一份该数据的副本?

值得关注的是,这些数据可能更大,更大 - 我还希望编写能够处理更大数据集的算法,使用Haskell代码的几次调用所使用的相同模式的不可变数据。

此外,我想并行化这个,就像dispatch_apply()GCD或Parallel.ForEach(..)C#。 我在Haskell之外进行并行化的基本原理是,我知道我总是在许多单独的函数调用上操作,即1000个参与者,所以在Haskell函数内部使用细粒度并行化并不比在C级别进行管理更好。 运行FFI Haskell实例的“线程安全”,我该如何实现这一点 - 每次启动并行运行时,是否需要初始化一个Haskell实例? (如果我必须,似乎很慢)。我如何以良好的表现实现这一目标?


打电话对性能的影响是什么?

假设你只启动一次Haskell运行时(就像这样),在我的机器上,从C调用Haskell,跨越边界传递一个Int,需要约80,000个周期 (我的Core 2为31,000 ns ) - - 通过rdstc寄存器通过实验确定

是否有可能将这20kB的数据保存为Haskell代码访问的全局不可变引用

是的,这当然是可能的。 如果数据真的是不可变的,那么无论你:

  • 通过编组来跨越语言边界来回传输数据;
  • 来回传递数据的参考;
  • 或者将其缓存在Haskell端的IORef
  • 哪种策略最好? 它取决于数据类型。 最惯用的方式是将来自C数据的引用传递给Haskell,将其视为ByteStringVector

    我想将其并行化

    我强烈建议然后反转控制,并从Haskell运行时进行并行化 - 它将更加健壮,因为该路径已经过严格测试。

    关于线程安全性,并行调用运行在同一运行时的foreign exported函数显然是安全的 - 尽管相当确信没有人为了获得并行性而尝试过。 调用获取一个能力,这本质上是一个锁,所以多个调用可能会阻止,从而减少了并行的机会。 在多核的情况下(例如-N4左右),您的结果可能会有所不同(有多种功能可用),但这几乎肯定是提高性能的不好方法。

    同样,通过forkIO来从Haskell进行许多并行函数调用是一个更好的记录,更好的测试路径,比在C端执行工作更少的开销,最终代码可能更少。

    只需调用Haskell函数即可通过多个Haskell线程进行并行处理。 简单!


    我为我的一个应用程序使用了C和Haskell线程的组合,并没有注意到两者之间的性能差异。 所以我制定了一个简单的基准...这比唐的速度要快一点/便宜一些。 这是在2.66GHz i7上测量1000万次迭代:

    $ ./foo
    IO  : 2381952795 nanoseconds total, 238.195279 nanoseconds per, 160000000 value
    Pure: 2188546976 nanoseconds total, 218.854698 nanoseconds per, 160000000 value
    

    在OSX 10.6上用GHC 7.0.3 / x86_64和gcc-4.2.1编译

    ghc -no-hs-main -lstdc++ -O2 -optc-O2 -o foo ForeignExportCost.hs Driver.cpp
    

    哈斯克尔:

    {-# LANGUAGE ForeignFunctionInterface #-}
    
    module ForeignExportCost where
    
    import Foreign.C.Types
    
    foreign export ccall simpleFunction :: CInt -> CInt
    simpleFunction i = i * i
    
    foreign export ccall simpleFunctionIO :: CInt -> IO CInt
    simpleFunctionIO i = return (i * i)
    

    而用于驱动它的OSX C ++应用程序应该很容易适应Windows或Linux:

    #include <stdio.h>
    #include <mach/mach_time.h>
    #include <mach/kern_return.h>
    #include <HsFFI.h>
    #include "ForeignExportCost_stub.h"
    
    static const int s_loop = 10000000;
    
    int main(int argc, char** argv) {
        hs_init(&argc, &argv);
    
        struct mach_timebase_info timebase_info = { };
        kern_return_t err;
        err = mach_timebase_info(&timebase_info);
        if (err != KERN_SUCCESS) {
            fprintf(stderr, "error: %xn", err);
            return err;
        }
    
        // timing a function in IO
        uint64_t start = mach_absolute_time();
        HsInt32 val = 0;
        for (int i = 0; i < s_loop; ++i) {
            val += simpleFunctionIO(4);
        }
    
        // in nanoseconds per http://developer.apple.com/library/mac/#qa/qa1398/_index.html
        uint64_t duration = (mach_absolute_time() - start) * timebase_info.numer / timebase_info.denom;
        double duration_per = static_cast<double>(duration) / s_loop;
        printf("IO  : %lld nanoseconds total, %f nanoseconds per, %d valuen", duration, duration_per, val);
    
        // run the loop again with a pure function
        start = mach_absolute_time();
        val = 0;
        for (int i = 0; i < s_loop; ++i) {
            val += simpleFunction(4);
        }
    
        duration = (mach_absolute_time() - start) * timebase_info.numer / timebase_info.denom;
        duration_per = static_cast<double>(duration) / s_loop;
        printf("Pure: %lld nanoseconds total, %f nanoseconds per, %d valuen", duration, duration_per, val);
    
        hs_exit();
    }
    

    如果你通过指针,Haskell可以窥探那个20k的blob。

    链接地址: http://www.djcxy.com/p/43237.html

    上一篇: Performance considerations of Haskell FFI / C?

    下一篇: Combining Free types