Esporre il non determinismo derivante dallo scheduler dei thread del sistema operativo


10

Come tutti sappiamo, i moderni sistemi operativi dispongono di programmatori di thread che possono selezionare diversi ordini per pianificare i thread in base alla logica interna a cui il codice non è a conoscenza. Normalmente architetti il ​​tuo codice multithread per assicurarti che questo non determinismo che ti viene imposto non influisca significativamente sul tuo output.

L'obiettivo qui è l'opposto. Produrre un programma che stampa i numeri interi nell'intervallo [0,99] ma in un ordine che varierà da corsa a corsa a causa del programmatore di thread del sistema operativo.

Devi raggiungere "abbastanza non determinismo", definito come:

In 10 serie sequenziali di 10 prove il programma deve produrre almeno 9 permutazioni uniche all'interno di ciascuna prova. Potresti avere un numero ragionevole di prove fallite su entrambi i lati dei 10 consecutivi che hanno esito positivo.

Oppure, per dirla in altro modo, sono necessarie 100 esecuzioni del programma in cui ogni blocco di 10 esecuzioni ha al massimo due esecuzioni che producono la stessa cosa.

Quindi, a volte lo scambio di 98 e 99 non lo taglierà.

Questo è un , quindi vince la risposta che utilizza il minor numero di byte.

minutiae

  • Scrivi il tuo output su stdout, una voce per riga
  • Se si altera il formato con due thread interlacciati che i caratteri scrivono su stdout (anche occasionalmente) causando cose come numeri a tre cifre o righe vuote, il risultato non è valido
  • L'unica eccezione alla regola sopra è che puoi emettere una singola riga vuota dopo aver stampato l'ultimo numero richiesto (prego)
  • In caso di perdita o duplicazione dei valori richiesti, il risultato non è valido
  • Il tuo programma non deve essere non deterministico su un processore single core (anche se complimenti se lo è)
  • Il tuo programma può usare fili / fibre verdi che non sono effettivamente gestiti dal kernel del sistema operativo se soddisfa ancora gli altri requisiti della sfida e il sistema di threading fa parte della tua lingua o della libreria standard per la tua lingua
  • Il tempo di esecuzione del programma deve essere affidabile in meno di 5 secondi su un processore moderno
  • Non è possibile specificare le modifiche all'ambiente che si verificano al di fuori del programma, ad esempio le modifiche alle attese o alle impostazioni; il tuo programma dovrebbe passare se eseguito 100 volte indietro o con un'ora tra ogni corsa o 100 volte in parallelo (che probabilmente aiuterebbe effettivamente ...)
  • È possibile utilizzare un coprocessore come una GPU o Xeon Phi e il proprio meccanismo di pianificazione interno per le attività. Le regole si applicano a questo nello stesso modo in cui si applicano ai thread verdi.
  • Sentiti libero di provocare lo scheduler con ogni tipo di sonno, resa e altri trucchi, purché obbedisca alle regole specificate in questo post

Operazioni vietate

L'unica fonte di non determinismo su cui puoi fare affidamento è quando lo scheduler pianifica l'esecuzione dei tuoi thread. Il seguente elenco non è esaustivo, inteso solo a fornire esempi di cose che non è consentito fare in quanto ammettono altre fonti di non determinismo.

  • Accesso diretto o indiretto a qualsiasi tipo di funzionalità PRNG o hardware RNG (a meno che non sia parte integrante dello scheduler).
  • Lettura in qualsiasi tipo di input (ora di sistema, filesystem, rete, ecc.)
  • Lettura ID thread o ID processo
  • Personalizzazione dello scheduler del sistema operativo; è necessario utilizzare uno scheduler del sistema operativo standard da un sistema operativo tradizionale
  • È inoltre vietato personalizzare il tuo programmatore di filo verde / fibra. Ciò significa che se scrivi una lingua per questa sfida devi usare i thread del sistema operativo.

Convalida risposta

Preferibilmente una risposta funzionerebbe su tutti i sistemi operativi comuni e sui processori moderni, con i complimenti assegnati in proporzione all'ampiezza del supporto. Tuttavia, questo non è un requisito della sfida. Almeno una risposta deve supportare un moderno processore SMP e un moderno sistema operativo. Testerò le risposte principali nella misura della mia disponibilità hardware.

  • Se la voce non produrrà l'output richiesto su un i7 5960x con Windows 10 v1607 x64, specificare l'ambiente richiesto
  • Se è qualcosa che posso riprodurre facilmente con VMWare Workstation, fornisci le specifiche esatte del sistema operativo e della VM
  • Se non è possibile produrlo in nessuna di queste condizioni, registra una cattura dello schermo simultanea del test come descritto nella sezione dell'intestazione e una registrazione video portatile dello schermo con l'interazione del mouse e della tastiera (o qualsiasi schema di controllo il tuo calcolo non standard il dispositivo utilizza) chiaramente visibili e pubblica entrambi i video insieme alla tua risposta e include una spiegazione del perché funziona
  • In alternativa, ottieni un utente di lunga data rispettabile (che non sei tu) con hardware corrispondente per riprodurre il risultato e garantirlo per te
  • Se la tua voce è in un linguaggio di programmazione esotico che uno sviluppatore tipico non sarà impostato per compilare / jit / interpretare, fornisci le istruzioni di installazione
  • Se la voce dipende da una versione specifica dell'interprete / altro JVM / Python, specificare quale
  • Se ci vogliono più di 10 minuti di tiri consecutivi per ottenere i tuoi 10 set sequenziali di prove di successo nei miei test, fallisci (quindi non lasciare che la condizione di successo sia un evento bizzarro, specialmente se sei vicino alla parte superiore tempo di esecuzione)

4
-1 per "Se mi annoio ....". Direi specificare esattamente quanto tempo può richiedere.
Rɪᴋᴇʀ

@EasterlyIrk Dice anche 'affidabile in meno di cinque secondi su una CPU moderna'
Pavel

@Pavel non è quello a cui mi riferisco. Le 10 prove riuscite non sono correlate ai 5 secondi.
Rɪᴋᴇʀ

@EasterlyIrk Abbastanza giusto, ora sono 10 minuti.
Techrocket9,

@ Techrocket9 cool, downvote annullato.
Rɪᴋᴇʀ

Risposte:


4

Perl 6 , 27 byte

await map {start .say},^100

Spiegazione:

      map {          },^100  # Iterate over the range 0..99, and for each of number:
           start             # Send the following task to a thread pool of OS threads:
                 .say        # Print the number, followed by a newline.
await                        # Wait until all tasks have completed.

Spero che questo soddisfi il compito. (In caso contrario, per favore fatemelo sapere).

test:

Lo script della shell che ho usato per testare il non determinismo sufficiente:

#!/usr/bin/bash
for i in {1..10}; do
    set=""
    for j in {1..10}; do
        set="${set}$(perl6 nondet.p6 | tr '\n' ',')\n"
    done
    permutations="$(echo -e "$set" | head -n -1 | sort | uniq | wc -l)"
    echo -n "$permutations "
done

Per me, questo produce:

10 10 10 10 10 10 10 10 10 10 

Istruzioni di installazione:

Ho eseguito il test con un Rakudo Perl 6 aggiornato su Linux a 64 bit, anche se immagino che funzionerà su altre piattaforme.

La pagina di download di Rakudo contiene istruzioni per l'installazione. Ho compilato il mio da Git in questo modo:

git clone git@github.com:rakudo/rakudo.git
cd rakudo
perl Configure.pl --gen-moar --make-install
export PATH="$(pwd)/install/bin/:$PATH"

Provalo online:

O semplicemente provalo online, usando questo link Provalo online fornito da @ b2gills. Ho controllato alcune corse e ho ricevuto un ordine diverso ogni volta, ma non ho avuto la pazienza di eseguirlo 100 volte attraverso quell'interfaccia online.



Convalidato su Windows 10 x64 su un i7 5960x con Rakudo Perl versione 2016.11
Techrocket9

4

bash, 32 28 byte

for i in {0..99};{ echo $i&}

L'ho eseguito 100 volte e ho ottenuto 100 risultati diversi.

Modifica: salvato 4 byte grazie a @DigitalTrauma.


Mi hai battuto sul tempo. In realtà il mio è un po 'più breve for i in {0..99};{ echo $i&}, ma hai pubblicato prima - puoi prenderlo :)
Digital Trauma

Ecco un modo per testarlo in TIO. Questo fa 10 esecuzioni dello script, catturando l'output di ogni esecuzione, e md5 l'output di ciascuna esecuzione. Possiamo vedere ogni volta che gli md5 sono diversi. Gli md5 sono ordinati per rendere evidenti i potenziali duplicati.
Trauma digitale

@DigitalTrauma Non documentato ma carino!
Neil,

1
Sì - c'è un suggerimento per questo.
Trauma digitale

È interessante notare che questo non riesce a raggiungere "sufficiente non determinismo" quando viene eseguito nel bash-on-windows ufficiale di Microsoft su una E5-2699 v4, ma funziona in una VM RHEL Workstation con 4 core sulla stessa macchina in modo che passi.
Techrocket9,

2

PowerShell , 54 46 44 39 byte

workflow p{foreach -p($i in 0..99){$i}}

I flussi di lavoro di PowerShell non sono supportati in TIO, quindi non puoi provarlo lì. Dovrebbe funzionare benissimo sul tuo computer Windows 10 :)

Definisce una funzione pche produrrà l'elenco di numeri quando viene richiamato.

sincronizzazione

Una sola corsa funziona in modo affidabile in circa 600 ms sulla mia macchina. I 100 test definiti di seguito terminano in meno di 2 minuti.

analisi

Ecco il codice completo per testarlo:

workflow p{foreach -p($i in 0..99){$i}}
#workflow p{foreach($i in 0..99){$i}}
# uncomment above to prove testing methodology does detect duplicates

1..10 | % {
    $set = $_
    Write-Host "Set $set of 10"
    1..10 | % -b {
        $runs = @()
    } -p {
        $run = $_
        Write-Host "-- Run $run of 10 in set $set"
        $runs += "$(p)"
    } -e {
        Write-Host "-- There were $(10-($runs|Get-Unique).Count) duplicate runs in set $set"
    }
}

Uscita sulla mia macchina:

Set 1 of 10
-- Run 1 of 10 in set 1
-- Run 2 of 10 in set 1
-- Run 3 of 10 in set 1
-- Run 4 of 10 in set 1
-- Run 5 of 10 in set 1
-- Run 6 of 10 in set 1
-- Run 7 of 10 in set 1
-- Run 8 of 10 in set 1
-- Run 9 of 10 in set 1
-- Run 10 of 10 in set 1
-- There were 0 duplicate runs in set 1
Set 2 of 10
-- Run 1 of 10 in set 2
-- Run 2 of 10 in set 2
-- Run 3 of 10 in set 2
-- Run 4 of 10 in set 2
-- Run 5 of 10 in set 2
-- Run 6 of 10 in set 2
-- Run 7 of 10 in set 2
-- Run 8 of 10 in set 2
-- Run 9 of 10 in set 2
-- Run 10 of 10 in set 2
-- There were 0 duplicate runs in set 2
Set 3 of 10
-- Run 1 of 10 in set 3
-- Run 2 of 10 in set 3
-- Run 3 of 10 in set 3
-- Run 4 of 10 in set 3
-- Run 5 of 10 in set 3
-- Run 6 of 10 in set 3
-- Run 7 of 10 in set 3
-- Run 8 of 10 in set 3
-- Run 9 of 10 in set 3
-- Run 10 of 10 in set 3
-- There were 0 duplicate runs in set 3
Set 4 of 10
-- Run 1 of 10 in set 4
-- Run 2 of 10 in set 4
-- Run 3 of 10 in set 4
-- Run 4 of 10 in set 4
-- Run 5 of 10 in set 4
-- Run 6 of 10 in set 4
-- Run 7 of 10 in set 4
-- Run 8 of 10 in set 4
-- Run 9 of 10 in set 4
-- Run 10 of 10 in set 4
-- There were 0 duplicate runs in set 4
Set 5 of 10
-- Run 1 of 10 in set 5
-- Run 2 of 10 in set 5
-- Run 3 of 10 in set 5
-- Run 4 of 10 in set 5
-- Run 5 of 10 in set 5
-- Run 6 of 10 in set 5
-- Run 7 of 10 in set 5
-- Run 8 of 10 in set 5
-- Run 9 of 10 in set 5
-- Run 10 of 10 in set 5
-- There were 0 duplicate runs in set 5
Set 6 of 10
-- Run 1 of 10 in set 6
-- Run 2 of 10 in set 6
-- Run 3 of 10 in set 6
-- Run 4 of 10 in set 6
-- Run 5 of 10 in set 6
-- Run 6 of 10 in set 6
-- Run 7 of 10 in set 6
-- Run 8 of 10 in set 6
-- Run 9 of 10 in set 6
-- Run 10 of 10 in set 6
-- There were 0 duplicate runs in set 6
Set 7 of 10
-- Run 1 of 10 in set 7
-- Run 2 of 10 in set 7
-- Run 3 of 10 in set 7
-- Run 4 of 10 in set 7
-- Run 5 of 10 in set 7
-- Run 6 of 10 in set 7
-- Run 7 of 10 in set 7
-- Run 8 of 10 in set 7
-- Run 9 of 10 in set 7
-- Run 10 of 10 in set 7
-- There were 0 duplicate runs in set 7
Set 8 of 10
-- Run 1 of 10 in set 8
-- Run 2 of 10 in set 8
-- Run 3 of 10 in set 8
-- Run 4 of 10 in set 8
-- Run 5 of 10 in set 8
-- Run 6 of 10 in set 8
-- Run 7 of 10 in set 8
-- Run 8 of 10 in set 8
-- Run 9 of 10 in set 8
-- Run 10 of 10 in set 8
-- There were 0 duplicate runs in set 8
Set 9 of 10
-- Run 1 of 10 in set 9
-- Run 2 of 10 in set 9
-- Run 3 of 10 in set 9
-- Run 4 of 10 in set 9
-- Run 5 of 10 in set 9
-- Run 6 of 10 in set 9
-- Run 7 of 10 in set 9
-- Run 8 of 10 in set 9
-- Run 9 of 10 in set 9
-- Run 10 of 10 in set 9
-- There were 0 duplicate runs in set 9
Set 10 of 10
-- Run 1 of 10 in set 10
-- Run 2 of 10 in set 10
-- Run 3 of 10 in set 10
-- Run 4 of 10 in set 10
-- Run 5 of 10 in set 10
-- Run 6 of 10 in set 10
-- Run 7 of 10 in set 10
-- Run 8 of 10 in set 10
-- Run 9 of 10 in set 10
-- Run 10 of 10 in set 10
-- There were 0 duplicate runs in set 10

È interessante notare che questo richiede 51 secondi per corsa sul mio box E5-2699 v4, ma solo .7 secondi sul mio laptop i5-5200U. Raggiunge il grado di non determinismo richiesto sul laptop mentre arriva al di sotto del massimo di 5 secondi, quindi passa. Apparentemente, lo scheduler di PowerShell non funziona bene con molti core e attività brevi.
Techrocket9,

E ci vogliono 58 secondi sull'i7 5960x
Techrocket9 il

Hm ... 74 secondi su un laptop i5-6300U. Forse è un problema con Windows 10 o PowerShell 5.1, poiché l'i5-5200U è l'unica macchina tra quelle testate che non eseguono Win10 (è in esecuzione 8.1).
Techrocket9,

@ Techrocket9 strano, stavo testando su Win10, PS 5.1. In ISE però.
Briantist

2

GCC su Linux, 47 byte

main(i){for(i=99;fork()?i--:!printf("%d\n",i););}

Questo mi ha dato risultati diversi praticamente ogni volta, essendo stato compilato gcc (no flags) versione 4.9.2. In particolare, ero su Debian 8.6 a 64 bit (versione del kernel 3.16.31).

Spiegazione

Se fork()restituisce zero (processo figlio), il valore di iviene stampato e la condizione del ciclo è falsa, poiché printfrestituirà un valore maggiore di zero. Nel processo padre, la condizione del ciclo è giusta i--.


Come la risposta bash. Deterministico su Windows, ma passa su Linux (in questo caso Debian).
Techrocket9,
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.