Era evident de ceva vreme, așa că o spun în mod oficial: weblogs.studentclub.ro/ovidiupl se închide. Pentru probleme de serviciu, va apărea în curând un blog al echipei din care fac parte, CLR Code Generation team (adică JIT + NGen) pe blogs.msdn.com. Pentru chestiuni tehnice în general, ca și pentru alte halucinații, există http://ovidiupl.spaces.live.com, pe care puteți găsi deocamdată poze din cele mai recente excursii prin munți și, ocazional, fișiere partajate și alte asemenea; pe viitor e posibil să apară și lucruri mai concrete și mai utile.

N-am știut până acum, dar pe TechNet există un catalog destul de amplu numit Solution Accelerators, destinat administratorilor de sistem. Sunt acolo tot felul de ghiduri cu rețete pentru îndeplinit rapid tot felul de sarcini.

Acum câteva zile, a fost publicat Malware Removal Starter Kit. Starter kitul în sine este un simplu document Word, cu informații despre malware în general, despre cum trebuie să arate un plan de răspuns pentru urgențe, despre cum arată simptomele unei infecții cu malware șamd.

De departe, însă, partea cea mai interesantă a documentului este secțiunea despre pregătirea unui kit pentru scanare offline.

După cum bine știți (sau sper că știți), un sistem de operare infectat cu malware nu se poate curăța de unul singur. Motivul este simplu: Un rootkit bine scris care se încarcă în nucleul sistemului de operare se poate ascunde rezonabil de bine de orice scanner. În general, chiar dacă scannerele devin din ce în ce mai inteligente, nici rootkiturile nu stau pe loc, și are loc o cursă a înarmărilor, în care uneori scannerele câștigă, uneori nu.

În general, dacă reinstalarea sistemului de operare pe o maşină infectată nu este o opţiune, soluția este scanarea offline a sistemului infectat. Cu alte cuvinte, putem folosi o mașină curată pentru a scana hard-diskurile sistemului infectat, sau putem boota un alt sistem de operare pe aceeași mașină.

Una peste alta, Malware Removal Starter Kit conține instrucțiuni detaliate pentru crearea unui CD bootabil de Windows, pe care pot fi instalate utilitare de scanare pentru viruși și spyware, sau alte utilitare de uz general (printre exemplele menționate în documentație se numără avast! Virus Cleaner, McAfee AVERT Stinger, Microsoft Malicious Software Removal Tool și Spybot - Search & Destroy, dar teoretic pot fi instalate și altele).

În concluzie, dacă aveți nevoie din când în când să scanați câte o mașină suspectă, aveți acum la dispoziție un set simplu de scule. Instrucțiuni de folosire aveți la http://www.microsoft.com/technet/security/guidance/disasterrecovery/malware/default.mspx.

Technorati tags: ,

Ultima dată când am fost prin România, statisticile rutiere erau sinistre. Poate ar fi utilă și pe acolo o campanie de genul ăsta:

http://www.youtube.com/watch?v=5hWxU_ICoHM

(Dacă mai e nevoie de explicații: http://www.msnbc.msn.com/id/19569900/site/newsweek/)

Ieri am fost la o prezentare de securitate (un fel de quick refresher). Când am ajuns la secțiunea de SQL Injection, ne-a fost prezentat exemplul clasic de injection, care folosea " ' OR 2 > 1 -- ". La un moment dat, instructorul întreabă "de ce am folosit 2 > 1?".

"Pentru că expresia e întotdeauna adevărată și «orice» OR TRUE = TRUE etc"

"Bine, bine, dar de ce nu am folosit OR 1 = 1 așa cum vedeți în toate exemplele de pe net?"

"... ?!?"

"Pentru că în prezent, mai toate sistemele de intrusion detection caută în mod explicit în șirurile care le ajung pe mână după 1=1. Vă vine să credeți cât de imbecili pot fi?"

Nu-mi vine să cred, dar sunt sigur că e adevărat. Peticirea problemei acolo unde se manifestă în loc de rezolvarea problemei reale e un sport deosebit de util pentru securitatea jobului. Nu și pentru securitatea codului, dar cel puțin e genul de lucru care te ține ocupat cu săptămânile.

Technorati tags: , ,

Zilele astea am încercat să îmi aduc puţin la zi cunoştinţele despre securitatea aplicaţiilor. Unul din conceptele pe care am ajuns să-mi arunc ochii este funcţionalitatea MIC din Vista.

Încă de la prima versiune, securitatea din Windows NT s-a bazat în mare măsură pe Discretionary Access Control, care probabil este familiar multora - fiecare obiect din Windows are un ACL (Access Control List) alcătuit din mai multe intrări ACE (Access Control Entry).

Fără a intra în prea multe detalii despre DAC, una din limitările fundamentale ale modelului este că toate procesele aparţinând aceluiaşi utilizator au drepturi egale asupra obiectelor utilizatorului. Cu alte cuvinte, chiar dacă folosesc un Windows dintr-un cont de utilizator normal (fără nici un fel de privilegii suplimentare şi fără a aparţine de alt grup în afară de Users), toate aplicaţiile pe care le folosesc au acces egal la fişierele mele (chiar dacă ele nu pot afecta în nici un fel restul sistemului).

În Windows Vista, Mandatory Integrity Control oferă posibilitatea de a separa aplicaţiile şi fişierele folosite de acelaşi utilizator pe niveluri de integritate. Există mai multe modele teoretice care descriu acest gen de funcţionalitate. Două din cele mai simple sunt:

  • Modelul Bell-LaPadula (inventat în 1973 pentru Pentagon şi numit şi "modelul militar") rezolvă problema confidenţialităţii datelor, în sensul că fiecare "document" are un nivel de integritate (de exemplu "Top Secret", "Classified", "Unclassified" şi "Public"), iar fiecare entitate are la rândul său un nivel de acces.
    În modelul Bell-LaPadula, entităţile nu pot citi în sus (un angajat cu drept de acces la documente publice nu va putea citi documente "Top Secret") şi nu pot scrie în jos (un angajat "top secret" nu va crea documente pentru ochii publicului).
    Atenţie, documentul nu interzice entităţilor să citească în jos (un angajat "top secret" poate citi orice) şi să scrie în sus (un angajat cu acces la nivelul "classified" poate crea documente "top secret"... Dar ar fi bine să le scrie ca lumea din prima, pentru că nu va avea cum să le mai citească).
  • Modelul Biba (1977) se ocupă de integritatea datelor. În acest model, entităţile nu pot scrie în sus şi nu pot citi în jos. Cu alte cuvinte, dacă în modelul Bell-LaPadula, datele importante nu se puteau "scurge în jos", în modelul Biba, datele contaminate nu se pot propaga în sus. E bine de reţinut că în modelul Biba, entităţile pot citi în sus şi pot scrie în jos.

Implementarea din Windows Vista nu e nici una, nici alta. Mai exact, este cam jumătate din modelul Biba, jumătate din modelul Bell-LaPadula şi încă ceva, în sensul că există patru niveluri de integritate (Low, Medium, High şi System), iar obiectele sunt etichetate cu nivelul de integritate la care se află şi cu politica de acces la care vor fi supuse entităţile care doresc acces.

În versiunea curentă, cele trei politici disponibile sunt SYSTEM_MANDATORY_LABEL_NO_WRITE_UP (o entitate aflată la un nivel scăzut nu poate modifica obiecte aflate la un nivel mai înalt), SYSTEM_MANDATORY_LABEL_NO_READ_UP (o entitate nu poate citi obiecte aflate la un nivel mai înalt) şi SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP (o entitate nu poate executa cod aflat la un nivel mai înalt). Etichetarea se face cu ajutorul funcţiei AddMandatoryAce, iar dacă un obiect nu are o etichetă, se consideră implicit că se află la nivelul Medium.

La logon, utilizatorul primeşte la rândul lui o etichetă (Medium), iar toate aplicaţiile pe care le porneşte rulează implicit la acest nivel. Când UAC este activat, procesele elevate vor rula la nivelul High, iar Internet Explorer va fi Low (atunci când opţiunea de a rula în "Protected Mode" este activă în IE; atât UAC, cât şi Internet Explorer Protected Mode pot fi dezactivate). Serviciile critice sistemului de operare rulează ca System.

Nu am idee care au fost constrângerile care au condus la acest design, dar dacă ar fi să speculez puţin, aş spune că a contat foarte mult uşurinţa în utilizare a sistemului. Evident, aplicarea ambelor modele de care am discutat fără a face sistemul practic inutilizabil este imposibilă (dacă nici o entitate nu poate scrie/citi, nici în sus, nici în jos, ajungem practic de unde am plecat, sau mai rău).

Alegerea unei singure metode ar fi însemnat că utilizatorii ar fi beneficiat fie doar de integritatea datelor, fie doar de confidenţialitatea lor, dar nu de ambele facilităţi. Şi aşa, hibridul aparent ciudat din Windows Vista capătă sens, pentru că modelul oferă posibilitatea de a proteja date cu nivel ridicat de integritate în faţa operaţiilor neautorizate de scriere/citire/execuţie din partea entităţilor cu nivel mai scăzut.

Sigur, modelul conţine şi un element de risc, în sensul că operaţiile de citire "în jos" nu sunt împiedicate în nici un fel (cel puţin din câte mi-am dat eu seama de prin documentaţie). Probabil că totuşi este mai bine aşa, pentru că ar fi fost cel puţin ciudat (din punctul de vedere al unui utilizator) ca o aplicaţie rulând ca Medium să nu poată citi un document descărcat de pe internet şi salvat cu eticheta Low.

Important este însă că o aplicaţie aflată la un nivel scăzut nu poate ajunge la obiecte aflate mai sus pe cont propriu. E nevoie de o altă aplicaţie (lansată explicit de utilizator sau de altă componentă din sistem) care să preia date "de jos" pentru ca acele date să fie procesate la un nivel mai ridicat.

Privit în izolare, MIC nu e nici pe departe extraordinar ca măsură de securitate. La o privire de ansamblu, însă, integrarea cu UAC, cu interfaţa vizuală (prin User Interface Privilege Isolation), plus bateria de măsuri defensive care înconjoară aplicaţiile fac din Windows Vista un sistem mult mai robust în comparaţie cu versiunile anterioare.

Technorati tags: ,

Dacă nu ştiaţi, în SUA este în plină desfăşurare sezonul taxelor. An de an, contribuabilul american munceşte cu asiduitate la adunări şi scăderi, deduceri şi procente, termenul limită pentru depunerea declaraţiei de venit fiind de obicei 15 aprilie (anul ăsta cred că e 16 aprilie, 15 pică într-o duminică).

Pe parcursul acestui proces complex, Publicaţia 17 (HTML, PDF) reprezintă o deosebit de utilă lumină călăuzitoare. Extrem de interesant în această publicaţie este capitolul 12, "Alte venituri". La pagina 85, aflăm următoarele:

Bribes. If you receive a bribe, include it in your income.

Până aici, nimic neobişnuit. Oamenii guvernului nu sunt spectaculos de inteligenţi nicăieri în lume, nu m-ar surprinde să-şi pună veniturile colaterale pe declaraţia de venit. Lucrurile devin interesante câteva paragrafe mai jos, la pagina 87:

Illegal income. Illegal income, such as money from dealing illegal drugs, must be included in your income on Form 1040, line 21, or on Schedule C or Schedule C-EZ (Form 1040) if from your self-employment activity.

Dacă n-aţi înţeles, vă explic eu. Dacă vindeţi dulciuri la colţul străzii "pe persoană fizică", trebuie să declaraţi veniturile pe linia 21 a formularului 1040. Dacă prestaţi această activitate ca liber profesionist, trebuie să ataşaţi şi Schedule C sau Schedule C-EZ şi să declaraţi acolo veniturile. Şi asta nu e tot. La pagina 88:

Stolen property. If you steal property, you must report its fair market value in your income in the year you steal it unless in the same year, you return it to its rightful owner.

Cu alte cuvinte, dacă din greşeală aţi rămas pe-acasă cu un laptop (sau zece) pe care aţi uitat să-l înapoiaţi proprietarului până la sfârşitul lui 2006, va trebui să plătiţi impozit la valoarea curentă de piaţă a bunului respectiv.

(Dacă nu vreţi să navigaţi prin PDF, citatele de mai sus se găsesc şi în versiunea HTML, sunt doar mai greu de găsit.)

Dacă se întâmplă vreodată să lucrăm la același proiect, trebuie să știți că există două reguli simple care mă fac foarte fericit, atâta vreme cât sunt respectate de toată lumea:

1. "Single point of truth" (aka "Don't repeat yourself")

Regula asta spune că orice entitate trebuie definită o singură dată într-un proiect. Denumirea de "single point of truth" am găsit-o în "The Art of Unix Programming" de Eric Raymond și am reținut-o pentru că denotă perfect efectul pe care îl are: Când ai ceva de modificat, trebuie să mergi într-un singur loc, nu să orbecăi prin tot felul de locații obscure.

Mai este cunoscută și sub numele de "nu introduceți constante magice în cod", dar exprimarea asta nu e suficient de generală. Am văzut și bucăți de cod în care aceeași constantă este definită în mai multe locuri. E destul de enervant descoperi că într-un modul PI are aceeași semnificație ca în altul, dar 4 zecimale în plus. În plus, regula se aplică și pentru alte lucruri în afară de constante.

2. În source control se țin numai fișiere sursă*

Dintr-un motiv sau altul, ați putea fi tentați să introduceți în source control binare generate de către proiect. Nu faceți așa ceva, deoarece încălcați regula #1 - din moment ce puteți obține sculele respective prin compilare, nu are nici un rost să vă repetați.

Dacă faceți așa ceva deoarece procesul de build depinde de prezența sculelor respective, aveți o problemă de bootstrapping (aka "oul și găina"). Încercați să rezolvați problema originală și nu să o mascați cu soluții improvizate.

Dacă faceți așa ceva deoarece aveți extrem de multe surse, din care un număr foarte mic se modifică de la un build la altul, problema se află tot în procesul de build. Găsiți o modalitate eficientă de a compila numai fișierele modificate atunci când există un set anterior de binare la îndemână. Aveți grijă să nu introduceți buguri legate de interacțiunea dintre binare cu versiuni ușor diferite.

*Există o excepție notabilă. Țineți în source control toate componentele externe folosite în procesul de build (SDK, compilator, scule auxiliare gen interpretorul de Perl și Python etc). Dacă în viitor doriți să reconstituiți de la zero o anumită versiune a proiectului, veți avea nevoie de exact același compilator, biblioteci etc, pentru a obține exact același rezultat. De asemenea e recomandat să vă folosiți inteligența când decideți ce conține toolset-ul necesar pentru build. Probabil că nu vreți să faceți check-in la o instalare completă de Windows și Visual Studio.

Pe lângă cele spuse mai sus, ar mai fi câteva reguli auxiliare, dar nu are rost să mă lungesc cu vorba. Ar mai trebui să precizez că regulile sunt necesare, nu și suficiente pentru a mă face fericit.

N-am idee dacă ați băgat în seamă anunțul din 8 ianuarie 2007 despre Windows Home Server. Dacă nu știți despre ce e vorba, Windows Home Server e o variantă consumer-friendly de Windows Server, destinată utilizatorilor care au mai multe calculatoare acasă.

Eu am numai două calculatoare, un desktop și un notebook, dar îmi propun de vreo jumătate de an să-mi organizez cumva pozele, muzica și documentele în așa fel încât să-mi fie ușor accesibile de pe ambele mașini (eventual fără să fie pornite amândouă în același timp), și eventual să am și o schemă oarecare de backup, și cu toate astea nu am ajuns până acum la nici un rezultat concludent (dar, trebuie să recunosc, încă nu am încercat nici măcar lucrurile simple gen Offline Files din Windows XP sau o sculă de replicare peer-to-peer gen Groove).

Cea mai mare problemă e că periodic mai apare câte un set de poze și când mă apuc să le descarc, ajung pe undeva, după care când le organizez trebuie să țin cont care sunt deja pe CD sau DVD și care nu, care sunt duplicate pe ambele calculatoare și care sunt unicat. Cealaltă problemă e că Windows Search îmi e foarte util, dar pe mine personal mă deranjează să văd tot felul de aplicații consumând timp de procesor, și mai ales operații de intrare/ieșire fără să fac nimic la calculator (oarecum ironic, ținând cont că la serviciu una din componentele mele este serviciul NGen din .NET Framework, care face același lucru).

Aveam de mai multă vreme pe wish-list construirea unei mașini pe care să-mi stea toate documentele, pe care să ruleze tot timpul Windows Search, și care să aibă suficient spațiu încât să nu mai am nevoie vreodată să fac backup pe DVD (și ceva redundanță în așa fel încât să nu pierd toate datele o dată cu primul hard-disk care se decide să mă părăsească pentru o lume mai bună). Din câte am văzut, aș putea crea un RAID de mai mult de 1 TB la un preț destul de rezonabil. 1 TB reprezintă de vreo 10 ori mai multe date decât am scris până acum pe CD-uri și DVD-uri de backup.

Ca să nu mai lungesc povestea, am decis să amân pasul acesta pentru o vreme când am auzit că pe undeva prin firmă, o echipă pregătește un server de genul ăsta. Din câte știu, planul pentru versiunea finală arată cam așa: Un utilizator care are mai multe calculatoare pe acasă cumpără un sistem cu Windows Home Server preinstalat. Acest sistem nu este PC-ul tradițional cu Windows, ci un server care poate fi ascuns în debara și de care poți uita complet după ce îl configurezi (nu necesită monitor, tastatură, mouse, doar electricitate, aer și conexiune la rețea). Pe celelalte calculatoare din casă trebuie instalat clientul de Home Server, care știe ce directoare au o copie master pe server și care se ocupă zilnic de backup, replicare și rezoluția conflictelor.

Mai mult, una din funcțiile anunțate de echipă mi-a depășit așteptările - există posibilitatea de a subscrie la un serviciu online care permite accesarea securizată a mașinii de oriunde de pe internet - nu mai e nevoie să plimb memory stick-uri sau CD-RW-uri după mine, sau să-mi trimit emailuri de acasă la serviciu și invers! De alte dorințe de-ale mele (Windows Search integrat pe server, sau posibilitatea de a descărca patch-urile de pe Windows Update o singură dată ca să le instalez pe toate calculatoarele după aceea) n-am auzit nimic încă, dar sunt convins că mai devreme sau mai târziu se vor gândi și la mine. În schimb, o altă caracteristică a sistemului (care depinde de hardware-ul utilizat, ce-i drept) este că e suficientă introducerea unui hard-disk nou și sistemul îl detectează automat și începe să-l folosească pentru stocare (fără reboot, fără alte eforturi de configurare).

Dacă ați rezistat până aici, urmează și vestea bună: Dacă mergeți la http://connect.microsoft.com/WindowsHomeServer și răspundeți la chestionarul de acolo, aveți șansa de a participa la programul beta 2 pentru Windows Home Server. Completarea chestionarului nu garantează că veți primi beta-ul, dar sunt convins că echipa are nevoie de câți mai mulți utilizatori care să chinuiască produsul în configurații cât mai diverse. Dacă aveți un calculator liber pe-acasă, vreți să îl transformați în server de fișiere și să vă jucați cu Home Server, completați chestionarul cât mai curând!

Există exact două moduri de a scrie programe: bine sau prost. Ați putea fi tentați să credeți că există o varietate de nuanțe între cele două extreme, dar adevărul e că lucrurile nu stau deloc așa.

Cum anume arată codul bun nu are rost să discutăm. "Toată lumea știe că" e important să fie clar, bine comentat, consistent, modularizat, și că analiza cerințelor, alegerea unei arhitecturi, crearea unui model de securitate, testarea funcțională și evaluarea performanțelor sunt doar câțiva din pașii esențiali pentru crearea unei aplicații de calitate.

Am observat însă că în orice proiect există un indicator foarte simplu al calității proiectului pe ansamblu: Istoricul check-in-urilor din sistemul de source control. Serios, asta e tot. Dacă aveți la îndemână un proiect la care se lucrează de mai mult de 1-2 ani, aruncați o privire prin modificările făcute în surse. Este un exercițiu foarte interesant, care oferă posibilitatea de a înțelege "de ce"-ul din spatele multor decizii luate de-a lungul timpului.

De exemplu, dacă introducerea unei funcționalități noi se face "chirurgical", în sensul că există o serie de fișiere, clase și metode noi, dar modificările aduse codului existent sunt minime, înseamnă că trebuie să mulțumiți autorilor designului inițial pentru că au creat un produs modular și ușor extensibil (și astfel de produse există, nu sunt doar un mit).

În schimb, dacă un check-in atinge zeci de fișiere, dar funcționalitatea nouă e ascunsă doar în două-trei, ați dat de belea: Aveți de-a face cu un sistem strâns integrat, care trebuie testat ca un întreg pentru a verifica dacă piesele individuale lucrează cum trebuie. De obicei, complexitatea testării și întreținerii codului crește exponențial cu introducerea fiecărei funcționalități noi (probabil că nu trebuie să vă amintesc că și aceste produse sunt reale).

(Dacă ar fi să mă exprim abstract, aș spune că un sistem integrat este ca un spațiu vectorial în care lucrând de-a lungul axei Rn+1 trebuie să ții cont de toate combinațiile dintre funcționalitățile existente pe primele n axe, pe când într-un sistem modularizat, există o oarecare independență. Dar nu o să mă exprim abstract, mai ales că analogia nu e ideală).

Mai pot fi trase diverse concluzii și din frecvența cu care este modificat un fișier, atât în perioada de dezvoltare, cât și în perioada de întreținere a componentei pe care o găzduiește. Dacă este modificat foarte rar, fie este scris extrem de bine, fie este inutil. Dacă este modificat foarte des, e interesant de văzut care e amplitudinea modificărilor și care e cauza lor - dacă e prost scris și necesită multă atenție ca să rămână în stare de funcționare, dacă a devenit prea mare și trebuie segregat într-un fel sau altul pentru că modificările se fac de fapt în componente distincte sau dacă e altceva în neregulă.

Răsfoind check-in-urile altora, am învățat să fiu puțin mai critic cu ale mele. Secretul din spatele multor proiecte este să menții complexitatea la minim, ori complexitatea nu crește în mod detectabil de la o zi la alta, ci urcă pe o pantă lină. Nu e ca și cum vineri seara pleci acasă după ce ai lucrat la un proiect "simplu" și luni dimineața codul e dintr-o dată de neînțeles. Din cauza asta, fiecare check-in trebuie să adauge sau să modifice atât cât trebuie și nimic mai mult.

Într-un comentariu primit zilele trecute, un cititor se plângea de caracterele pe care le folosesc pe weblog, mai exact de 'ș' și 'ț'. S-ar putea să știți că în versiunile anterioare de Windows, cele două litere erau reprezentate folosind caractere împrumutate din setul Latin2 (ISO-8859-2), mai exact U+015E (LATIN CAPITAL LETTER S WITH CEDILLA), U+015F (LATIN SMALL LETTER S WITH CEDILLA), U+0162 (LATIN CAPITAL LETTER T WITH CEDILLA) și U+0163 (LATIN SMALL LETTER T WITH CEDILLA), cunoscute și sub numele de "ș și ț cu sedilă". Pe siteul consorțiului Unicode găsiți aceste caractere în secțiunea Latin Extended-A.

Din câte știu, aceste caractere au fost preluate din setul folosit pentru limba turcă deoarece la vremea respectivă nu exista un standard pentru limba română (vorbim aici de mijlocul anilor '90). După cum v-ați dat seama deja, caracterele cu pricina sunt compuse din 's' și 't' și o sedilă atașată sub ele.

Problema este că în limba română, cele două caractere sunt compuse de fapt prin atașarea unei virgule și au fost introduse ceva mai târziu în standardele din domeniu (ISO-8859-16, care conține așa-numitul set Latin10). Cu alte cuvinte, caracterele corecte pentru limba română sunt U+0218 (LATIN CAPITAL LETTER S WITH COMMA BELOW), U+0219 (LATIN SMALL LETTER S WITH COMMA BELOW), U+021A (LATIN CAPITAL LETTER T WITH COMMA BELOW) și U+021B (LATIN SMALL LETTER T WITH COMMA BELOW), pe care le puteți găsi în secțiunea Latin Extended-B, paragraful "Additions for Romanian".

Pentru a nu lungi prea mult povestea, Windows Vista conține suport complet pentru aceste caractere, cu tot cu un layout nou pentru tastatură (pe care îl folosesc și pentru a scrie acest articol). Am aflat astăzi o veste bună de pe pagina lui Michael Kaplan, developer în grupul de internaționalizare de la Microsoft: Tocmai a fost lansat pe Microsoft Downloads un pachet pentru Windows XP numit European Union Expansion Font Update, care actualizează fonturile Times New Roman și Arial pentru a permite afișarea corectă a celor patru caractere noi.

Vestea mai puțin bună este că introducerea acestor caractere de la tastatură este mai dificilă în Windows XP, deoarece layout-ul implicit nu știe de ele. Totuși, există posibilitatea de a crea layout-uri particularizate de tastatură folosind Microsoft Keyboard Layout Creator, așa că există speranțe și pentru cei care vor să adopte noile caractere pe Windows XP. Oricum, cel puțin problema vizualizării este parțial rezolvată.

În loc de încheiere, mă gândeam că poate comunitatea românească de IT ar trebui să-i mulțumească și lui Cristian Secară, unul din primii care au remarcat inconsistența implementării diacriticelor în Windows și care a susținut în mod activ cauza adoptării și implementării standardelor pentru limba română. Vorbeam zilele trecute cu Tudor despre diverse subiecte și printre altele, îmi povestea că a fost foarte impresionat de gradul în care s-a implicat și de pasiunea pe care a arătat-o Cristian Secară în a urmări soluționarea acestei probleme.

Mai rămân de convertit de la formatul vechi documentele și bazele de date și am rezolvat-o și pe asta... Că tot veni vorba, pe Windows Vista am avut o vreme și layout-ul Romanian (Standard), și pe cel anterior, Romanian (Legacy). Am renunțat foarte repede la varianta veche, deoarece m-am obișnuit ușor cu cea nouă și pentru că trecerea de la engleză la română o fac cu Alt+Shift, dar trecerea de la un layout la altul în română se face cu Ctrl+Shift.

Cum folosesc foarte mult tastatura, și Ctrl+Shift (+ săgeată) e folosit și pentru a selecta blocuri de text (cuvânt cu cuvânt), ajunsesem să comut de la Legacy la Standard și invers de câteva ori pe document, fără să-mi dau seama. Dacă sunteți foarte atenți, o să remarcați asta în câteva articole de acum câteva săptămâni, care conțin și caracterele cu sedilă, și pe cele cu virgulă. Asta este, durerile tranziției...

Zilele trecute, cineva a pus o întrebare pe una din listele interne de discuții dedicate .NET Framework: În afară de Double.NaN, Double.PositiveInfinity (șamd), mai există și alte numere speciale în .NET?

Evident, au urmat tradiționalele "Ce anume vrei să faci de fapt?" și "Numărul Pi este special, sau nu se pune?", dar răspunsul de azi dimineață le-a făcut praf pe toate cele dinainte:

Teoremă: Orice număr este special.

Demonstrație: În mod evident, 0 este special, fiind primul număr natural. 1 este și el special, fiind elementul identitate pentru înmulțire. 2 este singurul număr prim și par. 3 este cel mai mic număr prim și impar.

Se poate spune că există multe numere speciale. Să presupunem că mulțimea numerelor care nu sunt speciale este nevidă. Atunci, există cel mai mic număr natural care nu este special.

Dar, în acest caz, și acest număr e destul de special, nu?

Tocmai am demonstrat că dacă mulțimea numerelor care nu sunt speciale este nevidă, ea va conține cel puțin un număr special. Acest lucru este absurd, deci ipoteza de la care am pornit este greșită.

În concluzie, toate numerele sunt speciale. Extinderea demonstrației pentru numere reale rămâne cititorului ca exercițiu.

Am încheiat articolul de data trecută cu o întrebare: Ce este mai avantajos pentru un utilizator, să aibă o conexiune de 1 Gbps cu latența de 1 s, sau o conexiune de 10 Mbps cu latența de 1 ms?

Depinde. În primul caz, pentru fiecare cerere utilizatorul va aștepta 2 secunde până să primească primul bit al răspunsului, dar răspunsul propriu-zis va sosi foarte repede. Dacă fiecare cerere are în medie 100 KB, rezultă că în medie utilizatorul va aștepta 2.0001024 s pentru fiecare pagină web. Dacă, în schimb, fiecare cerere are 100 MB, timpul mediu de așteptare (până la ultimul bit) va fi de 2.1048576 s. Cu alte cuvinte, conexiunea este foarte avantajoasă pentru transferul cantităților mari de date și atunci când decalajul de 2 secunde nu este important.

În al doilea caz, la cereri de 100 KB, răspunsul mediu va dura 0.01124 s. În schimb, la cereri de 100 MB, răspunsul mediu va avea 10.48676 s. Cu alte cuvinte, tipul acesta de conexiune excelează la interactivitate pentru transferuri mici și medii, dar nu se comportă bine la volume mari de date. Evident, ambele exemple sunt prezentate exclusiv din punctul de vedere (pur teoretic) al clientului și nu țin cont de o mulțime de alți factori din lumea reală.

Generalizând, ne interesează de fapt răspunsul la două întrebări: Cum alegem scenariile pe care le testăm, și cum alegem parametrii care influențează cel mai semnificativ acele scenarii?

La prima întrebare, răspunsul este mai delicat decât v-ați aștepta: Teoretic, scenariile esențiale din punctul de vedere al performanțelor sunt la alegerea clientului. Sigur, toată lumea își dorește aplicații rapide și care să ocupe puțină memorie, dar acest obiectiv are un mare defect - nu este măsurabil. Soluția este să includeți în specificațiile aplicației obiective de performanță concrete, specifice și măsurabile.

Acest lucru e valabil indiferent că dezvoltați o soluție sub contract, pentru un client, caz în care parametrii de performanță doriți sunt o parte a contractului, sau o soluție pentru publicul larg, caz în care este responsabilitatea voastră să determinați cerințele de performanță și să le tratați la fel ca pe toate celelalte cerințe ale aplicației.

Alegerea scenariilor propriu-zise este unul din domeniile care țin mai mult de arta programării (vizavi de știința programării), deoarece implică înțelegerea percepțiilor și așteptărilor utilizatorilor. Nu există o rețetă universală pentru rezolvarea acestei probleme.

În lumea reală, de cele mai multe ori clienții articulează cu greu scenarii concrete de performanță și dorințele lor în legătură cu acestea. Probabil că singura strategie corectă este să vă folosiți intuiția și să alegeți câteva situații pe care le considerați importante, să optimizați scenariile alese, și să includeți în aplicație instrumentație și mecanisme de feedback cât mai vizibile și mai ușor de folosit. În acest fel, dacă utilizatorii sunt nemulțumiți și se dovedește că deciziile luate au fost incorecte, puteți adapta rapid aplicația la cerințele reale ale clienților.

La a doua întrebare, răspunsul este similar: În cazul în care după o sesiune de profiling observați o problemă de performanță, probabil că vă veți pune întrebări în legătură cu natura problemei și veți ajunge să decideți care parametri merită luați în considerare. Ca regulă generală, orice operație care necesită implicarea altor componente decât procesorul și memoria sistemului poate dura oricât.

Mult mai grav este dacă după toate sesiunile de lucru nu observați nici un fel de probleme (ce se întâmplă dacă toate testele sunt rulate într-o rețea de 1 Gbps și cu latență de 1 ms?). Teoretic, dacă înțelegeți bine arhitectura aplicației, sunt mari șanse să vă dați seama de la bun început care sunt elementele cu potențialul cel mai mare de a ridica probleme de performanță. Un alt factor care poate fi de mare ajutor sunt colegii mai experimentați de echipă (greșelile trecutului sunt în general un indicator foarte bun al greșelilor care vor fi evitate pe viitor; evident, există și excepții de la această regulă).

În paranteză fie spus, am avut ocazia să întâlnesc programatori altminteri capabili, dar care au rămas complet siderați când un consultant mult mai trecut prin arhitecturi distribuite le-a explicat că o aplicație bazată pe .NET Remoting, care face enorm de multe apeluri în rețea pentru fiecare operație se va comporta excelent în mediul lor de test (rețea LAN de 100 Mbps), dar nu are nici o șansă să supraviețuiască în viața reală: clientul lor dorea să utilizeze aplicația peste linii ISDN de 128 kbps, și atât latența, cât și lățimea de bandă făceau și cele mai simple funcții să se execute cu viteza melcului. Nu cred că vreți să descoperiți probleme de acest gen când proiectul e încheiat.

Dacă nu aveți la dispoziție colegi super-experimentați și nu cunoașteți fiecare colțișor al aplicației implementate și al platformei de care depinde, ultima soluție este forța brută: Testați pe cât mai multe combinații de platforme hardware și software. Folosiți cele mai puternice combinații pe care le aveți la dispoziție pentru a rezolva problemele cele mai presante, după care treceți la configurații cât mai apropiate de ale utilizatorilor și vedeți ce probleme mai ies la iveală. E valabil atât pentru aplicații client, cât și pentru servere! Nu vreți ca aplicația să meargă perfect pe serverul de test, cu o bază de date de 10 utilizatori, dar să se târască pe serverul de producție care poate fi de 100 de ori mai rapid, dar suportă 100 de milioane de utilizatori.

În articolul inițial despre profiling au rămas câteva chestiuni nelămurite, asupra cărora aș vrea să revin cu mai multe detalii.

Astăzi discutăm puțin despre o anomalie observată în rezultatele măsurării din articol. Din ce cauză salvarea imaginii nou create durează 515 ms în unul din teste și 57 ms în al doilea? Răspunsul este foarte simplu, dar trebuie avut permanent în vedere atunci când vine vorba de evaluarea performanțelor: Toate măsurările sunt afectate de zgomot.

Este foarte posibil ca în prima situație, aplicația să fi așteptat până când datele au fost scrise efectiv pe disc, deoarece sistemul de operare nu avea suficiente pagini disponibile în cache-ul managerului de memorie, pe când în a doua situație este destul de evident că rezultatele salvării nu s-au dus direct pe disc: imaginile generate au puțin peste 9 MB, iar această cantitate de date scrisă în 57 ms ar implica o viteză de transfer la scriere de aproximativ 158 MB/s. Întrucât nici măcar interfața discului nu suportă viteze de genul acesta (am rulat testul pe un disc Ultra DMA), este clar că la mijloc se află unul sau mai multe niveluri de caching.

Soluția pentru eliminarea zgomotului este simplă: Efectuați cât mai multe teste, în limita timpului disponibil, și luați-le în considerare pe cele mai relevante din punct de vedere statistic (de exemplu, eliminând din listă rezultatele "anormale", până când abaterea standard devine rezonabil de mică). De cele mai multe ori, anomaliile vor ieși foarte ușor în evidență chiar și vizual (de exemplu într-un grafic unidimensional, în care rezultatul fiecărei măsurări este un punct).

Sigur, mai este nevoie de încă o clarificare: Care este definiția normalității? În exemplul de mai sus, trebuie să ne așteptăm ca datele să poată fi scrise de fiecare dată în cache, extrem de rapid, sau dimpotrivă, este de așteptat ca în majoritatea cazurilor memoria sistemului să fie insuficientă astfel încât aplicația va fi nevoită să aștepte ca fiecare scriere să aibă loc complet?

Evident, nu există un răspuns universal: Dacă aplicația încearcă să scrie 10 MB de date pe disc, timpii de execuție vor varia semnificativ în funcție de viteza discului și de cantitatea de memorie disponibilă sistemului de operare. De exemplu, este destul de plauzibil că pe o mașină cu 64-128 MB de RAM, aceste date nu vor sta foarte mult în cache, deoarece 10 MB reprezintă un procent însemnat din memoria sistemului, care este probabil necesară pentru execuția normală a aplicațiilor și a codului sistemului de operare; în acest caz, viteza discului va fi o variabilă importantă.

Pe de altă parte, pe o mașină cu 1-2 GB de RAM care rulează rezonabil de puține aplicații (să zicem că sunt ocupați maxim 150 MB de memorie), șansele sunt foarte mari ca datele respective să fie transferate foarte rapid într-o memorie tampon a sistemului de operare, de unde vor fi scrise mult mai târziu pe disc (asincron, din punctul de vedere al aplicației). În acest caz, viteza propriu-zisă a discului nici nu mai contează.

În practică, pentru evaluarea influenței factorilor externi, se încearcă de obicei să se măsoare comportamentul aplicației în situațiile extreme, în așa fel încât comportamentul aplicației să fie cât mai bine înțeles pe un set de configurații hardware. De exemplu, la măsurarea vitezei operațiilor cu discul, se practică măsurarea "la rece", când cache-ul sistemului de operare nu conține nici un fel de date (și eventual este suficient de mic încât să forțeze ca un număr mare de operații de scriere să ajungă direct până la disc) și "la cald", când cache-ul sistemului de operare conține deja o mare parte din datele necesare aplicației și este suficient de mare încât majoritatea scrierilor să se facă asincron.

Similar, pentru aplicațiile care comunică în rețea se fac în mod obișnuit teste pe conexiuni cu latență variată și cu lățime de bandă variată. Ce anume trebuie testat depinde de natura aplicației - de exemplu pentru un navigator de web, latența contează într-o oarecare măsură, deoarece ea dictează cât are de așteptat aplicația până să primească chiar și primul bit venit de pe un server web, dar lățimea de bandă este cel puţin la fel de importantă, deoarece determină timpul de aşteptare dintre primul și ultimul bit. Ne putem imagina cu uşurinţă tot felul de situaţii extreme generate de combinaţiile posibile de latenţă şi viteză: De exemplu, este mai avantajoasă o conexiune de 1 Gbps cu latență de 1 s sau o conexiune de 10 Mbps cu latența de 1 ms? Răspunsul în episodul viitor (dar puteți înșira speculații în secțiunea de comentarii).

La rugămintea lui Mihai Alexe, doctorand la Virginia Tech Department of Computer Science, postez un anunţ al prof. Adrian Sandu, din cadrul aceluiaşi departament:

Cu respect doresc să vă informez de posibilitatea de a urma studii de doctorat în Computer Science la Virginia Tech. Burse de studii (în diferite specializări în cadrul Computer Science) sunt disponibile pentru aplicanţii calificaţi.

Virginia Tech oferă un mediu excepţional pentru studii de doctorat şi pentru cercetare avansată în mai multe domenii din Computer Science. Vă scriu în special despre domeniul în care îmi desfăşor eu activitatea - "Computational Science and Engineering" (metode de calcul numeric, parallel, şi aplicaţii). Burse speciale de cercetare sunt disponibile pentru absolvenţi de diplomă sau de masterat cu calităţi deosebite, interesaţi să urmeze doctoratul în acest domeniu.

Departamentul de Computer Science (http://www.cs.vt.edu) are un grup puternic de cercetare în Computational Science. Domeniile de cercetare cuprind calcul masiv-parallel, algoritmi numerici de algebră liniară, optimizare, metode de rezolvare numerică a ecuaţiilor diferenţiale ordinare şi cu derivate parţiale, etc, cu diverse domenii de aplicaţie.

Virginia Tech găzduieşte supercomputerul System X (http://www.tcf.vt.edu) care, la 12.5 TeraFlops/sec este cel mai rapid supercomputer academic din lume. Colegiul de Inginerie (din care face parte Departamentul de Computer Science) este an de an nominalizat ca una dintre cele mai bune şcoli de ştiinţe inginereşti din Statele Unite.

Grupul nostru (http://people.cs.vt.edu/~asandu) lucrează la multe proiecte interesante. Dezvoltăm algoritmi de calcul numeric şi software pentru simulări foarte mari pe maşini masiv-paralele, algoritmi pentru integrarea informaţională a observaţiilor şi modelelor, metode de simulare cu rezoluţie adaptivă în timp şi spaţiu, modelarea sistemelor cu incertitudini, etc. Domeniile de aplicaţie includ ştiinţele Pământului, inginerie mecanică, biologie, etc.

Procedura de admitere (http://www.cs.vt.edu/gpc/admissions.html) şi cerinţele programului de doctorat (http://www.cs.vt.edu/gpc/phd.html) sunt on-line. Cei interesaţi în a urma studii de doctorat în Computer Science la Virginia Tech pot să mă contacteze direct la adresa de mai jos.

Cu mult respect al Dvs,
Prof. Adrian Sandu

Adrian Sandu, Assoc. Prof.Phone: (540) 231-2193
Department of Computer ScienceFax: (540) 231-6075
Virginia TechEmail: sandu@cs.vt.edu
Blacksburg, VA 24061-0106URL: http://www.cs.vt.edu/~asandu

Pe scurt, un candidat ar trebui să fie interesat de cercetare în următoarele direcţii:

  • ecuaţii diferenţiale cu derivate parţiale
  • adjoint modelling
  • probleme inverse
  • optimizare pentru probleme neliniare

Ca o bază de plecare, studenţii interesaţi trebuie să aibă cunoştinţe fundamentale de analiză numerică, algebră liniară + ecuaţii diferenţiale ordinare + algoritmi paraleli (bineînţeles, totul urmează să fie aprofundat prin cursuri la graduate level o dată ce încep lucrul la VT).


Dacă sunteţi atraşi de acest domeniu, sau aveţi prieteni care se încadrează în cerinţele de mai sus, puteţi lua legătura cu prof. Sandu folosind datele de contact de mai sus.

Din Gândul de astăzi anul ăsta, aflăm:

La 200 de absente pe semestru, adica pentru mai putin de doua luni de cursuri, seminarii si laboratoare cumulate, un student de la Politehnica va trebui sa plateasca taxa anuala de studii in urmatorul an universitar. Aceasta este cea mai drastica si cea mai contestata (de catre studenti) prevedere din noul Regulament al Universitatii Politehnica Bucuresti (UPB), ce va intra in vigoare la 1 octombrie 2006. Studentii care vor chiuli mai mult de 80 de ore "din activitatile didactice dintr-un semestru", adica aproximativ doua saptamani, vor fi sanctionati cu mustrare, iar pentru mai mult de 130 de ore vor primi un avertisment scris.

Calificativele de student mustrat sau atentionat nu sunt puse doar de ochii lumii, ci atrag dupa sine pierderea urmatoarelor drepturi: de a participa la tabere de odihna, de a primi burse de merit, de a fi reprezentant al studentilor sau de a participa la programele SOCRATES, ERASMUS, LEONARDO, precum si la alte programe internationale. Regulamentul UPB nu a fost votat de nici unul dintre studentii prezenti la sedinta Senatului din 4 mai, cand a fost adoptat. Prima contestatie a studentilor a fost aceea ca ei nu au fost consultati de conducerea universitatii atunci cand a fost intocmit regulamentul. In scurt timp de la adoptarea regulamentului, Asociatia Studentilor din Politehnica a inaintat conducerii o petitie in care reprosa, in primul rand, prezenta obligatorie la cursuri. Degeaba au incercat ei sa argumenteze ca prezenta la curs nu determina nivelul de cunostinte al studentului, ca sunt prea putine sali cu o capacitate de peste o suta de locuri in care sa se desfasoare cursurile (intr-o grupa [mai degrabă serie - n.r.] fiind 150 de studenti), ca multi studenti lucreaza sau sunt la doua facultati sau chiar ca exista profesori carora le lipsesc aptitudinile pedagogice si ale caror cursuri sunt greu de urmarit. Tot ce au putut obtine, in urma sedintei care a avut loc la sfarsitul saptamanii trecute, a fost o amanare a aplicarii regulamentului.

Din același articol, aflăm părerea doamnei profesoare Andronescu:

"forma de invatamant pe care Politehnica Bucuresti o promoveaza este aceea la zi, cu frecventa. In al doilea rand, invatamantul tehnic este un invatamant greu, dificil, iar la curs profesorul iti spune mai mult decat scrie in carte, iti povesteste si din experienta proprie. Si atunci este un plus de valoare, iar studentul invata mai mult. Deocamdata, am hotarat ca acest sistem sa se aplice un an, experimental, fara sanctiuni, ca o modalitate de a inregistra frecventa si pentru a analiza aspectele bune si mai putin bune. E posibil ca, la sfarsit, sa ne dam seama ca e gresit, dar e posibil si ca studentii sa se convinga ca e pentru buna lor pregatire profesionala. Nu am facut acest lucru impotriva studentilor, ci pentru cresterea calitatii in invatamant. "60% din cei mai buni studenti, de la Electrotehnica si Calculatoare, din anul II, lucreaza. Or, sa muncesti 8 ore cand esti student e prea mult. Ceea ce invata la serviciu este extraordinar de limitat, desi angajatorul, care urmareste profit, ii spune ca el acolo face scoala. Te intrebi ce va face studentul cu cariera". Ce solutii a gasit doamna rector? "M-am gandit la o initiativa legislativa prin care studentii sa poata sa lucreze part-time. Mai vrem sa aducem companii care sa lucreze in campusul universitatii. Sa dezvoltam un parc tehnologic in curtea Politehnicii".

Dacă ar fi să privesc lucrurile în mod abstract, m-aș bucura pentru această hotărâre. Este anormal ca în fiecare semestru, sălile de curs să fie destul de populate la primul curs, dar la ultimele cursuri să mai participe doar câte un reprezentant al fiecărei grupe. Este la fel de anormal ca studenții să-și petreacă semestre întregi la serviciu, când ei ar trebui să se preocupe de dezvoltarea lor intelectuală. Desigur, asta e o părere pur abstractă. E abia un an și un pic de când am scăpat din facultate și dacă îmi amintesc bine, realitatea conține și câteva elemente care lipsesc din modelul abstract de mai sus.

Conducerea Politehnicii face o confuzie gravă între cauză și efect. Sigur, este de fapt o problemă destul de comună pentru liderii care nu se ridică la înălțimea responsabilităților lor. În general, cam oricine poate identifica o problemă (de exemplu, "sălile de curs sunt goale; ciudat, pe hârtie avem destui cursanți"). De aici, însă, lucrurile pot porni în două direcții. Un manager competent va începe căutarea soluției cu o întrebare pe care cam toți copiii o pun la o anumită vârstă, dar pe care ca adulți o punem din ce în ce mai rar: "De ce?". Nu se va mulțumi cu cel mai comod, sau cu primul răspuns pe care îl va primi ("pentru că studenții trag chiulul"), ci va repeta întrebarea ("dar de ce trag studenții chiulul?") până când calitățile lui de manager îi vor spune că a ajuns la adevărata cauză a fenomenului. Abia atunci poate trece la al doilea pas, la căutarea unei soluții.

Desigur, există o alternativă mult mai simplă: De ce sunt sălile de curs goale? Pentru că studenții absentează. A, păi e simplu atunci: Din moment ce suntem deasupra studenților din punctul de vedere al autorității, vom decreta că locul lor e la curs, nu în altă parte. Avem și argumente logice: Din moment ce forma de învățământ este la zi, cu frecvență, e clar că trebuie să impunem acest aspect prin regulament. Dacă întreabă cineva, mai avem și alte argumente.

Opiniile doamnei Andronescu ar fi un exemplu foarte bun pentru elevii de clasa a VI-a care încep să învețe bazele geometriei euclidiene (dacă asta se mai întâmplă în clasa a VI-a; pe vremea mea așa era). Înainte de a pătrunde prea adânc în axiome și teoreme, profesorul nostru de matematică ne-a ținut o prelegere despre bazele logicii. Printre altele, ne-a explicat lucruri precum tabelele de adevăr, despre implicațiile logice și despre formula echivalentă pentru implicație, construită cu logică tradițională (P=>Q <=> !P || Q). Tot atunci ne-a atras atenția asupra unei situații interesante, principiul implicării adevărului prin fals (0=>1 are valoarea logică 1). Mai exact, e posibil să pleci de la premize greșite și să ajungi la o concluzie corectă.

Citatul de mai sus este interesant din alt motiv: Premizele de la care se pornește sunt rezonabile (studenții nu ar trebui să absenteze de la cursuri și nu ar trebui să aibă serviciu full-time în timpul școlii), dar concluzia la care se ajunge (prezența va fi obligatorie la cursuri) este aberantă. Cu alte cuvinte, cineva a greșit calculele pe drum (1=>0 are valoarea logică 0).

Eu le urez celor care păstoresc atât de inteligent viitoarele generații de ingineri ale României să pună mâna pe o carte bună de economie și pe una de logică. Nu de alta, dar suspectez că atunci când studenții cei mai buni preferă să lucreze pe salarii destul de proaste, nu fac altceva decât să aleagă răul cel mai mic. Până la urmă, mai bine 2 grame de experiență și un salariu oarecare decât câteva tone de timp pierdut și burse de câteva ori mai mici, nu?

Studenților nu le urez deocamdata nimic. Asta e o discutie pentru altadata.

Am promis în discuția de acum câteva zile că voi explica mai pe larg cum am utilizat profilerul din Visual Studio Team System pentru a măsura viteza de execuție a unei aplicații C#. M-am gândit eu bine și am ajuns la concluzia că mai bine scriu acest post sub forma unui îndrumar pas cu pas, cu ocazia asta urmând să experimentez și modul în care se vor înțelege Windows Live Writer și Community Server la salvarea pozelor și la ce voi mai încerca eu să fac pe aici. Cu alte cuvinte, dacă nu iese din prima, cel puțin știți de ce. În plus, am făcut tot posibilul să scriu articolul în așa fel încât să vă descurcați și fără exemplele vizuale.

Configurarea sistemului

În primul rând, aveți nevoie de Visual Studio Team Edition for Developers, cu utilitarele de performanță instalate, pe o mașină rezonabil de puternică (pe o mașină cu performanțe mai slabe, viteza aplicației poate avea de suferit fără să fie de vină aplicația în sine, de exemplu din cauza paginării). Va trebui să lucrați dintr-un cont de administrator, deoarece pentru anumite configurații despre care vom discuta mai jos este necesară încărcarea unui driver în nucleul sistemului de operare, iar această operație are nevoie de privilegii indisponibile utilizatorilor obișnuiți. În fine, pentru a obține de la profiler lista corectă de funcții apelate, veți avea nevoie de simboluri corecte atât pentru aplicația voastră, cât și pentru binarele Microsoft.

Dacă aveți clientul de Symbol Server instalat corespunzător (conform instrucțiunilor de pe pagina Getting Started de pe site-ul dedicat Debugging Tools for Windows), nu mai aveți nimic de făcut. În particular, pe calculatorul meu am configurat variabila de mediu _NT_SYMBOL_PATH la valoarea SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols pentru toți utilizatorii și obțin automat simbolurile publice de la Microsoft de fiecare dată când pornesc orice debugger de la Microsoft, de la WinDbg la Visual Studio. Mai puteți face "set _NT_SYMBOL_PATH=SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols" dintr-o consolă de Windows și lansați Visual Studio de acolo și sunt destul de sigur că puteți configura calea și din Tools - Options - Debugging - Symbols, dar nu am încercat asta niciodată.

Aplicația

Presupunând că toate condiţiile de bază sunt îndeplinite, deschideți un Visual Studio și creați o aplicație C# de consolă. Eu am denumit proiectul "Grayscale", dar îi puteți spune cum doriți. Adăugați o referință la System.Drawing.dll, directivele using corespunzătoare, și adăugați următoarea funcție în clasa care conține programul principal:

static void Grayscale(Bitmap img)
{
if (img.PixelFormat != PixelFormat.Format24bppRgb)
{
// Mental note: Never embed strings in code like this
throw new ArgumentException("Expected a 24 bpp bitmap");
}
BitmapData bmpData = null;
try
{
bmpData = img.LockBits(
new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.ReadWrite, img.PixelFormat);

unsafe
{
for (int y = 0; y < img.Height; y++)
{
byte* src = (byte*)bmpData.Scan0 + y * bmpData.Stride;
for (int x = 0; x < img.Width; x++, src += 3)
{
src[0] = src[1] = src[2] = (byte)(0.2125 * src[2] +
0.7154 * src[1] + 0.0721 * src[0]);
}
}
}
}
finally
{
if (bmpData != null)
{
img.UnlockBits(bmpData);
}
}
}

Programul principal e destul de simplu - deschide primul fișier primit pe linia de comandă, îl transformă și îl salvează în al doilea:

static void Main(string[] args)
{
try
{
if (args.Length != 2)
{
// Mental note: Never embed strings in code like this
throw new ArgumentException("Usage: Grayscale InputFile.bmp OutputFile.bmp");
}
Bitmap bmp = new Bitmap(args[0]);
Grayscale(bmp);
bmp.Save(args[1]);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}

Ajunși în stadiul acesta, trebuie să configurăm puțin proiectul pentru a îl putea compila cu succes. Deschideți proprietățile proiectului (în Solution Explorer, faceți clic pe dreapta pe proiectul Grayscale) și mergeți la pagina "Build". În caseta "Configuration" selectați "All Configurations", după care bifați opțiunea "Allow unsafe code". Vă mai recomand și să treceți prin pagina "Debug" și să deselectați opțiunea "Enable the Visual Studio hosting process" (Există în MSDN Library explicații despre Visual Studio hosting process; pe scurt, oferă servicii gen partial trust debugging și design-time expression evaluation. Nu este necesar pentru măsurătorile noastre, și cu cât avem mai puține procese pornite, cu atât mai bine).

Măsurătorile

Formalitățile fiind încheiate, putem trece la partea interesantă. Selectați configurația "Release" a proiectului (în general, e bine să aveți grijă să nu măsurați viteza versiunii de debug) și deschideți fereastra "Performance Explorer" (View - Other Windows - Performance Explorer). Dacă nu găsiți comanda, e posibil să nu aveți Visual Studio Team Edition for Developers instalat (vezi pasul 1) sau să vă fi particularizat mediul de dezvoltare în cine știe ce fel, caz în care va trebui să deschideți dialogul "Customize" cu Tools - Customize, să selectați pagina "Commands" și să căutați comanda "Performance Explorer" în grupul "View".

În fereastra "Performance Explorer", faceți clic pe "New Performance Session". Va fi creată automat o sesiune numită "PerformanceN", cu două subdirectoare, "Targets" și "Reports". Faceți clic pe dreapta pe "Targets" și selectați "Add Target Project". Întrucât proiectul Grayscale este singurul care face parte din soluție, va fi adăugat automat ca țintă. Faceți clic pe dreapta pe Grayscale și deschideți fereastra de proprietăți. Pe pagina "Launch", bifați "Override project settings", iar în caseta "Arguments" introduceți calea completă spre fișierul .BMP care va fi folosit drept intrare și calea completă spre ieșire (de exemplu, 'C:\Grayscale\Test\Input.bmp C:\Grayscale\Test\Output.bmp', fără apostrofuri). Eu am folosit ca intrare o poză oarecare, de 2048 x 1536 pixeli, convertită la formatul BMP 24 bpp (cam 9 MB și ceva). Nu uitați să folosiți ghilimele în cazul în care numele conțin spații. Salvați setările și închideți fereastra.

Salvați sesiunea de performanță pe care tocmai ați creat-o (eu am salvat-o în același director cu proiectul, și i-am dat același nume). Faceți clic pe dreapta pe sesiunea de performanță și deschideți fereastra de proprietăți. În pagina "General", configurați opțiunea "Profiling collection" pe valoarea "Instrumentation" (implicit este pe "Sampling"). Deoarece ne interesează să evaluăm doar viteza aplicației, nu vom configura secțiunea ".NET memory profiling collection" (deși sunt lucruri interesante de văzut și acolo). Verificați că în pagina "Launch" este bifat proiectul Grayscale (ar trebui să fie bifat implicit, și nu există alte opțiuni oricum). Salvați setările și închideți fereastra.

În acest moment, în "Performance Explorer" ar trebui să mai fi apărut un buton numit "Launch". Faceți clic pe el pentru a rula aplicația. După terminarea procesului, Visual Studio va descărca datele obținute și le va transforma într-o serie de rapoarte. În acest timp, e o idee bună să monitorizați fereastra Output - dacă observați mesage de genul "Failed to load pdb for module ...", înseamnă că nu ați configurat corespunzător simbolurile (vezi pasul 1).

În pagina de sumar sunt afișate funcțiile cele mai interesante din program. Primul grup afișează funcțiile apelate de cele mai multe ori, al doilea afișează funcțiile în care este petrecut cel mai mult timp (fără a pune la socoteală funcțiile apelate de acestea), iar al treilea afișează funcțiile care durează cel mai mult. După părerea mea, pagina aceasta este cel mai puțin interesantă, în special din cauza terminologiei alese pentru cele trei grupuri (Care e diferența între al doilea și al treilea grup? De ce în al doilea grup, funcția get_Width apare cu 1114 ms, iar în al treilea are 1253 ms?). Chiar și așa, e ușor de observat care sunt cele mai "interesante" apeluri de funcție.

Ceva mai interesantă este pagina "Functions" din raport. Puteți vedea o listă plată de funcții, sau le puteți grupa după modulul de care aparțin. Implicit, sunt afișate coloane pentru numărul de apeluri și pentru cele mai importante măsurători de timp. Puteți adăuga și alte coloane, dacă doriți, și puteți sorta datele cum vă este mai la îndemână.

"Exclusive time" este termenul pentru timpul petrecut exclusiv în codul unei funcții, fără a include în această valoare timpul petrecut în funcțiile apelate, pe când "Inclusive time" este termenul pentru timpul petrecut într-o funcție, cu tot cu graful de funcții pe care aceasta le-a apelat. Diferența dintre "Elapsed" și "Application" este simplă: Primul termen desemnează timpul fizic petrecut în funcția respectivă (numit și "wall-clock time"), pe când al doilea desemnează timpul de procesor consumat efectiv de acea funcție, convertit la milisecunde. Dacă tot suntem aici, explicații complete privind termenii utilizați în Visual Studio găsiți pe pagina Understanding Performance Terms din MSDN.

În practică, cele două valori nu coincid niciodată, cel puțin pentru că în sistemele de operare multitasking, nucleul sistemului întrerupe periodic fiecare aplicație pentru a oferi și altora șansa de a rula. Dacă tot suntem la acest subiect, spunem despre un program că este "CPU bound" atunci când "Application time" tinde spre "Elapsed time" sau, cu alte cuvinte când aplicația folosește procesorul la capacitatea maximă permisă de sistemul de operare. Cealaltă extremă este reprezentată de aplicații "I/O bound", în care "Elapsed time" este mult mai mare mare decât "Application time", deoarece aplicația depinde mai mult de sistemele de intrare/ieșire ale calculatorului (de exemplu, transferul unui fișier mare peste o conexiune lentă).

Între cele două extreme găsim un spectru foarte variat, despre care nu vom discuta azi. Merită totuși amintit că un aspect nu îl exclude complet pe celălalt: Cele mai multe aplicații au nevoie de ambele resurse, dar una din ele se întâmplă să domine. Există și aplicații care au nevoie în măsură aproximativ egală de timp de procesor și de operații de intrare/ieșire, cel mai comun exemplu de aplicație lacomă cu toate resursele fiind compilatorul de C++.

Trecând pe pagina "Call Tree", găsim aceleași informații, dar organizate într-un arbore de apeluri. Aceasta este una din vederile mele preferate, pentru că scoate foarte ușor în evidență locurile unde își petrece aplicația timpul. În cazul nostru, din datele obținute reiese că funcția Main este nesemnificativă, deoarece din timpul total de rulare (3054 ms), aplicația petrece 3 ms în Main. Se vede, în schimb, că execuția funcției Grayscale domină timpul total de rulare, cu 2485 ms, și că aplicația petrece 50 ms încărcând imaginea de pe disc și 515 ms salvând rezultatul (puteți investiga de ce apare această discrepanță ca exercițiu).

Continuând disecarea rezultatelor, observăm că Program.Grayscale petrece 1231 ms făcând operații proprii, dar că aproximativ 2485 - 1231 = 1254 ms sunt petrecute în funcțiile apelate. Dintre acestea, iese imediat în evidență faptul că 1253 ms sunt petrecute în Image.get_Width, care este apelată de un număr de ori apropiat de numărul pixelilor din imagine.

Pentru că am ales un program simplu ca exemplu, investigația noastră se oprește aici: Știm că dimensiunile pozei nu se schimbă pe parcursul execuției algoritmului, așa că vom apela o singură dată Image.get_Width și vom salva rezultatele într-o variabilă locală:

bmpData = img.LockBits(
new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.ReadWrite, img.PixelFormat);
 
int width = img.Width;
int height = img.height;
 
unsafe
{
for (int y = 0; y < height; y++)
{
byte* src = (byte*)bmpData.Scan0 + y * bmpData.Stride;
for (int x = 0; x < width; x++, src += 3)
{
src[0] = src[1] = src[2] = (byte)(0.2125 * src[2] +
0.7154 * src[1] + 0.0721 * src[0]);
}
}
}

(Am scos în afara buclei și apelul Image.get_Height, pentru simetrie).

Cu codul modificat, după încă o rundă de măsurători rezultatele se schimbă destul de radical:  Timpul total de execuție scade la 157 ms, din care funcția de conversie la alb-negru ocupă 51 ms, încărcarea imaginii durează 45 ms, iar salvarea ei 57 ms (ciudat, chiar ar merita investigat de ce în cealaltă rundă de măsurări salvarea era de 10 ori mai lentă).

Înainte de a încheia, mai merită vorbit despre câteva lucruri. În primul rând, când am configurat sesiunea de performanță, ați observat că am schimbat tipul măsurării efectuate din "Sampling" în "Instrumentation". Cele două metode sunt radical diferite, și merită scoasă în evidență diferența dintre ele.

La instrumentarea unei aplicații, profilerul inserează cod suplimentar pentru fiecare apel de funcție (înaintea lui, și după el) pentru a măsura timpul de execuție al fiecărei componente. Această metodă are avantajul că este precisă, în sensul că oferă informații exacte despre numărul de apeluri efectuate, despre funcțiile care apelează și sunt apelate de o anumită funcție, și rezonabil de exacte despre timpii de execuție. Spun "rezonabil de exacte" deoarece inserarea codului suplimentar de către profiler afectează memoria ocupată, ca și viteza aplicației.

Tehnica de măsurare prin sampling este radical diferită: Periodic, sistemul de operare întrerupe execuția tuturor firelor de execuție din procesul monitorizat, parcurge stiva fiecărui fir de execuție din acel moment și memorează această stivă. Sunt de notat câteva lucruri aici: În primul rând, metoda de măsurare este inexactă, deoarece procesul nu poate fi suspendat la fiecare instrucțiune executată. Intervalul de timp la care are loc întreruperea este de obicei de ordinul milioanelor de perioade de ceas (1 - 10 milioane).

Apoi, pentru ca datele obținute să aibă valoare, ele trebuie să fie relevante din punct de vedere statistic; cu alte cuvinte, numărul de mostre adunate trebuie să fie cel puțin de ordinul miilor, dacă nu mai mare (cu cât mai mare, cu atât mai bine).

În al treilea rând, nu suntem obligați ca pe axa X să avem timpul ca unitate de măsură. De exemplu, putem instrui profilerul să ia o mostră la fiecare 50 de hard page faults. Evident, schimbând unitatea de măsură, schimbăm înțelesul datelor: Funcțiile care vor apărea în acest caz nu sunt cele mai interesante din punctul de vedere al vitezei, ci al accesului la memoria internă și externă a sistemului.

În al patrulea rând, contează extrem de mult frecvența cu care sunt adunate mostrele. Dacă intervalul ales e prea mare, ne vom alege cu mai puține mostre, deci scade relevanța statistică a setului de date colectat. Pe de altă parte, dacă intervalul e prea mic, vom ajunge să întrerupem procesul prea des și îi vom afecta performanțele în mod negativ fără ca procesul să fie vinovat de acest lucru (de exemplu prin introducerea forțată a unui număr prea mare de schimbări de context).

În fine, am lăsat la urmă unul din aspectele cele mai importante: Din punctul de vedere al sistemului de operare, o stivă e o stivă și atât. Nu contează că pe ea se află funcții native sau .NET, profilerul va evalua tot. În felul acesta, putem diseca mult mai precis modul în care lucrează o aplicație (așa am aflat de exemplu că apelul Image.get_Width este lent deoarece există o tranziție spre cod nativ și un nivel de sincronizare).

M-am lungit cu vorba, așa că mai bine mă opresc aici. Am pus la în pagina  Profiling 101 toate imaginile pe care le-am cules cu ocazia acestui experiment. Nu voi intra azi în subiecte precum automatizarea acestui proces, configurarea mașinilor de test și a testelor pentru că mai am nevoie de subiecte și pentru altă dată. Și încă nu am ajuns la garbage collector.

În fine, nu mi-e clar nici măcar acum dacă pluralul pentru "apostrof" este "apostrofuri" (așa cum am scris aici) sau "apostroafe" (așa cum am scris prin alte articole). Am fost pe www.dexonline.ro și au vreo cinci intrări pentru acest cuvânt, provenind din diferite dicționare. Prima variantă apare mai des, dar apare și a doua (cel puțin o dată).

Claudio Caldato, program manager în grupul CLR, și Maoni Stephens, developer în același grup, au un articol foarte reușit în MSDN Magazine de luna aceasta despre investigarea problemelor de memorie în aplicații .NET. Nu exagerez deloc când spun că dacă parcurgeți acest articol și cele câteva articole de pe blogs.msdn.com la care se face referire, e foarte probabil să fiți în stare să depanați pe cont propriu cam orice problemă de performanță legată de alocarea obiectelor în aplicații .NET.

Technorati tags:

... Când evaluați performanțele aplicațiilor.

Am citit cu interes astăzi articolul lui Victor despre lucrul cu clasa Bitmap și diferențele surprinzătoare de performanță cauzate de două apeluri de funcție. Dacă nu ați citit încă articolul, chestiunea arată în rezumat cam așa:

Un obiect de tip System.Drawing.Bitmap este transformat (aici, în particular, o imagine color este transformată în alb-negru). Transformarea se face iterând peste fiecare pixel al imaginii și accesând direct datele care compun imaginea, folosind cod C# unsafe. Victor a observat că dacă scrie bucla în care se face prelucrarea așa:

int width = bmp.Width;
int height = bmp.Height;
for (int y = 0; y < height; y++)
    for (int x = 0; x < width; x++)
        [...]

... În loc de așa...

for (int y = 0; y < bmp.Height; y++)
    for (int x = 0; x < bmp.Width; x++)
        [...]

... Creșterea de performanță este semnificativă (de la 2 secunde la 0.3 secunde pe testul lui).

Dacă am înțeles bine, explicația oferită de Victor a fost că salvarea rezultatelor în două variabile locale face ca utilizarea lor să fie mult mai rapidă decât citirea lor repetată din memorie.

În vreme ce explicația este plauzibilă, pentru mine ea nu este satisfăcătoare, deoarece nu este susținută cu măsurători concrete. În plus, intuiția îmi spune că back-end-ul compilatorului JIT ar trebui să înlocuiască o instrucțiune de forma "x < bmp.get_Width()" (pentru JIT noțiunea de proprietate nu prea există) cu echivalentul lui "x < bmp.m_width", presupunând că obiectul Bitmap implementează proprietatea ca pe o fațadă pentru o variabilă internă; în funcție de metoda compilată, este posibil pentru JIT chiar și să salveze acea variabilă într-un registru, dacă este foarte des folosită. Chiar și dacă apelul de funcție nu ar fi fost substituit inline, diferența de viteză nu avea cum să fie atât de mare câtă vreme acea proprietate nu ar fi făcut altceva decât să returneze o valoare precalculată.

Ca să nu cad în același păcat, cel de a susține o ipoteză fără date, m-am pus pe măsurat. Am construit o aplicație de test cât mai asemănătoare, am compilat o versiune retail și am pus profilerul pe ea. Într-adevăr, pentru varianta care apela proprietatea la fiecare iterație a buclei, testul meu dura în medie 750 ms, pentru varianta care salva rezultatele în variabile locale, testul dura în medie 16 ms (cam de 47 de ori mai rapid), o îmbunătățire chiar mai spectaculoasă decât cea observată de Victor, cauzată probabil de faptul că testul meu a fost extrem de simplist, în vreme ce al lui bănuiesc că făcea operații mai complexe care mascau într-o oarecare măsură pierderea de viteză.

Folosind un profiler, însă, am aflat ceva ce nu aș fi putut descoperi altfel: Proprietățile Width și Height ale clasei Bitmap sunt doar wrappere pentru apeluri interne de funcții GDI+. Cu alte cuvinte, valorile returnate provin din cod nativ (unde sunt probabil stocate într-un câmp oarecare) și trec prin nivelul P/Invoke din CLR. Mai mult, dacă e să dau crezare profilerului, implementarea nativă a funcțiilor este thread-safe, și o bună parte din timp este petrecut de fapt în mecanismul de sincronizare.

Ca să nu mă lungesc prea mult cu vorba, am de fapt de spus două lucruri.

În primul rând, aveți grijă cum utilizați proprietățile în .NET. Sunt deosebit de înșelătoare, deoarece sunt construite în așa fel încât să pară variabile, dar sunt de fapt apeluri de funcție. Asta înseamnă că nu e obligatoriu să fie pur și simplu fațade pentru niște variabile interne, ci pot fi apeluri extrem de complexe, care conțin sincronizare, efecte colaterale și toate bucuriile oferite de programarea imperativă în general. Exemplul meu favorit sunt expresiile de forma "a.X += b.Y", în care unii programatori nici nu realizează că se ascund trei apeluri de funcție (plus o adunare și o atribuire ca bonus).

În al doilea rând, atunci când întâmpinați probleme de performanță, sau pur și simplu diferențe inexplicabile, nu aruncați cu ipoteze fără să le susțineți cu date. Ca regulă generală în domeniul performanțelor aplicațiilor, dacă ați găsit o explicație pentru un fenomen oarecare pe căi pur teoretice, e foarte probabil ca explicația să fie greșită.

Citind niște cod, am avut azi un mic moment "WTF?!". Spuneți-mi și mie dacă vi se pare în regulă o declarație de funcție (aici, în particular, C++, dar ar putea fi în orice limbaj) de forma:

void EnumerateSomeDataStructure(LPCTSTR criteria, bool bDisplay, bool bErase, bool bQueue, bool bEraseQueued)

Dacă nu, ce anume e greșit aici?

(În realizarea acestei reconstituiri, identitatea celor implicați a fost ascunsă pentru a proteja martorii)

Probabil am mai povestit pe aici despre faptul că în vara anului 2003 am participat la un stagiu de practică (aka "internship") la Microsoft, tot în cadrul grupului CLR. La vremea respectivă, cantitatea şi calitatea instrumentaţiei disponibile în Windows şi în CLR au fost două din aspectele surprinzătoare pentru mine (în particular, mă refer la instrumentaţia ETW, despre care nici nu ştiam că există la vremea respectivă).

Mai auzisem eu că în codul din nucleul sistemelor de operare, cam 30% din linii conţin funcţionalitatea propriu-zisă, restul de 70% având grijă de tratarea erorilor şi de diverse alte elemente de infrastructură, dar am avut atunci pentru prima dată ocazia să beneficiez în practică de existenţa acelei infrastructuri, din perspectiva consumatorului de informaţie, iar posibilitatea de a vedea câte o "radiografie" a Windows în timp real s-a dovedit nepreţuită.

Tot atunci am auzit de la managerul meu o expresie care mi-a rămas întipărită - "Dacă ai cere cuiva să conducă o maşină fără indicatoare pe bord, ar spune că eşti nebun. Cu toate astea, majoritatea covârşitoare a aplicaţiilor din prezent nu au nici un fel de indicatoare ale stării lor interne."

În ziua de azi este o naivitate să spui că o aplicație este satisfăcătoare din punctul de vedere al calității doar pentru că îndeplinește cerințele funcționale din specificații.

În ultimii ani, ne-am obișnuit faptul că pe lângă criteriul menționat mai sus, o altă cerință minimă pentru orice aplicație este securitatea. E drept, se poate spune că securitatea codului este o consecință a calității lui, dar de obicei securitatea componentelor individuale ale unui sistem nu garantează automat securitatea sistemului pe ansamblu. Mai sunt necesari pași suplimentari la nivel arhitectural, cum ar fi analiza amenințărilor, încadrarea lor în categorii de risc șamd, dar (de multe ori) şi la nivel de implementare.

Similar, o altă cerinţă pe care trebuie să o îndeplinească orice aplicaţie este performanţa. Există, de altfel, oarecare similitudini între performanţă şi securitate, ca aspecte ale unei aplicaţii. E foarte dificil să demonstrezi că o bucată de cod este sigură sau performantă în absenţa unor obiective clare în această privinţă, dar este foarte uşor să recunoşti o aplicaţie nesigură sau neperformantă. Din păcate, pentru relativ mulţi producători de software, aceste aspecte nu apar în planificarea iniţială, ci sunt "ataşate" pe parcurs, atunci când lucrurile încep să scârţâie.

Enumerarea ar putea continua mult și bine. Am putea vorbi despre ușurința instalării, actualizării și ștergerii aplicațiilor, mai ales că "grija" cu care majoritatea producătorilor de software se ocupă de aceste aspecte e îngrijorătoare. Sau am putea vorbi despre instrumentare - subiectul cu care am deschis azi discuția - despre posibilitatea de a privi direct în măruntaiele unei aplicații pentru a înțelege ce se întâmplă cu ea, și mai ales de ce. Sau despre globalizarea și localizarea aplicațiilor, subiecte mult mai dificile decât par. Probabil că oricâte aspecte aș numi, tot aș uita alte câteva, la fel de importante.

Pentru unele probleme, soluțiile tehnice abundă. Când vine vorba de instrumentare, mă refer de exemplu la ETW și WMI, la contoare de performanță, event logging, dar și la soluții low-tech gen OutputDebugString sau scrierea de mesaje într-un fișier. Pentru altele, cum este securitatea, este nevoie de pregătire specializată.

Întrebarea mea este simplă: Pentru a scrie o aplicație, este de cele mai multe ori nevoie de programatori cu aptitudini relativ specializate. Unii sunt mai buni cu bazele de date, alții cu aplicațiile distribuite, alții cu interfețele Windows, web, (XAML?), și tot așa. Nu poți lua pe cineva care a lucrat ultimii câțiva ani în domeniul rețelelor și să-l faci peste noapte specialist în formate audio. Cum punem în echilibru nevoia de a crea aplicații cu adevărat industriale (care îndeplinesc toate criteriile menționate mai sus) cu necesitatea de a utiliza resursele fiecărui programator acolo unde este mai priceput?

Este mai bine ca fiecare programator să devină expert și în securitate, performanță șamd, pe lângă bagajul de cunoștințe specializate la care apelează în activitatea de zi cu zi? Sau este mai eficient să avem specialiști în aceste aspecte, care lucrează cu toate componentele unui proiect și au grijă de aplicație pe ansamblu, dar introduc un nivel suplimentar de indirectare (sau birocrație, sau proces, sau cum vreți să-i spuneți).

Sunt de părere că pentru proiecte mici, cu buget și echipe restrânse, fiecare participant trebuie să poată avea grijă pe cont propriu de aceste aspecte, pentru componentele pe care le construiește. Pe termen lung, însă, cred că e benefică existența unui specialist în performanță sau securitate, care să aibă rol de supervizor și dictator la nivelul întregului proiect. Mai cred și că nu toate aspectele trebuie tratate în mod egal; de exemplu nu mă aștept ca toți programatorii să înțeleagă subtilitățile legate de securitatea sau de internaționalizarea aplicațiilor la același nivel la care o face un specialist; pe de altă parte, însă, mă aștept ca orice programator să știe să folosească un debugger, un profiler, să-și instrumenteze codul și să includă toate aspectele relevante în proiectarea aplicației, de la bun început.

Voi ce părere aveți? Prin proiectele la care lucrați, cum sunt tratate aspectele acestea?

Vi s-a întâmplat vreodată să vă enervați când un "hacker" fabricat la Hollywood sparge un mega-super-calculator conectându-se la el cu telnet și folosind o adresă IPv4 care conține numere mai mari de 255? Și mie, dar suntem în cele din urmă răzbunați: GideonTech a publicat topul celor mai stupide reprezentări ale tehnologiei în filme. Voi ce ați fi pus în listă?

Bruce Schneier are astăzi un articol interesant pe blog (ca de obicei, de altfel), University Networks and Data Security. Multe din cele spuse acolo se aplică şi la infrastructura (sau la lipsa infrastructurii) din Politehnică. Când eram student, nodul RoEdu din UPB era administrat de câţiva studenţi şi absolvenţi foarte capabili (care după aceea au ajuns să facă acelaşi lucru pe la diverşi furnizori de internet din Bucureşti şi din ţară), dar securitatea reţelei era pusă pe planul doi, după funcţionalitate (prin 2000 o latenţă de 30 de secunde între două segmente de reţea din Automatică nu era ceva neobişnuit, dar prin 2002-2003 lucrurile mergeau deja şnur). Mă întreb dacă se mai ocupă cineva de lucrurile astea acum şi dacă se mai gândeşte cineva să îmbunătăţească lucrurile sau e suficient dacă merg aşa cum merg...

Technorati tags:

... Pe care le-aş face altfel dacă aş fi din nou tânăr. Nu sunt neapărat zece la număr, şi n-am îmbătrânit aşa de mult de când am postat ultima oară pe blog, dar înţelegeţi voi ideea.

Pe parcursul ultimelor luni am descoperit că viaţa de angajat full-time are un dezavantaj major: Nu mai deţin controlul asupra timpului meu. Am o listă lungă de mini-proiecte la care mi-ar plăcea să implementez câte ceva pentru cultura mea tehnică, o listă şi mai lungă de cărţi pe care mi-ar plăcea să le citesc şi o mulţime de alte activităţi pe care mi-ar plăcea să le desfăşor în mod regulat. Şi toate astea între 8-9 ore de somn şi 9-10 ore de lucru în fiecare zi.

Gândindu-mă la perioadele lungi din liceu şi facultate când aveam relativ mult timp liber la dispoziţie, am întocmit o listă cu câteva lucruri pentru care mi-aş aloca altfel resursele dacă aş mai avea ocazia.

În primul rând, cu riscul de a vă dezamăgi, programator fiind, mi-ar fi plăcut să petrec mai mult timp scriind cod. (Nu vă pierdeţi speranţa, urmează şi lucruri mai interesante ceva mai jos. Şi chiar şi aici, discuţia nu va fi de natură tehnică.)

Pentru mine, ca pasionat de software şi de posibilităţile pe care le oferă, perioada facultăţii a fost într-un fel un con de umbră, parţial din cauza facultăţii, parţial din cauza mea. Am petrecut o perioadă de timp adaptându-mă la ritmul şi cerinţele şcolii, oricare ar fi fost acelea, după care am devenit implicat într-o sumedenie de alte activităţi, care mai de care mai atrăgătoare, şi, undeva pe drum, am pierdut contactul cu plăcerea de a porni un proiect de la zero, care la sfârşitul unei perioade de acumulare şi efort face ceva, cât de cât interesant sau util.

Sigur, cod am scris tot timpul, dar este una când ai de făcut o temă pentru şcoală sau un proiect nu foarte complicat (şi de multe ori, activităţi extrem de interesante devin fade atunci când sunt făcute din obligaţie), şi este cu totul altceva când construieşti ceva pur şi simplu din pasiunea de a adăuga zi de zi câte puţin la un proiect care va prinde viaţă abia când adaugi la sfârşit cheia de boltă (asta depinde foarte mult şi de natura proiectului, şi de tehnologia folosită, şi de gusturile personale). Cred că m-am exprimat cam abstract, dar sunt sigur că pasionaţii de programare înţeleg la ce mă refer.

Ca o paranteză, la serviciu am de-a face cu mult cod, mai mult decât poate ţine sub control un om normal după câteva mii de ani de citit surse, dar programarea "la profesionişti" este puţin diferită de programarea ca hobby. După cum îmi spunea mentorul meu la un moment dat, codul sursă trebuie să servească două categorii de oameni: pe clienţii noştri şi pe colegii de echipă. O componentă bine scrisă îndeplineşte cerinţele funcţionale şi trebuie să fie sigură, performantă şi fiabilă; codul trebuie instrumentat, pentru a nu fi o cutie neagră pentru un administrator de sistem sau pentru un coleg de la suport, care încearcă să-şi dea seama de ce comportamentul unei aplicaţii nu corespune aşteptărilor; mai mult decât orice, totul trebuie documentat, atât în cod (care trebuie să fie cât mai uşor de citit, pentru că de scris, este scris o singură dată, dar va fi citit şi modificat de sute de ori după aceea), cât şi în documentaţie, astfel încât atunci când un participant la proiect decide să exploreze alte ramuri ale domeniului şi schimbă echipele, cel care îi ia locul să poată prinde viteză fără eforturi prea mari. (Eric Lippert are un articol foarte reuşit în care plasează simbolic programarea între proiectarea rachetelor şi chirurgia pe creier; vă las pe voi să aflaţi despre ce e vorba, şi merită să citiţi şi articolul care a stârnit discuţia). Pe scurt, pot să zburd pe unde am chef, câtă vreme rezultatul îndeplineşte toate condiţiile de mai sus şi efortul este justificat din punct de vedere economic. Dar să revenim.

În al doilea rând, aş avea mai multă grijă în alegerea proiectelor în care mă implic. Poate că sună un pic ciudat, aşa că o să explic mai pe larg: De-a lungul anilor de liceu şi de facultate, am fost pus de relativ multe ori în situaţia de a decide dacă vreau să mă implic într-o anumită activitate. De cele mai multe ori, ofertele au fost extrem de interesante, făcând foarte dificilă decizia de a le accepta sau de a le respinge. Aşa se face că, de câteva ori, am acceptat să particip la o acţiune deşi nu dispuneam de timpul necesar pentru a o duce la bun sfârşit în condiţii bune, şi am dezamăgit un număr (din fericire, mic) de oameni care mi-au oferit şansa de a le fi colaborator.

Cred că dacă aş fi ştiut să îmi evaluez mai bine limitele şi să spun "nu" atunci când era cazul, aş fi putut evita situaţiile neplăcute de acest gen. Sigur, faptul că am dus la bun sfârşit o varietate de acţiuni mi-a adus şi încă îmi mai aduce beneficii enorme, şi chiar şi învăţămintele trase din greşeli oferă o anumită valoare faptului că le-am comis. Atâta doar că eşecul, ca orice doctorie, este destul de amar, mai ales când implică şi dezamăgirea altora.

În al treilea rând, cred că ar fi trebuit să aloc măcar o fărâmă de timp studiului istoriei moderne, în particular, şi câteva alte discipline umaniste, în general. Citesc zilele acestea o carte numită "The Rise of the Vulcans", de James Mann, şi la începutul capitolului al cincilea, am fost uluit să constat că şase-şapte pagini de text mărunt, care discută câteva aspecte ale istoriei anilor '70 explică mai mult despre războiul din Irak şi despre situaţia din Orientul Mijlociu decât au explicat în câţiva ani o mulţime de jurnalişti isterizaţi, în căutare de audienţă, şi o mulţime la fel de mare de politicieni, în căutare de voturi.

Am realizat că istoria, aşa cum am învăţat-o noi în şcoala generală şi în liceu (cel puţin generaţia mea), reprezintă o fracţiune infimă din ce s-a întâmplat în lume pe parcursul timpului, iar acest lucru este cu atât mai evident când vorbim despre istoria ultimului secol. De exemplu, în manualele din liceu istoria modernă se reducea la tovarăşul Dej, tovarăşul Ceauşescu şi câteva comentarii despre Războiul Rece, ONU, URSS şi SUA; eu mai mult de atât nu-mi prea amintesc. E de-a dreptul uimitor cât de uşor de înţeles devin acţiunile naţiunilor şi politicienilor în prezent dacă ştii câtuşi de puţin despre trecutul lor. E valabil chiar şi pentru România... Pe o notă similară (să-i spunem "punctul 3 bis"), găsesc la fel de interesante psihologia şi sociologia. Sunt multe de explorat în universul care ne înconjoară, dar mai avem cel puţin la fel de multe de învăţat despre oameni, ca indivizi şi ca grupuri organizate.

Trecând la latura practică a lucrurilor, am descoperit în practică importanţa stăpânirii uneia sau mai multor limbi străine. De exemplu, cu engleza am descoperit o problemă interesantă: Am studiat engleza generală relativ mulţi ani, şi mă descurc relativ bine la nivel conversaţional, iar în ce priveşte cunoştinţele de programare pe care le posed, peste 90% provin din documentaţie scrisă în engleză, deci în în privinţa aceasta stau foarte bine. Am constatat însă că în viaţa de zi am probleme cu lucruri mărunte, cum ar fi denumirile unor legume sau fructe, sau ale unor accesorii casnice. E deosebit de distractiv atunci când ţi se strică ceva pe la baie şi trebuie să-i descrii instalatorului la telefon care e problema.

În plus, într-o lume globală, stăpânirea unei singure limbi străine se dovedeşte de multe ori nesatisfăcătoare. Deşi am avut tangenţe prin şcoală cu franceza şi rusa, nu am prea avut nevoie de ele în practică, şi din păcate mare lucru nu s-a lipit de mine (presupun că a înţelege o propoziţie din trei, şi respectiv treizeci, nu mă face vorbitor fluent al vreuneia din aceste limbi). Lucrurile sunt cu atât mai interesante în Europa, unde trebuie să alegi între germană, franceză şi spaniolă. Cel puţin în SUA, cu spaniola şi engleza te descurci cam peste tot.

Schimbăm iarăşi puţin vitezele şi ajungem la un aspect pe care l-am neglijat sistematic până recent, dar pentru care niciodată nu e prea târziu: sportul. Privind în urmă, găsesc cel puţin două motive pentru care ar fi trebuit să acord mai multă atenţie formei fizice:

În primul rând, oricât de banal ar suna, nu am fost construiţi pentru stilul de viaţă pe care îl avem în prezent, iar alimentaţia, sedentarismul şi ritmul dezordonat de viaţă îşi pun amprenta foarte rapid asupra fiecăruia, chiar dacă efectele nu sunt vizibile de la o zi la alta. O oră de mişcare în fiecare zi, împreună cu o nutriţie controlată puţin şi cu un ritm de viaţă stabil au efecte vizibile asupra stării fizice şi psihice.

Al doilea motiv pentru care am învăţat să apreciez sportul se leagă de răbdare, perseverenţă şi moral. Dacă vreţi, puşi în faţa unei provocări fizice, nu mai avem cum să ne minţim - lucrurile sunt albe sau negre - poţi sau nu poţi face faţă solicitării - aşa că un prim efect este că înveţi să îţi admiţi limitele, oricât de neplăcut ar fi acest lucru. Al doilea efect este că descoperi destul de repede că unele din limite pot fi împinse mai departe, cu condiţia să nu renunţi, şi să înţelegi că rezultatele pozitive nu apar peste noapte. Al treilea efect este că după un timp, devii din ce în ce mai greu de descurajat de eşecuri, pentru că de multe ori ceea ce era imposibil acum o vreme este prezent acum, datorită antrenamentelor. În fine, atunci când în cele din urmă te apropii de limitele reale ale organismului, ai cel puţin satisfacţia că exploatezi la maxim ceea ce ţi-a fost dat.

Apropo de sport, un alt domeniu pe care îl redescopăr cu interes este cel al anatomiei şi fiziologiei. Încercaţi să răsfoiţi un articol scurt despre hormoni, chimia sângelui sau a muşchilor, despre metabolism sau sistemul nervos şi reflectaţi puţin la cât de complexă este fiinţa umană pe plan fizic. Eu unul, pus în faţa unui astfel de sistem, mă simt aproape neputincios şi mă mir într-un fel de tentativa naivă a medicinei moderne care încă mai crede că poate înţelege interacţiunile interne ale organismului, şi că le poate repara atunci când deviază de la normalitate. În particular mi se pare amuzant modul liniar în care privim lucrurile, crezând că este suficient să modificăm una-două variabile pentru a obţine efectul scontat.

Cred că am încheiat lista de lucruri pe care le-aş fi schimbat mai devreme în viaţa mea. Nu am ajuns chiar la zece, aşa că voi lăsa deschisă o secţiune pe care o voi numi în mod original "şi altele".

În concluzie, o să spun doar că îmi doresc să fi înţeles mai devreme că este important să ştii să sprintezi, dar că viaţa este totuşi un maraton în care răbdarea şi perseverenţa joacă un rol critic. În afară de asta, chiar dacă multe probleme pot fi rezolvate din mers, pe măsură ce apar, şi chiar dacă suntem permanent supuşi neprevăzutului, mi se pare util să cultivăm şi aspecte importante pe termen lung. Ne place sau nu, în fiecare zi trebuie să lucrăm, să ne odihnim, să facem ceva pentru noi înşine, pe plan fizic şi psihic, şi să ne îngirjim şi de relaţia cu cei apropiaţi. Nu sunt lucruri pe care le putem face pe rând, câte unul pe zi. Am ajuns să-mi dau seama de asta doar de când timpul meu nu-mi mai aparţine.

Care sunt lucrurile pe care le-aţi face mai des şi de ce nu le faceţi?

Probabil ştiţi deja de la Todi despre rezultatele impresionante obţinute de studenţi români la Windows Embedded Student Challenge şi Imagine Cup. Am aflat acum câteva momente de încă o oportunitate de acest fel, de data aceasta deschisă tuturor.

Concursul se numeşte "Develop without Borders", este organizat de Microsoft în parteneriat cu HP şi este adresat programatorilor şi arhitecţilor de soluţii. Conceptul este următorul: Participanţii aleg o organizaţie de caritate pe care ar dori să o ajute, pătrund în activităţile pe care le desfăşoară această organizaţie şi înţeleg provocările pe care le întâlneşte, după care dezvoltă o soluţie bazată pe Office 2007 care rezolvă una sau mai multe din problemele descoperite.

Soluţia cu impactul cel mai semnificativ asupra unei astfel de organizaţii poate câştiga până la $50.000 pentru a dezvolta soluţia pilot sau pentru a o implementa.

Detalii la www.developwithoutborders.com.

Technorati tags:

Tocmai am aflat că a apărut ediţia august 2006 a CTP-ului de ADO.NET vNext. Dacă v-au plăcut demo-urile de LINQ şi v-aţi jucat cu unul din CTP-urile anterioare, probabil veţi dori să citiţi anunţul de la http://blogs.msdn.com/adonet/archive/2006/08/15/701479.aspx

Dacă aveţi nevoie de şi mai multe informaţii, echipa ADO.NET a adunat deja o colecţie destul de mare de resurse:

Whitepapers:

Channel 9

Screencasts

Blogs:

Blog posts (sunt sigur că lista e deja veche în câteva minute de acum)

MSDN Forums

Technorati tags:

More Posts Next page »