这个实际上是我的大学项目。在我的文章中,我需要提供证据证明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线程的速度要快一些。
现在,我的问题是:这是一个正确的基准吗?或者是否有一些因素需要我考虑以获得准确的结果?
由于
答案 0 :(得分:4)
你的基准测试可能会让你得到你想要的结果,但却有很多噪音。你测量的不是“创建一个线程需要多长时间”,而是“启动和运行一个创建多个线程的程序需要多长时间,然后在终止之前等待它们返回”。
在实践中答案可能或多或少相同,但在进行基准测试时,您应该尝试将其缩小,以便对您感兴趣的内容进行基准测试,并尽可能减少外部噪音。
为什么不围绕pthread_create
/ forkIO
来电打一个计时器,因为它们是您想要衡量的?
您对启动程序所需的时间不感兴趣,所以不要花时间。你对之后加入线程需要多长时间不感兴趣,所以不要花时间。
答案 1 :(得分:0)
根据线程数量,pthread_create()
可能会在某个时刻停止创建线程;在基准测试时应该注意这一点。