19. helmikuu 2011, 18:30

Pythonin multiprocessing

Python tarjoaa monia työkaluja samanaikaiseen laskentaan. Vanhanaikainen thread-moduuli alkaa väistyä korkeamman abstraktiotason laitoksilta, joista threading on säieystävällisille systeemeille ja multiprocessing prosessien forkkaamisen kannalla oleville ympäristöille. Säikeitä enempi Windowsissa, omia lapsiprosesseja Unixeissa.

Mikä minua on kaivanut eniten, on kaiken tämän helppouden alla piilevät monimutkaisuudet ja erikoistapaukset. WxPython, tuo mainio käyttöliittymäkirjasto, tuntuu olevan jotenkin säierajoitteinen. En ole vieläkään löytänyt hyvää ja toimivaa ratkaisua saada samanaikaista laskentaa toimimaan siten, että GUI pysyisi menossa mukana.

Lyhyt demo, sillä en jaksa paremmin esitellä asiaa:

from multiprocessing import Pool

files = len(self.batch)
if files > 0:
  self.log.doLog("Preparing done! " + str(files) + " files ready to convert")
  pool = Pool(processes=2)
  results = pool.map_async(convert_file, self.batch)
  while not results.ready():
    wx.Yield()
    results.wait(0.3)

Siinä on nipsaistu vähän ohjelman keskeltä logiikkaa. Kerään ison läjän tietoja listaan, joka on onneksi atomaarista tietoa, jonka voi yksittäinen funktio helposti suorittaa. Termi taitaa kulkea nimellä “naurettavan helposti rinnakkaistettava”. Käyttämäni menetelmä on karvalakkitapa, joten en ihmettele yhtään, että se on melko rajoittunut.

Jos kutsun poolia metodilla map, se blokkaa GUI:n. Jos kutsun tuota asynkronista map_async-metodia sellaisenaan, se ei edes lähde käyntiin. Sen palauttama olio AsyncResult taitaa olla eräänlaista laiskaa laskentaa. Jouduin tyytymään em. while-silmukan kaltaiseen purkkaan, jolloin käyttöliittymä päivittyy 0.3 sekunnin välein.

Ainut tapa, jolla olen saanut GUI:n pyörimään jatkuvasti mukana, on ollut tehdä alkeellinen säie metodin suhteen. Mutta Pythonin säikeet eivät sattumoisin tue moniprosessointia toteutuksensa tähden. Siksi multiprocessing, joka käynnistää lisäprosesseja. Se olisi kai viimeinen homma, minkä voisin vielä tehdä: käynnistää erillinen säie käynnistämään nuo erilliset laskennat. En tarvitsisi map_asyncciä. Minua muuten vaivasivat mystiset bugit toteutuksen puolella, jonka takia hylkäsin säieratkaisun. Johtivat suoraan segfaulttiin, joten Pythonin puolelta minun oli aika paha mennä mitään tekemään.

Muuten tuo Pool.map on perin tyydyttävä ratkaisu, etenkin jos kirjoittaa komentoriviohjelmia. Helppo tapa tuottaa lista operaatioita ja sitten niille käsittelijäfunktio, ja sitten antaa kyseisen mapin tehdä hommansa. Muissa piireissä Pool.map tunnetaan nimellä pmap (Parallel Map). Jos ei tästä viestistä muuta saanut irti niin ainakin suosituksen kokeilla helpohkoa moniajoa, ellei GUI ole välttämätön.

Tageja: ,

---
---

---

Aiheen vierestä