Progresul algoritmic târziu (partea 1, 4 aprilie)

La 3 aprilie 2019, când am început să aflu mai bine despre CUDA și puterea din plăcile video, mi-a căzut un văl de pe ochi și-a început să-mi pară rău că am investit în procesoarele clasice, mai degrabă decât în video (partea de video chiar am desconsiderat-o înadins, preferând ceva de tipul NVIDIA GT1030 single-slot cu 2 GB de GRRD5, pe principiul că „nu mă interesează jocurile, ci numerele”.

Ziceam că mai auzisem ceva de calcule numerice pe plăci video Tesla (în sâmbăta de 10 februarie 2018, de pildă, când îi spuneam cuiva despre povestea cu numerele), dar am crezut că este vorba numai despre ele și că ele sunt inaccesibile publicului larg (pe PCGarage, de pildă), folosibile prin cine știe ce laboratoare sau medii industriale. Asta era în mintea mea. Mai văzusem specificații de placă video unde apărea un număr... cam mare de procesoare, și NU era Tesla, ci ceva mai comercial - dar tot nu mi-a trăznit prin cap că acele procesoare ar fi bune și la numere, nu neapărat numai pentru grija imaginilor (și a jocurilor).

CUDA, care pentru plăcile video NVIDIA precum RTX 2080 TI este echivalentul OMP-ului procesoristic clasic în limbajul C, știe să separe în două mediile în care se scrie codul de compilat: calculatorul, cu RAM și procesor, este văzut ca „host”, mediul găzduitor al plăcii video, pe câtă vreme placa video în sine, cu procesoarele ei de CUDA, este văzută ca „device”, dispozitivul pentru ale cărui... procesoare se pune CUDA pe treabă. Am citit chiar, clar, că funcțiile GMP-ului NU pot fi folosite în interiorul video (device) al codului CUDA, tocmai pentru că sunt... funcții din mediul de „host” (cod legat de procesorul clasic al calculatorului), așa că trimiterea unui întreg „mpz_t” într-un bloc de cod-sursă paralelizator de CUDA, la placa video (1050, 2080...) ar însemna că se lasă cu eroare neînduplecabilă.

Este o cauză pentru care, prin anul 2012, japonezul Takatoshi Nakayama a încropit, pardon, a scris o sumă de funcții aritmetice derivate din GMP, special pentru CUDA, unite în biblioteca CUMP, dar setul acela este limitat la zona de numere reale cu virgulă (ar veni mpf_t în GMP), plus că atunci când am vrut să fac Makefile cu autoconf mi-a dat eroare, și mai departe eroare la tentativa de Make, așa încât NU am reușit s-o instalez ca să văd dacă pe o GDDR5 de 4 GB cu 768 de nuclee CUDA mpf_t-ul ar lucra mai repede decât mpz_t-ul pe un procesor obișnuit, fie el și Threadripper sau 7980XE.

Asta este cu adevărat o pacoste, când vrei o bibliotecă de funcții și erorile din codul pus la dispoziție te împiedică să lucrezi cu ea.
Și nici nu am statura programatorică morală să repar eu codul lui T.N.-san.

****************

În 4 aprilie, a patra zi din săptămână și-a patra din luna a patra din anul patru din jumătatea a patra de deceniu din secolul al patrulea de după cucerirea Transilvaniei de către habsburgii catolici, revelația asupra paralelizării cu CUDA pe video m-a angrenat să fac, deocamdată, cu resursele clasic-procesoristice existente, să fie ceva mai bine cu paralelizarea din OMP.

Altfel spus, provocarea video-CUDA-„plăcile video au tăria mare” m-a trezit oarecum la realitate, impulsionându-mă să paralelizez ceva mai mult cod de la numere pe partea de procesoare - anume la partea de sortare a conținutului fișierelor numerice.

În afara căutării și găsirii în sine de rezultate din care apoi să fie filtrate și culese deoparte numerele prețioase, durată mare de lucru mai este și la aranjarea conținuturilor numerice înnoite - sortarea strict crescătoare a marilor depozite (GIG-urile), sortarea fișierelor cu rezultate (NUM1), la fel, tot crescătoare, și sortarea fișierelor cu numere total noi, care nu erau în GIG-uri înainte (LPKP). Toate aceste sortări ocupau și ele destul de mult timp, și m-am gândit că s-or găsi algoritmi de sortare paralelă, ca să dureze mai puțin.

Așa că am cercetat și-am dat peste o variantă de QuickSort paralel cu OMP pe internet, iată aici (sau cum ar fi zis latinii, ecce hic):
https://github.com/eduardlopez/quicksort-parallel

Am prelucrat-o ca să facă sortare pentru unsigned long long, mpz_t și char * (ultimele două fiind variantele de tratare, ca tip de date, a numerelor la sortarea conținutului fișieristic).

Și a fost o reușită. Pentru mpz_t, cuplul funcțional de sortare este QPAR/QPARINT, iar pentru char-uri, SQPAR/SQPARINT (partea cu INT fiind cea cu conținut recursiv, unde apare și clauza de #pragma omp task).
Numărul de thread-uri de paralelizare este o constantă în /GMP64.h, zisă SF, de la sortare pe fire procesoristice.

Astfel, acum, cu unele excepții neînțelese pe la LPKP-urile de la „ERMETE”, sortările durează mai puțin, în funcție de cât de mare este acel SF față de 1 (și de cât de liber este procesorul implicat, ca să se ocupe toate thread-urile cerute; ar trebui să reușesc să iau măcar o placă video foarte puternică în 2019).

Contează și la ordonarea fișierelor cu rezultate, și la actualizarea magaziilor numerice (sunt și cazuri când nu actualizez GIG-urile, dar trag deoparte numerele noi în LPKP*-uri și apoi în LIT-uri sau LITT-uri, ca să verific mai repede noutățile, urmând apoi atenta actualizare a depozitelor și separarea noutăților numerice de tip LIT/LITT între ele, ca să nu se folosească de mai multe ori anumite grupuri de numere).

*LPKP-urile adună, sortate crescător, numerele noi, încă inexistente în GIG sau care abia intră acolo, și care au un anumit număr de cifre precis. De pildă un LIT111.LPKP are cele mai noi numere de 111 cifre care sunt strânse ca masă de manevră, ca o trâmbă de pământ excavat într-o mină, și în care pământ se pot găsi pietre prețioase (în cazul nostru, numere al căror numitor al fracției-raport dintre suma divizorilor și număr este maxim 10).
Un LIT120.LPKP, analog, cele mai noi numere de 120 de cifre găsite.

Dar depozitul mare, GIG-ul corespunzător, este GIG120.TXT și atunci cele zece fișiere .LPKP de la 111 la 120 trebuiesc unite într-unul mai mare, un LIT120.LIT (apoi LPKP-urile se șterg), iar următoarea căutare de numere se va face pe baza noutăților de 111-120 de cifre adunate în acest LIT120.LIT (poate să se cheme și LIT120.LITT sau LPT120.LIT, variațiuni de denumire).

******

Astfel, am reușit să mai îmbogățesc paralelizarea numerelor în OMP, punând sortările să fie mai rapide. Există și un măsurător de secunde ale duratei, la fiecare dintre cei doi algoritmi (QPAR la mepezetele, SQPAR la șiruri), bazat pe două variabile de tip double, start și stop, care apelează o funcție de timp din OMP: omp_get_wtime().
Și așa se vede și câte secunde (cu zecimale exacte de tip float!) ține o sortare pe stil nou.

De sortarea mpz_t se ocupă funcția LITUAN() din TOLIL.h, iar de cea pe char-uri, LITUAN2() (ibidem) (!).

Comentarii

Postări populare