Kirjoittaja Aihe: Onko pythonissa tapaa vapauttaa muistia kun ei muuttujaa enää tarvita ?  (Luettu 3780 kertaa)

kamara

  • Käyttäjä
  • Viestejä: 3031
    • Profiili
Onko pythonissa tapaa vapauttaa muistia kun ei muuttujaa enää tarvita ?

Toinen kysymys...
Voiko pythonissa parametrina funktiolle antaa pelkän osoittimen tai viitteen datasta ?

Käytännön ongelmana itselläni on, että lataan koneelle 1 Gt:n CSV-tiedoston, jota käsittelen pythonilla s.e. se kuluttaa ihan perusosassakin jopa 10 Gt, joten sen käyttämää kulutusta pitäisi pystyä pienentämään.

SuperOscar

  • Käyttäjä
  • Viestejä: 4062
  • Ocatarinetabellatsumtsum!
    • Profiili
    • Legisign.org
Joku tiennee tekniset yksityiskohdat paremmin, mutta:

a) Pythonissa on automaattinen roskienkeruu.

b) Pythonin tyypit jakautuvat kahtia sen mukaan, miten ne parametreina funktioon välitetään. Listat ja sanastot välitetään viitteinä, ei kopioina.
pöytäkone 1, NUC: openSUSE Leap 15.6, kannettavat 1–3: Debian GNU/Linux 12; pöytäkone 2: openSUSE Tumbleweed; RPi 1: FreeBSD 14-RELEASE; RPi 2: LibreELEC 11

kamara

  • Käyttäjä
  • Viestejä: 3031
    • Profiili
Joku tiennee tekniset yksityiskohdat paremmin, mutta:

a) Pythonissa on automaattinen roskienkeruu.

b) Pythonin tyypit jakautuvat kahtia sen mukaan, miten ne parametreina funktioon välitetään. Listat ja sanastot välitetään viitteinä, ei kopioina.

A- kohdan toki itsekin tiesin...

B-kohta on kiva tietää...

Taitaneen pitää tutkia vähän omaa räpellystä, josko keksisin tavan vapauttaa muistia ...

nm

  • Käyttäjä
  • Viestejä: 16428
    • Profiili
Onko pythonissa tapaa vapauttaa muistia kun ei muuttujaa enää tarvita ?

Olion (objektin) varaama muisti vapautuu automaattisesti heti, kun kyseiseen olioon ei enää ole viittauksia. Esimerkiksi:

1. Luodaan lista (eli taulukko), jossa on kolme lukua. Viitataan taulukkoon muuttujalla a:

Koodia: [Valitse]
a = [1, 2, 3]
2. Luodaan toinen lista ja vaihdetaan muuttuja a viittaamaan siihen:

Koodia: [Valitse]
a = [4]
Tässä viittaus alkuperäiseen listaolioon [1, 2, 3] häviää, ja Python vapauttaa olion muistista.

Ongelmia syntyy syklisistä viittauksista, eli jos esimerkin alkuperäisessä listassa olisi ollut viittaus listaan itseensä:

Koodia: [Valitse]
a = [1, 2, 3]
a.append[a] # Tässä lisätään listan perään viittaus listaan itseensä
a = [4]

Nyt varsinainen viittaus listaan [1, 2, 3, [...]] poistetaan, mutta sen sisälle jää syklinen viittaus, eikä pelkkä reference counting tunnista, että olio pitäisi poistaa muistista. Sen sijaan Pythonin roskienkerääjä (garbage collector) osaa poistaa tällaiset tapaukset. Roskienkeruu suoritetaan ajoittain, eli ylimääräistä muistia voi jäädä varatuksi joksikin aikaa. Jos ohjelma käsittelee suuria datamääriä, tästä voi aiheutua oikeastikin ongelmia. Silloin voi olla tarpeen pyytää välitöntä roskienkeruuta kutsumalla gc.collect()-funktiota.

Käytännössä verkkotietorakenteita lukuun ottamatta sykliset viittaukset ovat harvinaisia, ja Python vapauttaa muistin automaattisesti heti kun viittaukset poistetaan.

Katso myös:
https://stackoverflow.com/questions/34146304/why-should-you-manually-run-a-garbage-collection-in-python
https://docs.python.org/3/library/gc.html


Toinen kysymys...
Voiko pythonissa parametrina funktiolle antaa pelkän osoittimen tai viitteen datasta ?

Python käyttää "pass by assignment"-tyyppistä funktion parametrien välitystä. Käytännössä se tarkoittaa, että funktion parametreihin sijoitetaan funktiokutsussa annetut arvot. Se johtaa siihen, että funktion suorituksen alussa parametrit ovat viittauksia samoihin olioihin, jotka funktiolle on annettu kutsuttaessa. Esimerkiksi:

Koodia: [Valitse]
def appendzero(l):
     l.append(0)

a = [1,2,3]
appendzero(a)
print(a)
Lainaus
[1, 2, 3, 0]

Perustyypit kuten kokonais- ja liukuluvut ja merkkijonot ovat kuitenkin immutable-olioita, jolloin funktio ei voi vaikuttaa niiden sisältöön siten, että muutos näkyisi funktion ulkopuolella. Ylläolevan esimerkin taulukko sen sijaan on mutable, ja siksi siihen lisätty uusi alkio näkyy pääohjelman puolella. Myös taulukossa ennestään olevat alkiot voi korvata uusilla, vaikka ne sinänsä ovat immutable-tyyppisiä. Taulukossa vain vaihdetaan viittaukset uusiin olioihin ja vanhat lukuarvot vapautuvat muistista, koska niihin ei enää viitata.

Katso myös:
https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
https://medium.com/school-of-code/passing-by-assignment-in-python-7c829a2df10a
https://realpython.com/python-pass-by-reference/


Käytännön ongelmana itselläni on, että lataan koneelle 1 Gt:n CSV-tiedoston, jota käsittelen pythonilla s.e. se kuluttaa ihan perusosassakin jopa 10 Gt, joten sen käyttämää kulutusta pitäisi pystyä pienentämään.

Kuulostaa siltä, että koodi lataa tiedoston sisällön kokonaisuudessaan muistiin ja tekee siitä mahdollisesti useampia kopioita käsittelyn aikana.

Kannattaa tosiaan pohtia, miten ohjelma täsmälleen käsittelee muistiin ladattua dataa, ja tarvitaanko se kokonaisuudessaan. Ratkaistavasta ongelmasta riippuen voi olla mahdollista iteroida CSV-tiedostoa rivi kerrallaan tallentamatta koko sisältöä muistiin. Esimerkiksi keskiarvot ja maksimit ja minimit voi laskea hyvin pienellä muistinkäytöllä.

Voidaan katsoa ongelmallista koodia tarkemminkin, jos pystyt näyttämään sen.
« Viimeksi muokattu: 15.03.23 - klo:15.16 kirjoittanut nm »

kamara

  • Käyttäjä
  • Viestejä: 3031
    • Profiili
Kuulostaa siltä, että koodi lataa tiedoston sisällön kokonaisuudessaan muistiin ja tekee siitä mahdollisesti useampia kopioita käsittelyn aikana.

Jep, kristallipallosi on kunnossa, ja pelittää hyvin. 8)
Ajattelin manipuloida muistissa csv-tiedostoa, jotta kone tekisi sen nopeasti, ja kyllähän kone ruksuttaa sen menemään ihan kohtuullisen nopeasti.

Ja jep toisen kerran...
Datasta tehdään muutama kopio muistiin, ennen kuin on tarvetta vapauttaa muistia muihin tehtäviin.

Eli mistä olet löytänyt noin hyvän kristallipallon ?  ;D

Kannattaa tosiaan pohtia, miten ohjelma täsmälleen käsittelee muistiin ladattua dataa, ja tarvitaanko se kokonaisuudessaan. Ratkaistavasta ongelmasta riippuen voi olla mahdollista iteroida CSV-tiedostoa rivi kerrallaan tallentamatta koko sisältöä muistiin. Esimerkiksi keskiarvot ja maksimit ja minimit voi laskea hyvin pienellä muistinkäytöllä.

Ja olet oikeassa, että sen voi toteuttaa myös pienemmällä muistin käytöllä, vaikkakin otan tällä hetkellä mediaanin koko roskasta, mutta mediaanin sijasta riittäisi myös keskiarvoa lähellä oleva jokin arvo, jonka hakeminen ei olisi kuorman kannalta niin raskasta.

Voidaan katsoa ongelmallista koodia tarkemminkin, jos pystyt näyttämään sen.

Nyt meni vähän pieleen ...
... vielä ei ole tullut ongelmaa koodin kanssa, mutta hätkähdin testiajossa muistinkulutusta, joka saattaa myöhemmin osoittautua ongelmaksi.

Lisäksi vielä en näytä koodia A -> Se on varsin rumaa purkkakoodia, B -> Aivan vielä ei ole tullut ongelmaa eteen, vaikkakin ohjelma tuhlaa muistia aivan liikaa.

Ratkaisuksi poistan purkkaa vähän välistä, ja teen tiiviimmän ohjelman, ja muutan vähän rakennetta, jolloin on helpompi debugata koodia, kun nyt ymmärrän omassa päässäni vähän paremmin, kuinka se kannattaa toteuttaa.

Edit - Ja unohdin kiittää paljosta vaivannäöstä pieneen ongelmaani. Kiitos.
« Viimeksi muokattu: 15.03.23 - klo:18.44 kirjoittanut kamara »

nm

  • Käyttäjä
  • Viestejä: 16428
    • Profiili
Ja olet oikeassa, että sen voi toteuttaa myös pienemmällä muistin käytöllä, vaikkakin otan tällä hetkellä mediaanin koko roskasta, mutta mediaanin sijasta riittäisi myös keskiarvoa lähellä oleva jokin arvo, jonka hakeminen ei olisi kuorman kannalta niin raskasta.

Jees. Jos tehtävänä on laskea mediaanin tapaisia tunnuslukuja tai käsitellä dataa eri ulottuvuuksissa, kannattaa harkita Pandas- tai Polars-kirjastoa. Niiden sisäiset tietorakenteet ovat huomattavasti tiiviimpiä ja laskenta on nopeampaa kuin Pythonin olioiksi käärityillä tietotyypeillä. CSV-tiedoston lukeminen suoraan Polarsin dataframeksi ja sarakkeiden mediaanien laskeminen on moninkertaisesti geneeristä Python-toteutusta nopeampaa ja muistia kuluu luultavasti puolet vähemmän.

Jere Sumell

  • Käyttäjä
  • Viestejä: 742
  • Talous, Hallinto ja Markkinointi (AMK, 2017),B.B.A
    • Profiili
    • Tietokone-blogi
Jotenkin ei ole Pythonissa itse en ole funktioita niitä määritellessäni mieltänyt olioiksi, viittauksiksi muistipaikkaan, jossa fyysisesti tietokoneen musitissa se funktion data sijaitsee, siitä lähtien, kun tutustuin Pythoniin 2014 Turtle -tutoriaalilla ,"Sinaporen materiaali", olen mieltänyt muuttujat alusta lähtien olioiksi, mitä niihin voidaan kohdistaa .-notaatiolla funktiokutsuja ilman sen lisäksi, että alustus ja määrittelyvaiheessa ei tarvitse määritellä tietotyyppiä, siitä päätellen.

Onko muuten jotain käytännön vinkkejä, missä tilanteissa itse määriteillyillä funktioilla voisi olla hyödyllistä kohdentaa niihin jotain funktiokutstuja tai antaa yksittäisille muuttujille joko jokin suora arvo, tai sitten funktiokutsun kautta sen kutsuttavan funktion palautusarvo? Nekin on sitten olioita, muistiviittauksia, mitä tuli vastaan tämä nyt "Kaikki on Pythonissa olioita", mitä katsoin tämän säikeen innoittamana kuukelilta kysyin.

Jake VanderPlas:in dokumentti verkossa CC-lisenssillä kohdasta "Everything is an Object"
https://jakevdp.github.io/WhirlwindTourOfPython/03-semantics-variables.html

Tutorialspoint nyt tuttu lähde, mutta kyseisen julkaisun toimittaja/tutoriaalikirjoittaja Rajendra Dharmkar antaa yksinkertaisen esimerkin määrittelee funktion, joss sitten funktion nimen perässä pistenotaatiolla muuttujanimelä kutsutaan print() -tulostuslauseen sisällä. Tuo nyt ei ole kovin arkielämän hyödyllinen esimerkki, mutta esimerkki nyt kuitenkin.

https://www.tutorialspoint.com/Are-Python-functions-objects

Onko muutein noille itse määritellyille funktioille Pythonissa olemassa jotain kirjastoa tms. vastaavaa, jossa olisi nippu attribuutteja ja funktioita, joita voi kutsua itse määritellyihin funktioihin ilman, että niitä on määritellyt siihen oman funktion koodiin?
Free Internet and  people for humans all over the globe!

(Profiilikuvassa oma valokuvani GIMPissä editoituna Disney Classic-väripaletin väreihin ja muunnettuna bittikartta-tiedostosta vektorigrafiikaksi.)