2011-09-15 1 views
5

Ho notato un comportamento strano con la funzione threadDelay in GHC.Conc su alcune delle mie macchine. Il seguente programma:ThreadDelay Problema in Haskell (GHC) su Ubuntu

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

richiede 1 secondo per l'esecuzione, come previsto. D'altra parte, questo programma:

{-# 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) 

richiede circa 10 secondi per l'esecuzione su due delle mie macchine, anche se su altre macchine ci vogliono circa 1 secondo, come previsto. (Ho compilato entrambi i programmi sopra con il flag "-threaded".) Ecco una schermata di Threadscope che mostra che c'è attività solo una volta ogni 10 millisecondi: Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for 10 milliseconds.

D'altra parte, ecco uno screenshot da ThreadScope da una delle mie macchine su cui il programma prende 1 secondo totale: Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for about 1 milliseconds.

un simile programma 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; 
} 

fa la cosa giusta, cioè in esecuzione 'di tempo ./a.out' un output come :

1 
2 
... 
999 

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

Qualcuno ha riscontrato questo problema prima e, in caso affermativo, come può essere risolto? Sto eseguendo ghc 7.2.1 per Linux (x86_64) su tutte le mie macchine e sto eseguendo varie versioni di Ubuntu. Funziona male su Ubuntu 10.04.2, ma va bene per la 11.04.

risposta

3

threadDelay non è un timer preciso. Promette che il thread dormirà per almeno finché il suo argomento dice che dovrebbe, ma non promette nulla di più. Se vuoi che qualcosa avvenga periodicamente, dovrai usare qualcos'altro. (Non sono sicuro di cosa, ma probabilmente Unix' realtime alarm signal potrebbe funzionare per voi.)

1

Ho il sospetto che abbiate dimenticato di compilare con l'opzione '-threaded'. (L'ho fatto una sola volta per 6.12.3 e ha sempre avuto ritardi di 30 millisecondi.)

+0

Ho effettivamente compilato con la bandiera '-favorata'. – Andreas

1

Come indicato sopra, threadDelay offre una sola garanzia, ovvero l'attesa di almeno finché richiesto. Il runtime di Haskell non ottiene una cooperazione speciale dal sistema operativo

A parte questo, è il miglior sforzo dal sistema operativo.

Potrebbe valere la pena eseguire il benchmark dei risultati per threadDelays. Per esempio:

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() 

Sulla mia casella di Windows, questo mi dà:

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

Questo suggerisce la combinazione di ritardo e getCurrentTime ha una risoluzione di 15,6 millisecondi. Quando eseguo il ciclo 1000 volte di ritardo 1000, finisco per aspettare 15.6 secondi, quindi questa è solo l'attesa minima per un thread.

Sulla mia finestra Ubuntu (11.04, con kernel 2.6.38-11), ottengo molta più precisione (~ 100us).

Potrebbe essere possibile evitare il problema di temporizzazione mantenendo il programma più occupato, in modo da non cambiare contesto. Ad ogni modo, ti suggerirei di non utilizzare threadDelay per i tempi, o almeno controllare l'ora ed eseguire qualsiasi operazione fino all'istante dato.

Il tuo sonno ad alta precisione tramite C potrebbe funzionare per te, se sei disposto a fustigare con FFI, ma il costo è che dovrai utilizzare i thread associati (almeno per il tuo timer).