从多个 C/C++ 线程调用 Haskell

sek*_*nig 6 c++ multithreading haskell ffi

我在书面 Haskell 中有一个小函数,类型如下:

foreign export ccall sget :: Ptr CInt -> CSize -> Ptr CSize -> IO (Ptr CInt)
Run Code Online (Sandbox Code Playgroud)

我从同时运行的多个 C++ 线程调用它(通过 TBB)。在我的程序执行的这部分期间,即使我在六核 CPU(12 个逻辑核)上运行,我也几乎无法获得高于 1.4 的平均负载。因此,我怀疑对 Haskell 的调用要么全部通过单个线程汇集,要么正在进行一些重要的同步。

我没有明确地做任何这样的事情,所有函数所做的就是对传入的数据进行操作(将其存储到 Data.Vector.Storable 之后),并将结果作为新分配的数组返回(来自 Data.Marshal.Array) .

我需要做些什么才能完全启用这样的并发调用?

我在 Debian Linux(靶心/测试)上使用 GHC 8.6.5,并且我正在使用 -threaded -O2 进行编译。

期待阅读一些建议,

塞巴斯蒂安

K. *_*uhr 2

使用本答案末尾的简单示例,如果我使用以下命令进行编译:

$ ghc -O2 Worker.hs
$ ghc -O2 -threaded Worker.o caller.c -lpthread -no-hs-main -o test
Run Code Online (Sandbox Code Playgroud)

./test然后以 100% 的速度仅占用一个核心来运行它。我需要使用 来运行它./test +RTS -N,然后在我的 4 核桌面上,它以 400% 的速度运行,平均负载约为 4.0。

因此,RTS-N标志会影响可以同时运行导出的 Haskell 函数的并行线程数,并且不需要任何特殊操作(除了使用 进行编译-threaded和运行+RTS -n)即可充分利用所有可用核心。

因此,您的示例中一定存在导致问题的原因。这可能是线程之间对某些共享数据结构的争用。或者,并行垃圾收集可能会导致问题;我观察到,-N在一个简单的测试用例中,随着增加,并行 GC 会导致性能变差(遗憾的是,细节被遗忘),因此您可以尝试关闭并行 GC-qg或限制涉及的核心数量-qn2或其他内容。要启用这些选项,您需要调用而不是像我的示例中hs_init_with_rtsopts()那样调用hs_init()

如果这不起作用,我认为您必须尝试缩小问题范围并发布一个最小的示例来说明性能问题以获得更多帮助。

我的例子:

caller.c
#include "HsFFI.h"
#include "Rts.h"
#include "Worker_stub.h"
#include <pthread.h>

#define NUM_THREAD 4

void*
work(void* arg)
{
        for (;;) {
                fibIO(30);
        }
}

int
main(int argc, char **argv)
{
        hs_init_with_rtsopts(&argc, &argv);

        pthread_t threads[NUM_THREAD];
        for (int i = 0; i < NUM_THREAD; ++i) {
                int rc = pthread_create(&threads[i], NULL, work, NULL);
        }
        for (int i = 0; i < NUM_THREAD; ++i) {
                pthread_join(threads[i], NULL);
        }

        hs_exit();
        return 0;
}
Run Code Online (Sandbox Code Playgroud) Worker.hs
module Worker where

import Foreign

fibIO :: Int -> IO Int
fibIO = return . fib

fib :: Int -> Int
fib n | n > 1 = fib (n-1) + fib (n-2)
      | otherwise = 1

foreign export ccall fibIO :: Int -> IO Int
Run Code Online (Sandbox Code Playgroud)