将 Haskell 线程与内核线程进行比较 - 我的基准测试可行吗?

发布于 2024-11-05 10:02:01 字数 1979 浏览 0 评论 0原文

这实际上是我的大学项目。在我的文章中,我需要提供证据证明 Haskell 线程的创建速度比普通内核线程更快。我知道最好参考一些研究论文,但重点是我必须自己进行基准测试。

这是我的想法。我用 C(使用 pthreads)和 Haskell 编写了两个程序,它们创建了许多线程,但这些线程绝对不执行任何操作。我只需要测量创建线程的速度。

以下是 C 程序的源代码:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void* thread_main(void*);

int main(int argc, char* argv[])
{
   int n,i;
    pthread_t *threads;
    pthread_attr_t pthread_custom_attr;

    if (argc != 2)
    {
        printf ("Usage: %s n\n  where n is no. of threads\n",argv[0]);
       return 1;
   }

    n=atoi(argv[1]);

    threads=(pthread_t *)malloc(n*sizeof(*threads));
    pthread_attr_init(&pthread_custom_attr);

    for (i=0; i<n; i++)
    {
        pthread_create(&threads[i], &pthread_custom_attr, thread_main, (void *)(0));
    }

    for (i=0; i<n; i++)
    {
        pthread_join(threads[i],NULL);
    }
}

void* thread_main(void* p)
{
   return 0;
}

以及 Haskell 程序的源代码:

module Main (main) where

import System.IO.Unsafe
import System
import Control.Concurrent
import Control.Exception

children :: MVar [MVar ()]
children = unsafePerformIO (newMVar [])

waitForChildren :: IO ()
waitForChildren = do
   cs <- takeMVar children
   case cs of
      []   -> return ()
      m:ms -> do
         putMVar children ms
         takeMVar m
         waitForChildren

forkChild :: IO () -> IO ThreadId
forkChild io = do
   mvar <- newEmptyMVar
   childs <- takeMVar children
   putMVar children (mvar:childs)
   forkIO (io `finally` putMVar mvar ())

forkKids :: Int -> IO ()
forkKids 0 = return ()
forkKids n = do
   forkChild (threadMain)
   forkKids (n-1)

threadMain = return ()

main = do
   args <- getArgs
   forkKids (read (head args))
   waitForChildren

现在,我所做的是使用相同的参数(例如 10000)运行每个程序,并使用 time -f%e 测量它们的运行时间>,然后取运行时间的算术平均值。它表明创建 Haskell 线程的速度快了一个数量级。

现在,我的问题是:这是一个正确的基准吗?或者我需要考虑一些因素才能获得准确的结果?

谢谢

This one is actually for my university project. In my essay, I need to inlcude evidence that Haskell threads are faster to create than plain kernel threads. I know that it's better to refer to some research paper, but the point is that I have to do the benchmarking myself.

Here is what I've come up with. I've written two programs, in C (using pthreads) and Haskell, which create many threads, but those threads do absolutely nothing. I need to measure only the speed of creating a thread.

Here's the source code for the C program:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void* thread_main(void*);

int main(int argc, char* argv[])
{
   int n,i;
    pthread_t *threads;
    pthread_attr_t pthread_custom_attr;

    if (argc != 2)
    {
        printf ("Usage: %s n\n  where n is no. of threads\n",argv[0]);
       return 1;
   }

    n=atoi(argv[1]);

    threads=(pthread_t *)malloc(n*sizeof(*threads));
    pthread_attr_init(&pthread_custom_attr);

    for (i=0; i<n; i++)
    {
        pthread_create(&threads[i], &pthread_custom_attr, thread_main, (void *)(0));
    }

    for (i=0; i<n; i++)
    {
        pthread_join(threads[i],NULL);
    }
}

void* thread_main(void* p)
{
   return 0;
}

and for the Haskell program:

module Main (main) where

import System.IO.Unsafe
import System
import Control.Concurrent
import Control.Exception

children :: MVar [MVar ()]
children = unsafePerformIO (newMVar [])

waitForChildren :: IO ()
waitForChildren = do
   cs <- takeMVar children
   case cs of
      []   -> return ()
      m:ms -> do
         putMVar children ms
         takeMVar m
         waitForChildren

forkChild :: IO () -> IO ThreadId
forkChild io = do
   mvar <- newEmptyMVar
   childs <- takeMVar children
   putMVar children (mvar:childs)
   forkIO (io `finally` putMVar mvar ())

forkKids :: Int -> IO ()
forkKids 0 = return ()
forkKids n = do
   forkChild (threadMain)
   forkKids (n-1)

threadMain = return ()

main = do
   args <- getArgs
   forkKids (read (head args))
   waitForChildren

Now, what I do is I run each program with the same argument (e.g. 10000) and measure their running time with time -f%e, then take the arithmetic mean of the running times. It shows that creating Haskell threads in an order of magnitude faster.

Now, my question is: is this a correct benchmark? or is there some factor that I need to take into account to get accurate results?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

∞梦里开花 2024-11-12 10:02:01

您的基准测试可能会得到您想要的结果,但是存在大量噪音。您测量的不是“创建一个线程需要多长时间”,而是“启动并运行一个创建多个线程的程序,然后在终止之前等待它们返回需要多长时间”。

在实践中答案可能或多或少相同,但是在进行基准测试时,您应该尝试缩小范围,以便在尽可能少的外部噪音的情况下对您感兴趣的内容进行基准测试。

为什么不简单地在 pthread_create/forkIO 调用周围设置一个计时器,因为它们是您想要测量的内容?

您对启动程序需要多长时间不感兴趣,所以不要计时。您对之后加入线程需要多长时间不感兴趣,所以不要计时。

Your benchmarks are probably getting you the result you want, but there's an awful lot of noise. What you're measuring is not "how long does it take to create a thread", but "how long does it take to launch and run a program which creates a number of threads, and then waits for them to return before terminating".

The answers are probably more or less the same in practice, but when benchmarking, you should try to narrow it down so you benchmark that which you're interested in, with as little external noise as possible.

Why don't you simply slap a timer around the pthread_create/forkIO calls, since they're what you want to measure?

You're not interested in how long it takes to launch your program, so don't time that. You're not interested in how long it takes to join the threads afterwards, so don't time that.

梦过后 2024-11-12 10:02:01

根据线程的数量,pthread_create() 可能会在某个时刻停止创建线程;基准测试时应注意这一点。

Depending on the number of threads, pthread_create() might stop creating threads at some point; this should be paid attention to at benchmarking.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文