Bilirdinizmi - Python kodunuzu daha sürətli işləməyiniz üçün necə? (1-ci hissə) ...

Sonuncu dərsdə sizi kodunuzun profilini müəyyənləşdirməyə kömək edə biləcək bir line_profiler ilə tanış etdik. İndi irəliyə addım atmağın vaxtı gəldi.

Bilirdinizmi serialının bu 4-cü dərsində Python-dan istifadə edərək çox yivli və çox işləmə yanaşmalarının həyata keçirilməsini öyrənəcəyik. Bu yanaşmalar əməliyyat sistemini sistem aparatlarından optimal istifadəni təmin edir və bu səbəbdən kodun icrasını səmərəli edir.

0.) Gözləmələr

Unutmayın ki, bu dərsliyin məqsədi çox yivli və çox işlənmənin istifadəsi və faydalarını vurğulamaqdır.

Çox yivli və çox işlənmədən başqa anlayışlara aid kod blokları üçün oxucunun onları tanıması və ya digər onlayn mənbələri oxuyaraq başa düşmələri gözlənilir.

1.) Çox iplik

Sitat gətirən Wiki - Kompüter arxitekturasında çox yivli işləmə mərkəzi emal bölməsinin (CPU) (və ya çox nüvəli prosessorda bir nüvənin) əməliyyat sistemi tərəfindən dəstəklənən eyni vaxtda birdən çox icrası təmin etmək qabiliyyətidir.

Paralel icrası nəzərə alınmaqla, birdən çox prosesin paralel icrasına başlamaq və kodun daha sürətli icrasına nail olmaq olar. Texniki təfərrüatlara girmədən (lazım olduqda GIL haqqında oxuyun), yadda saxlamaq lazım olan bir şey, çox işlənmə kimi daha yaxşı sinxronizasiya olunduğu şəkillər, fayllar və s. Yükləmə kimi I / O əsaslı tapşırıqları yerinə yetirərkən çox iplik daha səmərəlidir. CPU əsaslı tapşırıqlarla, yəni hesablama baxımından intensiv olan tapşırıqlarla.

a.) Pythonda çox iplik

"İplik", bir Python standart kitabxanası çox iplik çərçivəsini dəstəkləyir. Kitabxana standart Python quraşdırılması ilə standart olaraq gəlir və buna görə birbaşa kodumuzda idxal edilə bilər.

Çox ipliklənmənin effektivliyini nümayiş etdirmək üçün unsplash-dən 5 görüntü yükləyəcəyik. Bu şəkilləri ardıcıl olaraq yüklədiyimiz zaman icra müddətini müşahidə edək:

#### İstifadələrin kitabxanaya idxalı tələbləri
#### def down_img (ad, link) funksiyasının müəyyən edilməsi: data = request.get (link) .content name = f "/ home / isud / DidYouKnow / Tutorial 5 / {name} .jpg" açıq (ad, "wb") sənəd kimi: file.write (data)
#### ardıcıllıqla yüklənən 5 şəkil %% timeit -n1 -r1 şəkillər = ['https://images.unsplash.com/photo-1531458999205-f31f14fa217b', 'https://images.unsplash.com/photo-1488572749058 -7f52dd70e0fa ',' https://images.unsplash.com/photo-1531404610614-68f9e73e35db ',' https://images.unsplash.com/photo-1523489405193-3884f5ca475f ',' https://images.unsplash.com / şəkil-1565098735462-5db3412ac4cb '] üçün, enumerate (şəkillər )dəki link: down_img (i, link)
#### %% timeit nəticəsi, hər döngədə 51.4 s ± 0 ns (hər biri 1 döngədən, hər biri 1 döngədən ibarətdir)

Görüldüyü kimi, 5 görüntünün yüklənməsi 51.4 saniyə çəkdi, burada yeni bir yükləmə yalnız əvvəlki yükləmə başa çatdıqdan sonra başlayır. İndi çox yivli kod işini necə inkişaf etdirə biləcəyini müşahidə edək.

#### zəruri kitabxana idxalı yivli idxal tələbləri idxalı
#### def down_img (ad, link) funksiyasının müəyyən edilməsi: data = request.get (link) .content name = f "/ home / isud / DidYouKnow / Tutorial 5 / {name} .jpg" açıq (ad, "wb") sənəd kimi: file.write (data)
#### Paralel mövzularda yüklənən şəkillər %% timeit -n1 -r1 mövzuları = [] images = ['https://images.unsplash.com/photo-1531458999205-f31f14fa217b', 'https: //images.unsplash. com / photo-1488572749058-7f52dd70e0fa ',' https://images.unsplash.com/photo-1531404610614-68f9e73e35db ',' https://images.unsplash.com/photo-1523489405193-3884f5ca475f ',' https: // images.unsplash.com/photo-1565098735462-5db3412ac4cb '] i üçün, enumerate (şəkillərdə) üçün link: t = threading.Thread (target = down_img, args = (i, link)) t.start () threads.append ( t)
iplərdəki iplər üçün: thread.join ()
#### %% timeit nəticəsi, hər döngədə 25.6 s ± 0 ns (hər biri 1 dövrə, hər biri 1 döngə deməkdir)

Kod Açıklaması - Şəkil endirmə dairəsini müəyyənləşdirin:

  • Addım 1 (Mövzunun başlanması) - Python tam kodu bir iplə işlədir (əsas ip deyək). Bu nümunədə, iplik kitabxanasından Thread funksiyasını çağıraraq paralel mövzuları işə salırıq və yerinə yetiriləcək hədəf prosesi təyin edirik (bu vəziyyətdə down_image). Çağırılan funksiyanın tələb etdiyi bütün arqumentlər ardıcıllıq obyekti kimi ötürülməlidir (bu vəziyyətdə çoxlu). Thread funksiyasına edilən hər bir çağırış yeni bir mövzu açır (gəlin paralel iplər deyək).
  • Addım 2 (Mövzunun başlanğıcı) - Mövzunun başlanğıc metoduna zəng etmək Python'a iplərin icrasına başlamağı tapşıracaqdır. Diqqət yetirin ki, for loop əsas cərgədə yerinə yetirildiyi və funksiya çağırışları paralel bir cərgədədirsə, görüntü yüklənmə zamanı loopun icrası davam edir.
  • Addım 3 (Mövzu qoşulun) - Hər bir yeni mövzu iplər adlı bir siyahıda tutulur. Paralel iplər sonra birləşmə metodu çağıraraq əsas ipə birləşdirilir.

Niyə qoşulmaq lazımdır?

  • 2-ci pilləyə qədər bütün iplərimiz (əsas ip və paralel iplər) paralel olaraq icra olunurdu. Funksiya çağırışları paralel mövzularda edildiyini nəzərə alsaq, əsas iş funksiya zəngləri tamamlanmadan əvvəl çox yerinə yetirilə bilər. Bunun qarşısını almaq üçün paralel iplər əsas iplərə birləşdirilməlidir. Bu əsas ipin paralel iplər tamamlandıqdan sonra tamamlanmasını təmin edir. Bu 2 ssenari aşağıdakı diaqramda göstərilir:
Qoşulmadan və qoşulmaqla
  • İcra müddəti - Göründüyü kimi, şəkilləri yükləmək üçün icra müddəti 50% -ə yaxın azalıb (25.6 saniyə).

Yuxarıda göstərilən nümunə, çox ipliyin I / O əməliyyatlarında nə qədər faydalı ola biləcəyini və yükləmə / yükləmə proseslərinin səmərəliliyini artırdığını göstərir.

2.) Çox işləmə

Çox iplik bir prosesdə birdən çox mövzu yerinə yetirməyə imkan verdiyi kimi, çox işləmə də paralel icra üçün ayrı-ayrı proseslərə başlamışdır. Yuxarıda qeyd edildiyi kimi, çox işləmə, ağır hesablama tələb edən məsələlər üçün CPU intensiv işlərində əhəmiyyətli dərəcədə səmərəliliyi təmin edir.

a.) Pythonda çox işləmə

"Doldurma" kimi, "çox işləmə" də Pythonda çox işləmə xüsusiyyətini təmin etmək üçün istifadə olunan başqa bir kitabxanadır.

Çox işlənmənin faydalarını nümayiş etdirmək üçün hesablama baxımından tükənən və bir neçə dəfə çağırılması tələb olunan (paralel icra nümayiş etdirmək üçün) bir funksiyaya ehtiyacımız var. Bu, böyük dəyər diapazonunun kvadratını yaratmaqla (1-dən 10 mil-ə qədər rəqəmlərin kvadratı) əmələ gəlir və bu hərəkəti dəfələrlə etməliyik (deyək ki, 8 dəfə). Normal şəraitdə bu funksiyanın yerinə yetirilməsini müşahidə edək:

#### Zaman kitabxanasının idxal vaxtı
#### Def demo_func (num) funksiyasını müəyyənləşdirmək: i üçün (num): a = i ** 2
#### Demo funksiyasını ardıcıllıqla% (%) aralığında (8) i üçün demo_func (10000000)
#### %% vaxt nəticələri 21.2 s ± 0 ns başına (orta hesabla 1 qaçış, hər biri 1 döngə)

Demo funksiyasının 8 ardıcıl icrası üçün alınan vaxta (21.2 saniyə) diqqət yetirin. İndi bu çox işləmə qurğusunda edildiyi zaman performansın yüksəldilməsini yoxlayaq.

#### Zaman kitabxanasının idxal vaxtı
#### Def demo_func (num) funksiyasını müəyyənləşdirmək: i üçün (num): a = i ** 2
#### Çox işləmə demo funksiyası %% timeit -n1 -r1 proseslər = [] lop_size = [10000000,10000000,10000000,10000000,10000000,10000000,10000000, 10000000] p = multiprocessing.Pool () p.map ( demo_func, lop_size) p.close () p.join ()
#### %% nəticə, hər bir döngədə 11.6 s ± 0 ns (hər biri 1 dövrə, hər biri 1 döngə deməkdir)

İcra müddətinin düşməsini 50% -ə yaxın (11.6 saniyə) müşahidə edin. Bu necə oldu? Ardıcıl işləmə zamanı bir CPU nüvəsi bir anda istifadə olunur, halbuki çox işlənmədə bütün sistem nüvələri paralel olaraq istifadə olunur. Bunu aşağıdakı CPU istifadə ekran görüntülərindən müşahidə etmək olar

Ardıcıl və paralel icra

Yuxarıdakı qrafikdəki hər bir xətt bir CPU nüvəsini təmsil edir. Ardıcıl icrada, hər bir funksiya çağırışı üçün bir nüvənin necə tetiklandığına diqqət yetirin, burada paralel icrada olduğu kimi, bütün nüvələr də bir anda işə salınır.

Kod izahatı

  • Addım 1 (Hovuz yaradılması) - Hovuz metodu paralel olaraq tətbiq oluna bilən proseslər hovuzunu yaradır. Heç bir arqument olmadan, yaradılan proseslərin sayı sisteminizdəki CPU nüvələrinin sayına bərabərdir. Dörd nüvəli bir sistemim var (4 nüvəli), bu, 8 funksiyadan sonra ilk 4 zəng paralel olaraq sonrakı 4 çağırışdan sonra davam edəcəkdir. Diqqət yetirin ki, hovuzdakı proseslərin xüsusi sayını (nüvələrin sayından çox) müəyyənləşdirə bilərsiniz, ancaq bir nöqtədən sonra sistem yaddaşını yeyməyə başlayacaq və performansını aşağı sala bilər
  • Addım 2 (Hovuz xəritələşdirilməsi) - Buradakı proseslərinizə ona veriləcək arqumentlərin siyahısı (ikinci arqument) ilə birlikdə müəyyən bir funksiyanı (birinci arqument) icra etmək tapşırığı verilir.
  • Addım 3 (Hovuz bağlamaq) - Bağlama metodu Python tərcüməçisinə, hovuza təqdim etmək istədiyimiz hər şeyi təqdim etdiyimizi və gələcəkdə hovuza heç bir giriş verilməyəcəyini tapşırır.
  • Addım 4 (hovuza qoşulma) - Yivləmə vəziyyətində olduğu kimi, birləşdirmə metodu, paralel proseslərin tamamlandıqdan sonra kodun yerinə yetirilməsini təmin edir.

Yuxarıda göstərilən ssenaridən, çox işlənmənin səmərəsiz kod performansına qarşı necə mübarizə aparmaq üçün əla bir silah ola biləcəyini aydın şəkildə görə bildik.

Son qeyd

Bu təlimatda, sistem cihazlarımızın optimal istifadəsini təmin edərək kodun işlənməsini yaxşılaşdırmağa yönəltdik. Növbəti hissə kod işləməsini əhəmiyyətli dərəcədə yaxşılaşdıra biləcək xüsusi proqramlaşdırma təcrübələrinə yönəldiləcəkdir.

Ümid edirəm ki, bilirsinizmi serialının bu dərsliyi məlumatverici idi və yeni bir şey öyrəndiniz.

Gələcək dərslərdə daha maraqlı mövzuları sınayacaq və gətirəcəkdir. Buna qədər:

BAŞQA Öyrən! ! ! !