【问题标题】:Calling into Haskell from multiple C/C++ threads从多个 C/C++ 线程调用 Haskell
【发布时间】:2020-03-16 19:00:42
【问题描述】:

我有一个用 Haskell 编写的小函数,类型如下:

foreign export ccall sget :: Ptr CInt -> CSize -> Ptr CSize -> IO (Ptr CInt)

我从同时运行的多个 C++ 线程调用它(通过 待定)。在执行程序的这一部分期间,我几乎无法获得 即使我在六核 CPU(12 逻辑核心)。因此,我怀疑要么调用 Haskell 通过单个线程汇集,或者有一些重要的 同步进行中。

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

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

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

期待阅读一些建议,

塞巴斯蒂安

【问题讨论】:

  • 更新: 我发现添加运行时选项 +RTS -N2 -RTS 会有所改进。在Documentation 中,我认为这只会影响 imported forgein 函数,但在这种情况下可能有帮助的是它启用了并行垃圾收集,如here 所述。然而,它仍然不是很好,而且我发现使用大于 2 的数字的性能比-N2 更差。
  • Haskell 程序是否与-threaded 关联?这就是 here 指定的内容。
  • 是的,对于编译和链接,我都指定了-threaded 选项。

标签: c++ multithreading haskell ffi


【解决方案1】:

使用这个答案末尾的简单示例,如果我编译:

$ ghc -O2 Worker.hs
$ ghc -O2 -threaded Worker.o caller.c -lpthread -no-hs-main -o test

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

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

因此,您的示例中一定有某些东西导致了问题。它可能是线程之间在某些共享数据结构上的争用。或者,并行垃圾收集可能会导致问题;我观察到并行 GC 在一个简单的测试用例中随着-N 的增加而导致性能下降(遗憾的是,细节忘记了),因此您可以尝试使用-qg 关闭并行GC 或限制-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;
}
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

【讨论】:

  • 我刚刚尝试了-qg,但我只收到此消息作为回报:大多数 RTS 选项已禁用。使用 hs_init_with_rtsopts() 来启用它们。 但是,该函数未在我的 HsFFI.h 中声明,当我添加手动 extern void hs_init_with_rtsopts(int *argc, char **argv[]); 时,我收到链接器错误。有什么建议我需要做些什么来完成这项工作?
  • 您应该能够#include "Rts.h" 获得定义,并且我已经更新了对我有用的示例。但是,您所做的也应该有效,我不明白您为什么会出现链接器错误。
  • 另一种使用 hs_init_ghc 的方法记录在 GHC 文档的 this section 的第二个代码示例中。
  • 好的,#include "Rts.h" 解决了。可能我的手动声明给出了链接器错误,因为我应该写extern "C" void ...。在性能方面,-N -qg 的组合是一个明显的胜利——谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-05-13
  • 2019-07-11
  • 1970-01-01
  • 2019-07-23
  • 2013-07-22
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
相关资源
最近更新 更多