2011-12-19 3 views
12

Sto lavorando a un progetto che è sia di memoria che di elaborazione intensiva. Una parte significativa dell'esecuzione utilizza il multi-threading con un FixedThreadPool. In breve; Ho il thread per il recupero dei dati da diverse posizioni remote (utilizzando le connessioni URL) e il popolamento di BlockingQueue con oggetti da analizzare e n. thread che selezionano questi oggetti ed eseguono l'analisi. edit: vedi codice qui sottoProblemi sporadici nell'esecuzione di un progetto Java multi-thread in Win7

Ora questa configurazione funziona come un fascino sulla mia macchina Linux in esecuzione OpenSUSE 11.3, ma un collega sta testando su una macchina molto simile in esecuzione Win7 è sempre notifiche personalizzate di timeout sulla coda polling (vedi codice sotto), molti di loro in realtà. Ho cercato di monitorare l'utilizzo del processore sulla sua macchina e sembra che il software non occupi più del 15% delle CPU mentre sulla mia macchina l'utilizzo del processore colpisce il tetto, proprio come volevo.

La mia domanda è, quindi, può essere un segno di "fame" della coda? Potrebbe essere che il thread del produttore non stia ottenendo abbastanza tempo CPU? Se sì, come faccio a dare una priorità particolare al thread in piscina?

UPDATE: ho cercato di individuare il problema, senza la gioia ... Ho fatto comunque guadagnare un po 'nuove intuizioni.

  • Il profilo dell'esecuzione del codice con JVisualVM dimostra un comportamento molto particolare. I metodi sono chiamati in brevi raffiche di CPU-time con diversi secondi di nessun progresso in mezzo. Questo per me significa che in qualche modo il sistema operativo sta colpendo i freni del processo.

  • Disabilitare l'anti-virus e demoni di back-up non hanno alcun significativo effetto sulla materia

  • modifica della priorità di java.exe (l'unico caso) attraverso il task manager (avvertita here) non cambia qualcosa. (Detto questo, non potevo dare priorità "realtime" a java, e dovevo accontentarmi di "high" prio)

  • Il profilo dell'utilizzo della rete mostra un buon flusso di dati in entrata e in uscita, quindi immagino che non è il collo di bottiglia (mentre è una parte considerevole del tempo di esecuzione del processo, ma che conosco già ed è praticamente la stessa percentuale di quello che ottengo sulla mia macchina Linux).

Tutte le idee su come il sistema operativo Win7 potrebbe essere limitante il tempo di CPU per il mio progetto? se non è il sistema operativo, quale potrebbe essere il fattore limitante? Vorrei sottolineare ancora una volta che la macchina NON sta eseguendo nessun altro calcolo intensivo allo stesso tempo e non c'è quasi nessun carico sul CPU diverso dal mio software. Questo mi sta facendo impazzire ...

EDIT: codice rilevante

public ConcurrencyService(Dataset d, QueryService qserv, Set<MyObject> s){ 

    timeout = 3; 
    this.qs = qserv; 
    this.bq = qs.getQueue(); 
    this.ds = d; 
    this.analyzedObjects = s; 
    this.drc = DebugRoutineContainer.getInstance(); 
    this.started = false; 

    int nbrOfProcs = Runtime.getRuntime().availableProcessors(); 
    poolSize = nbrOfProcs; 
    pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(poolSize); 
    drc.setScoreLogStream(new PrintStream(qs.getScoreLogFile())); 
} 

public void serve() throws InterruptedException { 
    try { 
     this.ds.initDataset(); 
     this.started = true; 
     pool.execute(new QueryingAction(qs)); 
     for(;;){ 
      MyObject p = bq.poll(timeout, TimeUnit.MINUTES); 

      if(p != null){ 
       if (p.getId().equals("0")) 
        break; 

       pool.submit(new AnalysisAction(ds, p, analyzedObjects, qs.getKnownAssocs())); 
      }else 
       drc.log("Timed out while waiting for an object..."); 

     } 

     } catch (Exception ex) { 
      ex.printStackTrace(); 
      String exit_msg = "Unexpected error in core analysis, terminating execution!"; 

     }finally{ 
      drc.log("--DEBUG: Termination criteria found, shutdown initiated.."); 
      drc.getMemoryInfo(true); // dump meminfo to log 

      pool.shutdown(); 

      int mins = 2; 
      int nCores = poolSize; 
      long totalTasks = pool.getTaskCount(), 
        compTasks = pool.getCompletedTaskCount(), 
        tasksRemaining = totalTasks - compTasks, 
        timeout = mins * tasksRemaining/nCores; 

      drc.log("--DEBUG: Shutdown commenced, thread pool will terminate once all objects are processed, " + 
         "or will timeout in : " + timeout + " minutes... \n" + compTasks + " of " + (totalTasks -1) + 
         " objects have been analyzed so far, " + "mean process time is: " + 
         drc.getMeanProcTimeAsString() + " milliseconds."); 

      pool.awaitTermination(timeout, TimeUnit.MINUTES); 
     } 

} 

La classe QueryingAction è un semplice Runnable che chiama il metodo di acquisizione dei dati nel QueryService oggetto designato che poi popola un BlockingQueue. La classe AnalysisAction esegue tutto il numero di crunch per una singola istanza di MyObject.

+0

Forse il sistema operativo stesso limita le quote di risorse del processo? – fge

+0

potrebbe essere il caso, ma io non sono abbastanza familiare con win7 per verificare se questo è il caso, qualche suggerimento su come controllare che sia il caso ?? – posdef

+0

Quanti thread di consumatori stai utilizzando? Hai provato a ridurre il numero a 1 e aumentarlo gradualmente per vedere come si evolve la situazione? – Tudor

risposta

1

Così, dopo settimane di giocherellare, wrestling in codice e di altri tipi di sofferenza Penso di aver avuto una svolta, "un momento di chiarezza", se volete ...

sono riuscito a dimostrare che il programma possa presenta lo stesso comportamento lento sulla mia macchina Linux e può infatti funzionare a tutto gas sulla problematica Win-7 macchina.Il nocciolo del problema sembra essere una sorta di corruzione dei file di sistema/cache che vengono utilizzati per archiviare i risultati di query precedenti e, nel complesso, accelerare l'analisi. Devi amare l'ironia, in questo caso sembra essere la ragione per l'analisi lenta ESTREMA. In retrospettiva, avrei dovuto saperlo (un rasoio di Occam) ...

Non sono ancora sicuro di come si verifichi la corruzione, ma almeno non è probabilmente correlato a sistemi operativi diversi. L'utilizzo dei file di sistema dalla mia macchina aumenta tuttavia l'output sull'host Win7 fino a circa il 40%. Profilare ulteriormente il processo ha anche rivelato che, stranamente, c'è molto più attività GC su Win7, che a quanto pare ha richiesto molto tempo alla CPU per il numero crunch. Dare -Xmx2g si occupa della raccolta eccessiva dei dati inutili e l'utilizzo della CPU per il processo aumenta fino al 95- 96% e i thread funzionano senza problemi.

Ora che la mia domanda iniziale risposta, devo dire che la reattività complessiva del Java è sicuramente meglio in ambiente Linux, anche senza allocare più memoria heap, posso facilmente multi-task, mentre io sono in esecuzione una vasta analisi in background . Le cose non sono così fluide in Win-7, e.x. il ridimensionamento della GUI è notevolmente lento una volta che l'analisi si avvia a tutta velocità.

Grazie per tutte le risposte, mi dispiace per la descrizione del problema parzialmente fuorviante. Ho semplicemente condiviso ciò che ho scoperto durante il debugging al meglio delle mie capacità. Ad ogni modo, credo che la ricompensa vada a Peter Lawrey, dal momento che ha subito segnalato un problema di I/O ed è stato il suo suggerimento su un thread del taglialegna che alla fine mi ha portato alla risposta.

3

ho il sospetto il filo produttore non sta ottenendo/caricamento dei dati di origine abbastanza velocemente. Questo potrebbe non essere una mancanza di CPU ma un problema relativo all'IO. (Non so perché avete time out sul BlockingQueue)

forse vale la pena avere un thread che registra periodicamente le cose come il numero di attività aggiunto e la lunghezza della coda (ad esempio ogni 5-15 secondi)

+0

Mentre sono d'accordo con te che è molto probabile che il thread del produttore non "produca" abbastanza velocemente, non capisco quale potrebbe essere la ragione di tale comportamento differenziale su due apparentemente identici macchine (eccetto il sistema operativo). Per quanto riguarda la coda; in realtà sono io a registrare i miei timeout dalla seguente chiamata: 'MyObj p = bq.poll (timeout, TimeUnit.MINUTES);' – posdef

1

La priorità non aiuta, dal momento che il problema non è un problema nel decidere chi ottiene risorse preziose - l'utilizzo delle risorse non è al massimo. L'unico modo in cui il thread del produttore non ottiene abbastanza tempo della CPU è se non fosse pronto per l'esecuzione. La priorità non aiuterà, poiché il problema non è un problema.

Quanti nuclei ha la macchina? È possibile che il thread del produttore funzioni a pieno regime e che non ci sia ancora abbastanza CPU per andare in giro. È anche possibile che il produttore sia vincolato all'I/O.

+0

Entrambe le macchine sono i7 con 4cores e hyper threading (sembrano 8 core per il sistema operativo). Ci sediamo nello stesso edificio fisico e nello stesso piano, probabilmente sullo stesso interruttore. Quindi non sono sicuro se c'è una drastica differenza tra I/O per quanto riguarda il mondo al di fuori delle scatole. :) La mia ipotesi è che Win7 abbia molti più thread associati al sistema operativo, il che potrebbe essere il motivo per cui le cose non stanno accadendo con la stessa efficienza con Linux. Tuttavia questo è solo un sentimento istintivo e non posso sostenerlo in alcun modo :) – posdef

+0

Il modo migliore per capirlo è guardare in dettaglio cosa sta succedendo sulla sua macchina. –

+0

solo per chiarire, si suggerisce di profilare la JVM mentre si esegue il software, ad esempio con jvisualvm?O ti riferisci ad un altro modo di guardarlo in dettaglio? – posdef

1

È possibile provare a separare il thread del produttore dal pool (ad esempio, creare un numero distinto Thread e impostare il pool su -1 della capacità corrente) e quindi impostare la priorità su massimo tramite setPriority. Vedi cosa succede, anche se la priorità raramente spiega una tale differenza di prestazioni.

+0

Questo è quello che stavo pensando, ma non sono sicuro che sia un metodo consigliabile. Ho letto un certo numero di post su SO, e su altre fonti, sconsigliamo di usare 'Thread' direttamente. Ma immagino che questa sarà la mia ultima risorsa. – posdef

+0

@posdef: Fintanto che risolve il problema e non ci si confonde nella gestione del produttore e dei consumatori come entità diverse, personalmente non consiglierei questa strategia. Ma, come ho detto, è difficile credere che la priorità sia l'unico fattore qui ... – Tudor

+0

Credo che alla fine finirò per implementarlo, in una versione futura del software, ma come accennato nella mia modifica non è l'unico problema giusto adesso. Apparentemente ci sono problemi più seri nel codice rispetto al fatto che il thread del produttore sia o meno nel pool. :( – posdef

1

Quando si dice la connessione URL, cosa si intende locale o remoto? Potrebbe essere che la velocità della rete sta rallentando il vostro produttore giù

+0

è una connessione remota.Tuttavia, come ho detto, dubito fortemente che sia la rete in quanto, sia le macchine linux che le win hanno hardware paragonabile, e siedono sulla stessa rete fisica.Oltre il profilo di rete mostra un buon livello (quasi) flusso di dati costante – posdef

0

penserei che fosse qualche problema specifico sistema operativo, perché questa è la differenza principale tra le due unità. Più specificamente, qualcosa sta rallentando i dati che arrivano attraverso la connessione remota.

Trova alcuni strumenti di analisi del traffico come Wireshark e/o Networx e prova a scoprire se c'è qualcosa che strozza il PC Win. Forse sta passando attraverso un proxy che ha una sorta di limite tariffario configurato.

3

Quindi, se ho capito bene il problema, è necessario un thread per recuperare i dati, e più thread per analizzare i dati recuperati. Il tuo problema è che i thread non sono sincronizzati correttamente per funzionare insieme e sfruttare appieno il processore.

hai un problema tipico produttore-consumatore con un solo produttore e diversi consumatori. Ti consiglio di rifare il tuo codice un po 'per avere, invece, diversi thread di consumatori indipendenti che attendono sempre che le risorse siano disponibili e solo poi in esecuzione. In questo modo garantisci il massimo utilizzo del processore.

filo dei consumatori:

while (!terminate) 
{ 
    synchronized (Producer.getLockObject()) 
    { 
     try 
     { 
      //sleep (no processing at all) 
      Producer.getLockObject().wait(); 
     } 
     catch (Exceptions..) 
    } 

    MyObject p = Producer.getObjectFromQueue(); //this function should be synchronized 

    //Analyse fetched data, and submit it to somewhere... 
}  

Produttore discussione:

while (!terminate) 
{ 
    MyObject newData = fetchData(); //fetch data from remote location 

    addDataToQueueu(newData); //this should also be synchronized 

    synchronized (getLockObject()) 
    { 
     //wake up one thread to deal with the data 
     getLockObject().notify(); 
    } 
} 

Si vede che in questo modo, le discussioni che sono sempre eseguendo un lavoro utile o dormire. Questo è solo un codice bozza per esemplificare. Vedi maggiori spiegazioni qui: http://www.javamex.com/tutorials/wait_notify_how_to.shtml e qui: http://www.java-samples.com/showtutorial.php?tutorialid=306

+0

Grazie per la risposta, e guarderò nei dettagli, ma sembra che manchi un punto cruciale e significativo: il mio codice ottiene la piena potenza del processore quando lo eseguo la mia macchina o un'altra macchina che esegue Win7. Quello che sto chiedendo è la ragione delle prestazioni differenziali su queste macchine simili (hardware-saggi) – posdef

+1

Ho avuto gli stessi problemi alcuni anni fa, quando lavoravo su un progetto che sarebbe stato eseguito in diversi sistemi operativi.La differenza che si ottiene è dovuta agli scheduler dei sistemi operativi. Anche se è la macchina virtuale java ad occuparsi della pianificazione, è ancora il sistema operativo che determina quando funziona jVM e quando può accedere ai sistemi remoti che si stanno utilizzando. È molto difficile specificare la differenza di comportamento, e credo che potresti perdere tempo a cercare di scoprirlo. Alla fine, sarà sempre colpa del tuo codice. Prova il mio approccio e dimmi se noti qualche miglioramento. – Anoyz

0

non dispiace davvero una risposta, ma non si adattava commento all'interno ed ancora vale la pena di lettura penso:

  • bene io non sono JAVA amichevole
  • ma recentemente ho lo stesso problema con Progetti C++ per il controllo della macchina tramite USB.
  • Su XP o W2K tutto va perfettamente per mesi di funzionamento 24/7 su ogni 2 o più core macchina
  • Su W7 e forte macchina abbastanza tutto va male, ma a volte (cca 1x per poche ore) si blocca per pochi secondi senza ragione ovvia.
  • Sulla macchina (notebook 1,66 GHz T2300E 2 core) W7 e relativamente debole i fili sono congelamento per un certo tempo ed eseguire di nuovo che sotto/overflow USB/FIFO WIN/App e comprimere comunicazione ...
    • risulta che nulla è bloccato, ma il W7 sheduler non fornisce occasionalmente la CPU ai thread corretti.
    • ho pensato che la comunicazione del driver USB (JUNGO) congeli bud che non è vero l'ho misurata ed è OK anche in freeze
    • il freeze era di circa 6-15 secondi cca una volta al minuto.
    • dopo l'aggiunta di un po 'di sicurezza posti letto per le discussioni loop il blocco è ridurre a circa 0,5 sec
    • ma ancora ci
    • anche se App Non sotto/Overflows FIFO lato driver di Windows USB fare (paio di volte al minuto per pochi ms)
  • Cambio di exe priorità/le discussioni e la classe non influisce sulle prestazioni su W7 (su XP, lavoro W2K come dovrebbe)

come si può vedere sembra che abbiamo molto probabilmente lo stesso problema.Nel mio caso:

  • non è I/O relativa (Se si sostituisce filo USB con simulazione di dispositivo si comporta simile)
  • l'aggiunta di sonno in codice critica tempo aiuta molto
  • errore è presente anche nel basso numero di thread [2 veloce (17ms) + 1 lento (250ms) + codice app = 4]
  • il mio consumo di CPU su W7 slow machine non è anche al 100% ma circa il 95% che è OK perché ho dormito ovunque
  • mie applicazioni utilizzano circa 40-100MB della memoria, ma sono di calcolo della CPU esigenti ...
    • ma non più di tanto che potrebbe funzionare in modo sicuro su più macchine più lente
    • ma a causa del collegamento driver USB e supporto per dispositivi multipli esso bisogno di almeno 2 core
  • il mio prossimo passo è quello di aggiungere un qualche tipo di registrazione di tempo di esecuzione/analizzare per vedere ciò che sta accadendo in modo più dettagliato
  • e anche po 'di riscrittura di inviare/ricevere le discussioni per vedere se aiuta

Quando imparo qualcosa di nuovo/utile lo aggiungerò.