Uke 9 - Multitasking og Linux-VMer

Oppgaver til mandag 24. - fredag 28. feb

  1. (Oblig)

     
    #include <algorithm>
    #include <iostream>

    using namespace std;

    int main()
    {
       // Lager et data-array
       int i,c;
       int arraySize = 32768;
       int data[arraySize];
       
       for (c = 0; c < arraySize; ++c)
         {
        data[c] = rand() % 256;
         }
       
       // Gir tilfeldig tall mellom 0 og 255
       // Gir samme array med tall for hver kjøring
        
       // sort(data, data + arraySize);
       // sorterter data-arrayet
       
       // Skriver ut de 10 første verdiene
       for (c = 0; c < 10; c++)
         cout << data[c] << "\n";
       
       // Legger sammen alle tall større enn 127
       long sum = 0;
       
       // Ytre løkke for at det skal ta litt tid...
       for (i = 0; i < 50000; ++i)
         {
            // Indre løkke
            for (c = 0; c < arraySize; ++c)
          {
             if (data[c] > 127)
               sum += data[c];
          }
         }
       
       cout << "sum = " << sum << "\n";
    }

    Dette er på grunn av branch prediction. Når arrayet ikke er sortert er det umulig å forutsi om if-testen slår til eller ikke utifra hva som har skjedd tidligere siden tallene er helt tilfeldige og det dermed vil variere usystematisk om de er større eller mindre enn 127. Men når arrayet har blitt sortert vil ingen av de første 16.000 if-testene slå til og dermed vil branch prediction treffe hver gang. Det samme gjelder for andre halvdel av programmet hvor if-testen alltid slår til. Når branch prediction treffer hver gang vil programmet gå raskere fordi det da aldri blir problemer med pipelinen eller med å rette opp i resultatene etter en feilprediskjon.
  2. (Oblig) En diskusjon med ChatGPT:
    ChatGPT: Dette fenomenet kan forklares med hvordan moderne prosessorer bruker cache-hukommelse og hvordan 
    de forutsier dataadgangsmønstre.
    
    Når du ikke sorterer data-arrayet, er tallene i det tilfeldige. Når du går gjennom arrayet, er det ingen 
    klar mønster til tallene du sammenligner med 127, så prosessoren har ingen måte å forutsi hvilke data den 
    skal hente fra hovedminnet og laste inn i cachen.
    
    Når du sorterer data-arrayet, blir tallene ordnet i en bestemt rekkefølge. Nå, når du går gjennom arrayet, er 
    det en klar sekvens til tallene du sammenligner med 127. Dette gjør det mye lettere for prosessoren å forutsi 
    hvilke data den skal hente fra hovedminnet og laste inn i cachen.
    
    Cache-henting er mye raskere enn å hente data fra hovedminnet, så når prosessoren kan forutsi hvilke data den 
    trenger å hente, kan den gjøre det på forhånd og spare tid når dataene faktisk trengs. Dette er kjent 
    som cache-forvarming.
    
    Så selv om sortering av data-arrayet legger til en ekstra operasjon, gjør det dataadgangen under summen mye 
    mer effektiv, noe som fører til en total reduksjon i kjøretid.
    
    Dette er et flott eksempel på hvorfor det noen ganger kan være vanskelig å forutsi ytelsen til et program 
    basert på antall operasjoner alene - maskinvareegenskapene og hvordan de samhandler med programmet kan ha 
    en stor innvirkning på den faktiske ytelsen.
    -------------------------------------------------------------------------------------
    
    Haugerud: Det kan vel ikke stemme? At dataene er sortert påvirker ikke cache, den samme sekvensen av tall 
    hentes inn hver gang. Og cache er i begge tilfeller varm etter første runde i løkken. Hva kan dette 
    ellers skyldes?
    
    -------------------------------------------------------------------------------------
    
    ChatGPT: Beklager for forvirringen. Du har rett, mine unnskyldninger for feilinformasjonen. Cache-
    forutsigbarhet skulle ikke være en faktor her siden vi itererer gjennom hele arrayet sekvensielt 
    i begge tilfeller.
    
    Det som faktisk skjer her, er relatert til prosessorens branch prediction. Moderne prosessorer prøver å 
    forutsi resultatet av betingede grener (som if-uttalelser) for å forbedre ytelsen. Ved å sortere arrayet, 
    gjør vi effektivt if (data[c] > 127)-sjekken mer forutsigbar for prosessoren, noe som fører til færre 
    mispredicted branches og dermed raskere utførelse.
    
    Når arrayet er sortert, vil alle tallene større enn 127 være på slutten av arrayet, så prosessoren kan 
    effektivt forutsi at if-sjekken vil være false for den første halvparten av arrayet, og true for den andre 
    halvparten. Når arrayet ikke er sortert, er resultatet av if-sjekken tilfeldig, noe som fører til mange 
    mispredicted branches og dermed tregere utførelse.
    
    Dette er et godt eksempel på hvordan detaljene i prosessorarkitekturen kan påvirke programytelsen 
    på uventede måter.
    -------------------------------------------------------------------------------------
    
    Haugerud: Ja, den vil vel etterhvert gjette riktig hver gang, helt til man her halvveis i arrayet.
    -------------------------------------------------------------------------------------
    
    ChatGPT: Ja, det stemmer! Når du sorterer arrayet, vil prosessoren etter hvert lære å forutsi utfallet av
    if-instruksjonen korrekt. I starten vil den forutsi at utfallet alltid er false (siden alle tallene i 
    starten av det sorterte arrayet er mindre enn eller lik 127). Når den når midtpunktet i arrayet, vil den 
    merke at utfallet plutselig endrer seg til true og vil raskt tilpasse seg denne endringen, og begynner 
    å forutsi at utfallet vil være true.
    
    Dermed vil prosessorens branch predictor effektivt "følge" sorteringen av arrayet og oppnå en høy grad 
    av nøyaktighet i sine forutsigelser, noe som gjør at koden kjører raskere.
    
    Dette demonstrerer hvordan moderne prosessorer bruker sofistikerte teknikker for å forbedre ytelsen, 
    og hvordan egenskapene til dataene dine kan påvirke disse teknikkene. 
    
    -------------------------------------------------------------------------------------
    
    Haugerud: Har dette også hatt sikkerhetsmessige konsekvenser i form av malware?
    -------------------------------------------------------------------------------------
    
    ChatGPT: Ja, prosessorfunksjoner som branch prediction og cache-mekanismer kan utnyttes av skadelig 
    programvare til å få tilgang til sensitiv informasjon. To kjente eksempler på dette er Spectre og Meltdown 
    sikkerhetsfeilene som ble oppdaget i 2018.
    
        Spectre utnytter prosessorens branch prediction mekanismen til å lure prosessoren til å kjøre 
        instruksjoner som ellers ikke ville blitt kjørt, og deretter få tilgang til resultatene 
        av disse operasjonene gjennom cache.
    
        Meltdown angriper en optimalisering kalt "ut-av-rekkefølge utførelse" hvor prosessoren utfører 
        instruksjoner i en ikke-lineær rekkefølge for å maksimere ytelsen. Meltdown kan få tilgang til data 
        fra andre programmer og operativsystemet ved å utnytte denne funksjonen.
    
    Disse angrepene er mulige på grunn av den komplekse interaksjonen mellom programvare og maskinvare, og de 
    har ført til betydelige endringer i hvordan operativsystemer og prosessorer er designet og implementert 
    for å forbedre sikkerheten.
    
    
  3. (Oblig)
    $ ./regn > res.txt 2> err.txt &
    
    Hvis du logger ut, fortsetter prosessen i bakgrunnen.
  4. $ (time regn) > res.txt 2> err.txt&
    
    Parentesene gjør at også time kjører før output blir omdirigert. Uten parenteser utføres kommandoen timeregn > res.txt 2> err.txt& og output fra time blir ikke omdirigert (time printer default til stderr).
  5. data2500 har 4 CPUer. Men det kan være andre som kjører samtidig og om alle 4 CPUene er belastet, kan det bli mindre enn 100% på hver prosess.
  6. Prosessen får ca 100% CPU-tid når ingen andre CPU-krevende prosesser kjører. Når to CPU-krevende jobber kjøres samtidig blir de satt på hver sin CPU og bruker 100% hver av hver sin CPU. Når 5 prosesser kjører fordeles de på 4 CPUer, flyttes fortløpende rundt på de 4 CPUene og får ca 80% CPU-tid hver. Til enhver tid er det to prosesser på samme CPU, men hvilken det er endres. Siden 5 prosesser fordeles på 4 CPUer, vil det ta 5/4 ganger tiden det tar for en prosess å bli ferdig på en CPU. Hvis en prosess er ferdig på 40 sekunder, vil 5 prosesser bli ferdig på (5/4)*40 = 50 sekunder. Totalt trenger prosessene 5*40 "CPU-sekunder" og 200 CPU-sekunder/4CPUer = 50 sekunder.
  7. (Oblig) Din Docker container blir tildelt CPU-tid tilsvarende to CPUer. Men prosessene settes likevel til å kjøre på hver sin CPU, men får da i praksis bare 2/3 av CPU-tid på den CPU-en. Prosessen får ca 100% CPU-tid når ingen andre CPU-krevende prosesser kjører. Når to CPU-krevende jobber kjøres samtidig blir de satt på hver sin CPU og bruker 100% hver av hver sin CPU. Når 3 prosesser kjører får de ca 67% CPU-tid hver. Siden 3 prosesser fordeles på 2 CPUer, vil det ta 3/2 ganger tiden det tar for en prosess å bli ferdig på en CPU. Hvis en prosess er ferdig på 35 sekunder, vil 3 prosesser bli ferdig på (3/2)*35 = 52.5 sekunder. Totalt trenger prosessene 3*35 "CPU-sekunder" og 105 CPU-sekunder/2CPUer = 52.5 sekunder.
  8. Svaret for 4 CPUer er galt, fordi scheduleren fordeler CPU-tid jevnt mellom alle prosessene som kjøres. De kjøres ikke i to grupper med 4 og to prosesser som ChatGPT mener. Alle 6 kjører samtidig, men to vil da tii en hver tid vente. Og generelt er fremgangsmåten feil, prosessene kjører samtidig, ikke sekvensielt. Men for de andre tilfellene blir svaret likevel riktig. Prosessene scheduleres til å bytte på å bruke de tilgjengelige CPUene slik at alle får like mye CPU-tid. Totalt behøves 6 CPU * 10 miutter = 60 CPU*minutter. Med for eksempel 4 CPUer tilgjengelig tar det da 60 CPU*minutter/4 CPU = 15 minutter å få alle prosessene ferdig.
    CPUerberegningminutter
    160/160
    260/230
    460/415
    660/610
    860/610
    Hvis det ikke er flere CPUer enn prosesser, kan man altså dele antall CPU-minutter som må utføres på antall CPUer. Når det er flere CPUer enn prosesser, blir ikke disse utnyttet. Med 8 CPUer vil bare 6 av CPUene brukes og det vil totalt ta 10 minutter å bli ferdig.
  9. for i in {1..6}; do time ./a.out& done
    
    vil vise ca 27 sekunder med litt variasjon for hver av prosessene, der en prosess bruker ca 9 sekunder.
  10. (Oblig)
    sudo addgroup s123456 sudo
    
    I /etc/sudoers
    # Allow members of group sudo to execute any command
    %sudo	ALL=(ALL:ALL) ALL
    
  11. $ sudo adduser groupXX os
    $ chgrp -R os ~/felles 
    $ chmod -R 770 ~/felles 
    
    Ja, filens eier endres til den siste som lagret den.
  12. Med chmod 007 ~/fil.txt er det kun alle andre som får lov til å se på filen. Men s123456 er med i gruppen os som filen tilhører og får derfor ikke se den.
  13. (Oblig)
  14. (Oblig)
    $ screen -d -m -S felles
    
    Deretter kan alle koble seg til, logget inn som group31, med
    $ screen -x felles
    
  15. (Oblig)

     
    #! /bin/bash

    ssh group100@os100.vlab.cs.hioa.no "if [ ! -d ~/tmp ]; then mkdir ~/tmp; fi"
    ssh group100@os100.vlab.cs.hioa.no "if [ -d ~/tmp/dir ]; then /bin/rm -R ~/tmp/dir; fi"


    tar cfz dir.tgz -C /tmp dir
    scp dir.tgz group100@os100.vlab.cs.hioa.no:~/tmp
    /bin/rm dir.tgz

    ssh group100@os100.vlab.cs.hioa.no "cd ~/tmp; tar xfz dir.tgz; /bin/rm dir.tgz"

  16.  
    #! /bin/bash

    # Run every night

    /usr/bin/rsync --delete -a group60@os60.vlab.cs.oslomet.no:/home/group60  /home/s123456/home
    echo "Backup ble tatt $(date)" >> sync.log

    # m h  do# m h  dom mon dow   command
    0 3 * * * /home/haugerud/sync.sh > /dev/null 2&>1
    
  17.  
    #! /bin/bash

    # Run every night

    # Tillegg for root:

    /usr/bin/rsync --delete -a root@os60.vlab.cs.hioa.no:/etc  /iu/cube/u0/s123456/home

  18. Ja, det er mulig å legge id_rsa i .ssh og så lage en id_rsa.pub med
    ssh-keygen -f ~/.ssh/id_rsa -y
    
    og så logge inn på data2500.
  19. (Oblig) find . -type f -newermt "12 Jun 2014 00:00" ! -newermt "12 Jun 2014 23:59" -ls