Un ambiente di runtime può rilevare un loop infinito?


19

Sarebbe possibile per un ambiente di runtime rilevare loop infiniti e successivamente arrestare il processo associato o implementare tale logica equivarrebbe a risolvere il problema di arresto?

Ai fini di questa domanda, definisco un "ciclo infinito" per indicare una serie di istruzioni e dati stack / heap iniziali associati che, una volta eseguiti, riportano il processo esattamente allo stesso stato (inclusi i dati) di prima iniziando il ciclo infinito. (In altre parole, un programma che genera un'espansione decimale indefinitamente lunga di pi non è "bloccato" in un "ciclo infinito", perché ad ogni iterazione ha più cifre di pi da qualche parte nella sua memoria associata.)

(Portato da /programming//q/16250472/1858225 )



Io non la penso così; non ci sono vincoli sull'input.
Kyle Strand

La tua domanda riguarda un ambiente di runtime reale (come la JVM) o un modo programmaticamente generico di rilevare un loop di questo tipo?
Benj,

@Benj stackoverflow.com/q/16249785/1858225~~V~~singular~~3rd La domanda originale (che non la mia era) era di circa ambienti reali di esecuzione (o meglio, circa sistemi operativi). Ciò si è chiuso, però, quindi l'ho riscritto, ho spostato l'attenzione sul lato teorico.
Kyle Strand,

OK. L'unico modo in cui lo vedo è di campionare alcuni punti chiave e crearne uno hash (potrebbero essere le ultime righe di un output di registro o uno stato della CPU come stack ptr) e memorizzare gli hash di un set di probe (un set in un determinato momento) in una catena di Markov. Quindi, sarai in grado (scegliendo le "sonde" giuste) di rilevare i blocchi ciclici. Sto anche pensando di collegare gli accessi alle librerie di sistema e di usare le loro voci come probe. Buon divertimento;)
Benj,

Risposte:


11

Potrebbe essere teoricamente possibile per un ambiente di runtime controllare tali loop usando la seguente procedura:

Dopo aver eseguito tutte le istruzioni, l'ambiente di runtime creava un'immagine completa dello stato di un processo in esecuzione (ovvero tutta la memoria ad esso associata, inclusi registri, PC, stack, heap e globali), salvava quell'immagine da qualche parte e quindi controllava vedere se corrisponde a una qualsiasi delle immagini salvate in precedenza per quel processo. Se c'è una corrispondenza, il processo è bloccato in un ciclo infinito. Altrimenti, viene eseguita l'istruzione successiva e il processo viene ripetuto.

Infatti, anziché eseguire questo controllo dopo ogni singola istruzione, l'ambiente di runtime potrebbe semplicemente mettere in pausa il processo periodicamente e creare uno stato di salvataggio. Se il processo è bloccato in un ciclo infinito che coinvolge n stati, quindi dopo al massimo n controlli, verrà osservato uno stato duplicato.

Si noti, ovviamente, che questa non è una soluzione al problema dell'arresto; la distinzione è discussa qui .

Ma una tale caratteristica sarebbe un enorme spreco di risorse ; sospendere continuamente un processo per salvare tutta la memoria ad esso associata lo rallenterebbe tremendamente e consumerebbe un'enorme quantità di memoria molto rapidamente. (Anche se le vecchie immagini potrebbero essere eliminate dopo un po ', sarebbe rischioso limitare il numero totale di immagini che potrebbero essere salvate perché un grande ciclo infinito - cioè uno con molti stati - potrebbe non essere catturato se ci sono troppo pochi stati mantenuti in memoria.) Inoltre, questa funzionalità non fornirebbe effettivamente molti vantaggi, poiché la sua capacità di rilevare errori sarebbe estremamente limitata e poiché è relativamente semplice trovare loop infiniti con altri metodi di debug (come passare semplicemente attraverso il codice e riconoscere l'errore logico).

Pertanto, dubito che un tale ambiente di runtime esista o che esisterà mai, a meno che qualcuno non lo programma solo per calci. (Che sono un po 'tentato di fare ora.)


8
È possibile (almeno nel mondo idealizzato delle macchine di Turing e simili) che un programma entri in un ciclo infinito senza ripetere uno stato . Pensa a qualcosa come il ciclo Cfor(i = 0; ; i++) ;
vonbrand,

nn<nn+1

@vonbrand, quel particolare loop non si adatta alla mia definizione di "loop" ai fini di questa particolare domanda (motivo per cui ho reso esplicita la mia definizione nella domanda stessa).
Kyle Strand

n

Forse non ho capito la tua domanda. Pensavo volessi sapere se era possibile decidere se un programma si ripete. Stavi solo chiedendo se fosse possibile decidere se alcuni programmi ripetessero lo stato?
Huck Bennett,

6

Supponiamo che il programma non interagisca con il mondo esterno, quindi è davvero possibile incapsulare l'intero stato del programma. (Questo significa che non fa alcun input, almeno.) Inoltre, supponiamo che il programma sia in esecuzione in un ambiente deterministico in modo che ogni stato abbia un successore unico, il che significa che il runtime non è thread o che il threading può essere ridotto in modo deterministico a una sequenza.

Con queste ipotesi altamente improbabili ma teoricamente non limitative, possiamo duplicare il programma ed eseguirlo in due runtime separati; ognuno farà esattamente lo stesso calcolo.

Quindi facciamolo. Lo eseguiremo una volta nel runtime di Tortoise e allo stesso tempo lo eseguiremo nel runtime di Hare. Tuttavia, provvederemo a far funzionare il runtime Hare esattamente due volte più veloce; ogni volta che il runtime Tortoise fa un passo, il runtime Hare fa due passi.

npknkknp

Il costo totale del test è un ulteriore stato e un confronto di stato per passaggio e terminerà in non più di tre volte il numero di passaggi necessari affinché il programma completi il ​​suo primo ciclo. (Una volta nella tartaruga e due volte nella lepre, per un totale di tre volte.)

Come i termini che ho usato implicano, questo è solo il famoso algoritmo di rilevamento del ciclo di tartaruga e lepre di Robert Floyd .


3

Proprio come stavo per suggerire l'algoritmo di rilevamento del ciclo di Floyd, il post di Rici mi ha battuto. Tuttavia, l'intera cosa può essere resa più pratica accelerando i confronti degli stati pieni.

Il collo di bottiglia dell'algoritmo proposto sarebbe nel confronto dello stato completo. Questi confronti di solito non finiscono, ma si fermano presto --- alla prima differenza. Un'ottimizzazione è ricordare dove si sono verificate le differenze passate e controllare prima quelle parti dello stato. Ad esempio, mantenere un elenco di posizioni e passare attraverso questo elenco prima di effettuare un confronto completo. Quando una posizione da questo elenco espone una differenza, interrompere il confronto (con errore) e spostare la posizione in primo piano nell'elenco.

Un approccio diverso (e potenzialmente più scalabile) è l'uso dell'hashing incrementale. Scegli una funzione dello stato completo in modo tale che i valori di hash siano facili da regolare in O (1) quando una parte dello stato cambia. Ad esempio, prendi una somma ponderata di parole di stato mod alcuni primi grandi e concatena con la somma non ponderata alcuni altri primi grandi (può anche lanciare una somma modulare ponderata di quadrati di parole, con peso e modulo diversi). In questo modo, gli aggiornamenti dell'hash impiegheranno O (1) in ogni fase di esecuzione, mentre i confronti impiegheranno O (1) in tempo fino a quando non si ottiene un hit. La probabilità di un falso positivo (cioè, gli hash corrispondono mentre gli stati differiscono) è molto bassa, e anche se ciò dovesse accadere, si ammortizzerà su un gran numero di veri negativi (i falsi negativi sono impossibili).

Ovviamente, in pratica, sembra più probabile che si verifichino situazioni come la generazione di cifre del numero più --- le cose continuano a cambiare, ma non finiscono mai. Un'altra possibilità frequente è che il ciclo infinito alloca memoria, nel qual caso esaurisce rapidamente tutta la memoria disponibile.

Nel mio corso su algoritmi e strutture di dati, il nostro autografo deve fare i conti con gli studenti che a volte entrano in cicli infiniti. Ciò è gestito da un timeout di 30 secondi e da un certo limite di memoria. Entrambi sono molto più liberi dei budget di runtime e di memoria che imponiamo come parte della valutazione. Non sono sicuro che l'implementazione del vero rilevamento a ciclo infinito avrebbe molto senso in questo contesto perché tali programmi funzioneranno un po 'più lentamente (è qui che il supporto hardware per l'hash dello stato potrebbe aiutare, ma di nuovo avresti bisogno di usi aggiuntivi per giustificarlo). Quando gli studenti sanno che il loro programma è scaduto, di solito possono trovare il ciclo infinito.


2

Lo strumento di terminazione AProVE esegue analisi statiche sui sistemi di riscrittura (inclusa una sottoclasse di programmi Haskell) che possono dimostrare la non terminazione, fornendo un esempio reale di programma non terminante. La tecnica è abbastanza potente e funziona usando una variante di una tecnica chiamata restringimento .

Per quanto ne so, tuttavia, non è stato fatto molto lavoro per individuare la non terminazione effettiva per le lingue generali.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.