2011-09-15 6 views
5

私のマシンの一部でGHC.ConcのthreadDelay関数で奇妙な動作が確認されました。次のプログラム:UbuntuでのHaskell(GHC)でのThreadDelayの問題

main = do print "start" 
      threadDelay (1000 * 1000) 
      print "done" 

は、予想どおり実行に1秒かかります。一方、このプログラム:期待通りに他のマシン上で、それは、約1秒かかりますけれども

{-# LANGUAGE BangPatterns #-} 
import Control.Concurrent 

main = do print "start" 
      loop 1000 
      print "done" 
    where loop :: Int -> IO() 
     loop !n = 
      if n == 0 
      then return() 
      else do threadDelay 1000 
        loop (n-1) 

は、私のマシンの2上で実行するのに約10秒かかります。 (私は上記の両方のプログラムを '-threaded'フラグでコンパイルしました)ここにThreadscopeのスクリーンショットがあり、10ミリ秒ごとに1回だけアクティビティがあることがわかります:Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for 10 milliseconds.

一方、プログラムは、合計1秒を要した上で、私のマシンのいずれかからThreadScope:Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for about 1 milliseconds.

同様のCプログラム:

#include <unistd.h> 
#include <stdio.h> 

int main() { 
    int i; 
    for (i=1; i < 1000; i++) { 
    printf("%i\n",i); 
    usleep(1000); 
    } 
    return 0; 
} 

は正しいことを行い、すなわち実行されている「タイム./a.outは」のような出力を提供します:

1 
2 
... 
999 

real 0m1.080s 
user 0m0.000s 
sys 0m0.020s 

この問題が発生したことがある人は誰ですか?もしそうなら、どのように修正できますか?私はすべてのマシンでLinux用のghc 7.2.1(x86_64)を実行していて、Ubuntuのさまざまなバージョンを実行しています。 Ubuntu 10.04.2ではうまく動作しますが、11.04では問題ありません。

答えて

3

threadDelayは正確なタイマーではありません。あなたのスレッドは、少なくとも、の睡眠になると約束している限り、それ以上のことは約束していません。定期的に何かをしたい場合は、何か他のものを使わなければなりません。 (私は何が分かりませんが、おそらくUnix' realtime alarm signalがあなたのために働くでしょう)

1

'-threaded'オプションを使ってコンパイルするのを忘れたと思います。 (私は6.12.3でこれを一度行いました。一貫して30ミリ秒のスレッド遅延がありました)。

+0

実際に '-threaded'フラグを付けてコンパイルしました。 – Andreas

1

上記のように、threadDelayは1つの保証をしています。つまり、あなたが要求した限り、を待ちます。 HaskellのランタイムはOSから特別な協力を得ていません

それ以外は、OSからのベストエフォートです。

threadDelaysの結果をベンチマークする価値があります。例えば:私の窓ボックスで

module Main where 
import Control.Concurrent 
import Data.Time 

time op = 
    getCurrentTime >>= \ t0 -> 
    op >> 
    getCurrentTime >>= \ tf -> 
    return $! (diffUTCTime tf t0) 

main :: IO() 
main = 
    let action tm = time (threadDelay tm) >>= putStrLn . show in 
    mapM action [2000,5000,10000,20000,30000,40000,50000] >> 
    return() 

が、これは私を与える:

0.0156098s 
0.0156098s 
0.0156098s 
0.0312196s 
0.0312196s 
0.0468294s 
0.0624392s

これは遅延のコンボを示唆してgetCurrentTimeは15.6ミリ秒の解像度を持ちます。 1000回の遅延1000をループすると、15.6秒待つことになるので、これはスレッドの最小待機に過ぎません。

私のUbuntuボックス(11.04、カーネル2.6.38-11)では、はるかに高い精度(〜100us)が得られます。

プログラムをより忙しくすることでタイミングの問題を避けることができますので、コンテキストを切り替える必要はありません。いずれにしても、私はあなたがタイミングのためにthreadDelayを使用しないこと、または少なくとも時間をチェックして、与えられた瞬間まであらゆる操作を実行することをお勧めします。あなたはFFIとマックに喜んでいる場合はCを経由して

あなたの高精度な睡眠は、あなたのために働くかもしれないが、コストは、あなたが(少なくとも、あなたのタイマーのための)結合スレッドを使用する必要がありますです。