Kirjoittaja Aihe: Kuorman jako monelle ytimelle  (Luettu 5237 kertaa)

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Kuorman jako monelle ytimelle
« : 21.01.21 - klo:18.30 »
Teinpä pienen python-ohjelman ratkaistakseni matemaattisen yhtälön: 4**x + 6**x = 9**x.

Koodia: [Valitse]
for x in range(1,100000):
    if 4**x + 6**x == 9**x:
        print(x)

Kun katselin Kubuntun KSysQuardilla järjestelmän kuormitusta, havaitsin HP 8200 koneella ytimen 2 kuormituksen olevan lähes koko ajan 100 % ja muut ytimet 1,3 ja 4 olivat ihan muutama prosentti hetkellisesti. Joskus esim. ydin 4 oli 20 % kuormalla pienen hetken.

Ihmettelen tätä kun olen vuosia sitten kuullut, että Linux tasaa hyvin kuormaa kahdelle ytimelle. Ainakin paremmin kuin Windows.

nm

  • Käyttäjä
  • Viestejä: 16426
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #1 : 21.01.21 - klo:19.02 »
Ihmettelen tätä kun olen vuosia sitten kuullut, että Linux tasaa hyvin kuormaa kahdelle ytimelle. Ainakin paremmin kuin Windows.

Se edellyttää, että kuorma on säikeistetty tai jaettu useaan prosessiin. Python-skriptisi suoritetaan yhdessä säikeessä. Joudut säikeistämään koodin itse, jos haluat, että sitä suoritetaan rinnakkain.

Linux ei myöskään helposti siirrä kuormaa ytimeltä toiselle, toisin kuin Windowsilla oli ainakin joskus aikoinaan tapana tehdä. Sellainen pallottelu heikentää suorituskykyä ja todennäköisesti haittaa prosessorin virransäästötoimintoja.

Tässä pari artikkelia aiheesta:

https://medium.com/mindful-engineering/multithreading-multiprocessing-in-python3-f6314ab5e23f

https://towardsdatascience.com/modern-parallel-and-distributed-python-a-quick-tutorial-on-ray-99f8d70369b8
« Viimeksi muokattu: 24.01.21 - klo:12.31 kirjoittanut nm »

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #2 : 21.01.21 - klo:19.31 »
Ajoin tuon ohjelman Kubuntussa terminaaliruudussa.

Koodia: [Valitse]
python3 yhtalo.py

Jos tällä nyt on mitään merkitystä tuossa. Jäi mainitsematta alkuperäisessä viestissä. Varmaan tilanne on sama, jos tuon ohjelman koodaisi esim. pyCharmilla ja ajaisi siinä. Ja sama juttu Javalla toteutettuna.

Ja kiitos nosita linkeistä tietolähteisiin!

nm

  • Käyttäjä
  • Viestejä: 16426
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #3 : 21.01.21 - klo:19.45 »
Jos tällä nyt on mitään merkitystä tuossa. Jäi mainitsematta alkuperäisessä viestissä. Varmaan tilanne on sama, jos tuon ohjelman koodaisi esim. pyCharmilla ja ajaisi siinä. Ja sama juttu Javalla toteutettuna.

Joo, ei riipu ohjelmointiympäristöstä, eikä juuri ohjelmointikielestäkään, ellei siinä ole silmukoiden automaattista rinnakkaistusta. Joillain kielillä tämä on helpompaa kuin toisilla, mutta viime kädessä koodaaja on itse vastuussa ongelman jakamisesta rinnakkaisesti suoritettaviin osiin.

jarmala

  • Käyttäjä
  • Viestejä: 789
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #4 : 22.01.21 - klo:01.00 »
Teinpä pienen python-ohjelman ratkaistakseni matemaattisen yhtälön: 4**x + 6**x = 9**x.

Ööh, minäkin kiinnostuin ja muokkasin koodia seuraavasti:

Koodia: [Valitse]
#! /usr/bin/python3
for x in range(1,100000):
    if (x % 100) == 0:
        print ('x =', x)
    if 4**x + 6**x == 9**x:
        print ('***** Result =', x)
        break

Mutta. Ei se löytänyt ratkaisua ainakaan välilta 1 - 99999. Entä sinulla?
Ubuntu 18.04 LTS, Gnome Flashback Metacity, Xeon E3-1245 V2, 8 GB
Ubuntu 22.04 LTS, KDE Plasma, Celeron N5105, 8 GB

nm

  • Käyttäjä
  • Viestejä: 16426
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #5 : 22.01.21 - klo:01.23 »

Yhtälölle ei ole kokonaislukuratkaisua. x≈1,18681

https://www.wolframalpha.com/input/?i=solve+4**x+%2B+6**x+%3D+9**x
« Viimeksi muokattu: 22.01.21 - klo:01.26 kirjoittanut nm »

_Pete_

  • Käyttäjä
  • Viestejä: 1845
  • Fufufuuffuuu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #6 : 22.01.21 - klo:10.21 »
Asiaan liittyen tässä esimerkkejä miten kuormaa Java:lla jaetaan monelle ytimelle:

https://mkyong.com/java8/java-8-parallel-streams-examples/

Varmaan pythonille vastaavat systeemit?


Tomin

  • Palvelimen ylläpitäjä
  • Käyttäjä / moderaattori+
  • Viestejä: 11481
    • Profiili
    • Tomin kotisivut
Vs: Kuorman jako monelle ytimelle
« Vastaus #7 : 22.01.21 - klo:13.07 »
Asiaan liittyen tässä esimerkkejä miten kuormaa Java:lla jaetaan monelle ytimelle:

https://mkyong.com/java8/java-8-parallel-streams-examples/

Varmaan pythonille vastaavat systeemit?

multiprosessing moduulilla ja Pool-luokalla onnistuu. Suurin piirtein saman verran joutuu ylimääräistä minimissään koodaamaan.

Jos kyse olisi C-kielestä, niin alkuperäisen viestin esimerkin rinnakkaistaisin openmp:llä. Sillä pääsisi verrattain vähällä lisäkoodilla.

Muokkaus: Lisätty linkit
« Viimeksi muokattu: 22.01.21 - klo:13.10 kirjoittanut Tomin »
Automaattinen allekirjoitus:
Lisäisitkö [RATKAISTU] ketjun ensimmäisen viestin aiheeseen ongelman ratkettua, kiitos.

Tomin

  • Palvelimen ylläpitäjä
  • Käyttäjä / moderaattori+
  • Viestejä: 11481
    • Profiili
    • Tomin kotisivut
Vs: Kuorman jako monelle ytimelle
« Vastaus #8 : 24.01.21 - klo:11.54 »
Keskustelu hypersäikeistyksestä on nyt omassa aiheesaan: https://forum.ubuntu-fi.org/index.php?topic=56067.0
Automaattinen allekirjoitus:
Lisäisitkö [RATKAISTU] ketjun ensimmäisen viestin aiheeseen ongelman ratkettua, kiitos.

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #9 : 03.02.21 - klo:14.12 »
Noita ohjelmointiesimerkkejä kaipailinkin. Sekä javalle, että pythonille. Kiitos.

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #10 : 03.02.21 - klo:14.25 »
Tämä ei Kubuntun KsysGuardin mukaan jakanut tasaisesti kuormaa. Yhdellä ytimellä neljästä oli lähes 100 % kuormaa ja toisella silloin tällöin noin 20 % ja loput kaksi olivat lähes toimettomia. Ohjelma ajettu konsoli-ikkunassa.

Koodia: [Valitse]
import multiprocessing as mp

def foo(q):
    for x in range(1,100000):
        if (x % 100) == 0:
            print ('x =', x)
        if 4**x + 6**x == 9**x:
            print ('***** Result =', x)
            break

if __name__ == '__main__':
    mp.set_start_method('spawn')
    q = mp.Queue()
    p = mp.Process(target=foo, args=(q,))
    p.start()
    print(q.get())
    p.join()

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #11 : 03.02.21 - klo:14.37 »
Tässä toisessa kokeessa alkoi näkyä jo hiukan kuorman tasaamista. Edelleenkin se keksittyy yhdelle ytimelle.



Koodia: [Valitse]
from multiprocessing import Process, Value, Array

def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(1000000))

    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()

    print(num.value)
    print(arr[:])

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #12 : 03.02.21 - klo:15.23 »
Tällä ohjelmalla tasaus onnistui.



Koodia: [Valitse]
from multiprocessing import Pool, TimeoutError
import time
import os

def f(x):
    return x*x

if __name__ == '__main__':
    # start 4 worker processes
    with Pool(processes=4) as pool:

        # print "[0, 1, 4,..., 81]"
        print(pool.map(f, range(10)))

        # print same numbers in arbitrary order
        for i in pool.imap_unordered(f, range(1000000)):
            print(i)

        # evaluate "f(20)" asynchronously
        res = pool.apply_async(f, (20,))      # runs in *only* one process
        print(res.get(timeout=1))             # prints "400"

        # evaluate "os.getpid()" asynchronously
        res = pool.apply_async(os.getpid, ()) # runs in *only* one process
        print(res.get(timeout=1))             # prints the PID of that process

        # launching multiple evaluations asynchronously *may* use more processes
        multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]
        print([res.get(timeout=1) for res in multiple_results])

        # make a single worker sleep for 10 secs
        res = pool.apply_async(time.sleep, (10,))
        try:
            print(res.get(timeout=1))
        except TimeoutError:
            print("We lacked patience and got a multiprocessing.TimeoutError")

        print("For the moment, the pool remains available for more work")

    # exiting the 'with'-block has stopped the pool
    print("Now the pool is closed and no longer available")

nm

  • Käyttäjä
  • Viestejä: 16426
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #13 : 03.02.21 - klo:15.32 »
Niin tuo ensimmäinen ohjelma alustaa taulukon, käynnistää yhden rinnakkaisen prosessin, mutta jää odottelemaan sen loppumista. Käynnistetty prosessi muuttaa taulukon arvot negatiiviksi ja lopettaa itsensä, jolloin vuorostaan pääohjelman suoritus jatkuu. Lopuksi pääprosessi listaa taulukon sisällön stdoutiin.

Tuossa tapauksessa ei siis tehdä mitään rinnakkain, ja lisäksi käynnistetyn prosessin laskenta kestää vain 1 – 2 sekuntia, kun taas tuloksen listaaminen päätteeseen kestää päätteestä riippuen 3 – 30 sekuntia.

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #14 : 03.02.21 - klo:15.50 »
Joo, en lukenut tuon sivun tekstejä loppuun asti, ennen kuin kokeilin jo niitä koodeja. Siitä se johtui. Siellä teksteissä oli jotain muistialueen yhteiskäytöstä. Tuo viimeinen esimerkki tasasikin sitten kuorman.

Muistelen, että Linuxissa pystyy jollain parametrilla säätelemään prosessorin/prosessorien kokonaiskuormaa tai nice-arvoja. Tai ainakin isoissa Unix-koneissa voi. Nyt kun tuon tasaamisen jälkeen jää vielä 20 % tehoista käyttämättä, niin sen käyttöönotolla saisi ikään kuin viidennen ytimen työsuorituksen. Jos siis on tiedossa, että koneelle ei tule muualta lisää kuormaa ja koneenkäyttäjä päättää tälle tehtävälle antaa koneen kaikki resurssit.

nm

  • Käyttäjä
  • Viestejä: 16426
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #15 : 03.02.21 - klo:17.01 »
Muistelen, että Linuxissa pystyy jollain parametrilla säätelemään prosessorin/prosessorien kokonaiskuormaa tai nice-arvoja.

Pystyy, mutta nykyisin prosessien skedulointi on varsin fiksua ja ytimiä on prosessoreissa paljon, joten manuaalista virittelyä ei yleensä tarvitse harrastaa. Lähinnä se voi olla tarpeen ajettaessa raskaita kuormia rinnakkain, jolloin nice-arvolla on mahdollista säätää niiden saamaa keskinäistä CPU-aikaa.

Nyt kun tuon tasaamisen jälkeen jää vielä 20 % tehoista käyttämättä, niin sen käyttöönotolla saisi ikään kuin viidennen ytimen työsuorituksen.

Havaitsemasi idle-osuus ei johdu siitä, etteikö kerneli haluaisi antaa kaikkea CPU-aikaa python-prosessiesi käyttöön. Veikkaan, että ongelmana on prosessien välisen kommunikoinnin viive. Esimerkkiohjelmasi suorittaa erittäin suuren määrän hyvin lyhytkestoisia laskentafunktioita neljän rinnakkaisen prosessin muodostamassa laskentapoolissa. Funktioiden käynnistäminen ja tulosten kerääminen vie valtaosan ajasta ja kuormittaa eniten pääprosessia. Prosessien välinen kommunikointi ja synkronointi aiheuttaa aina pienen viiveen tai odotuksen, vaikka käytettäisiin yhteistä muistia, ja tässä esimerkissä odotus haukkaa merkittävän osan suoritusajasta.

Kannattaa mieluummin jakaa laskenta pienempään määrään (10 - 1000 kpl) pitkäkestoisia funktioita, jotka sitten suoritetaan rinnakkain esimerkiksi tuon multiprocessing.Poolin avulla.

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #16 : 03.02.21 - klo:22.01 »
Muistan lukeneeni 1990-luvulla supertietokoneen ohjelmoinnista Fortran-kielellä. Siinä oli esimerkkejä, miten koodissa pienillä muutoksilla sai aikaan suuren eron lopputulosten ajoaikoihin. Nämä erot olivat mm. silmukoiden ehtolauseissa ja sen sellaisissa. Ehkä python-ohjelmassa on jotain samantapaista ja lähes kaikissa muissakin ohjelmointikielissä. Joissakin ohjelmointiympäristöissä on profiler, jolla voi mittailla ohjelmansa suoritusaikoja.

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #17 : 01.04.21 - klo:21.49 »
Kokeilin nyt 3.1 MHz Intelin i3-prosessorilla (4 ydintä) 8 GB muistin koneella (käyttis Kubuntu 20.10) Python-ohjelmankehitysympäristöllä pyCharm tekemään koodaamastani "pienen" python-ohjelman UML-kaavio. Kaavion teko kestää ja kestää ja levy sahaa. Mutta kuorma jakautuu hyvin eri ytimille.

Tulee vielä sellainen kysymys, että miksi kone varaa vain noin 4,8 GB 8 GB muistista? Jos se varaisin esim. 7 GB, niin homma voisi nopeutua?

« Viimeksi muokattu: 01.04.21 - klo:22.01 kirjoittanut ilkant »

ilkant

  • Käyttäjä
  • Viestejä: 1380
  • Kubuntu
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #18 : 02.04.21 - klo:11.55 »
Tässä on vielä esimerkki, miten kuorma ei jakaudu aina tasaisesti ytimille. Asensin pyCharmiin Julia-pluginin ja ajoin seuraavan Julia-ohjelman totient-laskun.

Koodia: [Valitse]
import Pkg; Pkg.add("Primes")
using Primes

for k = 1:10000000 Primes.totient(k) end

Ja kuorma jakautui aina vaihdellen yhdelle prosessorille 100 % kuormalla. Kehitysympäristö on sama, mutta kielenä Pythonin sijaan Julia (jonka pitäisi olla nopea).



nm

  • Käyttäjä
  • Viestejä: 16426
    • Profiili
Vs: Kuorman jako monelle ytimelle
« Vastaus #19 : 02.04.21 - klo:15.56 »
Kokeilin nyt 3.1 MHz Intelin i3-prosessorilla (4 ydintä) 8 GB muistin koneella (käyttis Kubuntu 20.10) Python-ohjelmankehitysympäristöllä pyCharm tekemään koodaamastani "pienen" python-ohjelman UML-kaavio. Kaavion teko kestää ja kestää ja levy sahaa. Mutta kuorma jakautuu hyvin eri ytimille.

Kokonaiskuorma on tuossakin silmämääräisesti katsottuna melko matala. Jokaisella ajanhetkellä vain yhden säikeen kuorma on korkeahko ja muut matalia.  Kuorma näyttää siis koostuvan lyhytkestoisista säikeistämättömistä suorituksista, jotka päätyvät satunnaisesti eri ytimille. Ei siis kovin hyvä esimerkki rinnakkaisesta suorituksesta.

Tulee vielä sellainen kysymys, että miksi kone varaa vain noin 4,8 GB 8 GB muistista? Jos se varaisin esim. 7 GB, niin homma voisi nopeutua?

Kukin ohjelma varaa juuri verran muistia kuin tarvitsee. Se riippuu ohjelman toteutuksesta sekä mm. JVM:n tapauksessa myös asetuksista, joilla virtuaalikoneelle annetaan muistia. Jotkut sovellukset osaavat käyttää muistia vähemmän tai enemmän eri tilanteissa, riippuen vapaan muistin määrästä, mutta usein ylimääräisestä muistista ei olisi mitään hyötyä sovelluksen toiminnan kannalta.

Vapaaksi jäävä muisti ei kuitenkaan ole lainkaan turhaa, vaan Linux hyödyntää sitä levyvälimuistina, mikä nopeuttaa tiedostojen kirjoitamista sekä toistuvaa lukemista.


Ja kuorma jakautui aina vaihdellen yhdelle prosessorille 100 % kuormalla. Kehitysympäristö on sama, mutta kielenä Pythonin sijaan Julia (jonka pitäisi olla nopea).

Primes.totient-funktio ei ole itsessään säikeistetty, kuten ei myöskään for-silmukka. Säikeistäminen on sinun vastuullasi:
 
https://docs.julialang.org/en/v1/manual/multi-threading/
https://docs.julialang.org/en/v1/base/multi-threading/

Koodia: [Valitse]
Threads.@threads for k = 1:10000000 Primes.totient(k) end

Määritä säikeiden lukumäärä komentorivillä tai ympäristömuuttujalla, kun käynnistät skriptin:

Koodia: [Valitse]
julia --threads 4 totient.jl