Jak wygląda praca nad oprogramowaniem do sekwencjonowania DNA w czasie rzeczywistym?

„Dzień dobry, dzisiaj zaczynamy nowy projekt, projekt, który będzie polegał na budowie systemu do analizy obrazu DNA w czasie rzeczywistym” – przyznajcie się, kto chciałby usłyszeć takie zdanie od swojego przełożonego? Chyba ze świecą szukać programisty, który przeszedłby obojętnie obok takiej oferty współpracy. Jak wynika z wielu badań, to właśnie ambitne i dobrze zdefiniowane cele należą do największych atutów w pracy deweloperów. Niestety, interesujące projekty nie zdarzają się zbyt często, a tym bardziej takie, które wymagają wiedzy również z innych dziedzin nauki…

Fot: University of Michigan School, CC BY 2.0.

Mimo że w większości firm z branży IT 99% zadań jest całkowicie powtarzalnych i nie wymaga od doświadczonych programistów nadmiernego wysiłku, to jednak zawsze istnieje ten 1%, który wywraca całą tą statystykę do góry nogami. Wspólnie z firmą Sii postanowiłem więc przygotować dla was materiał opowiadający o pracy nad oprogramowaniem wykorzystywanym do analizy obrazu DNA. Tematyka ta jest interesująca nie tylko pod względem technicznym, ale również legislacyjnym i organizacyjnym.

Inne postrzeganie problemu to początek drogi do sukcesu

Czy tworzenie oprogramowanie dla branży medycznej jest ciekawe? Większość z Was pewnie odpowiedziała twierdząco na tak postawione pytanie. Warto jednak nadmienić, że w praktyce nie jest tak kolorowo, jak może się wydawać. Jeżeli pracujemy nad projektem związanym z szeroko pojętą medycyną, musimy przestrzegać kilku rygorystycznych zasad:

  • z oczywistych względów stabilność wykorzystywanej technologii jest kluczowa, nie ma więc mowy o implementacji przykładowo najnowszej wersji Javy, która wyszła tydzień temu,
  • zgodnie z przepisami prawnymi projekt ma być wspierany przez najbliższe 10 lat, więc już w początkowej fazie wytwarzania oprogramowania trzeba brać to pod uwagę,
  • warto również pamiętać o tym, że to, co nas, programistów, interesuje najbardziej – czyli kodowanie i wytarzanie oprogramowania, jest jedynie niewielkim dodatkiem do pracy poświęconej chemicznym aspektom projektu.

W związku z tym zdarza się, że programiści nie są traktowani poważnie. Do tego dochodzi fakt, że wielu developerów nie ma doświadczenia z tego typu projektami. Dodatkowo, możemy mieć również do czynienia z klasycznym problemem przywiązania do dotychczasowego stylu pracy. To sprawia, że innowacyjne pomysły spotykają się często z niechętną reakcją osób zaangażowanych w projekt. Na początku trzeba więc przebić „mur” różnego rodzaju regulacji i przyzwyczajeń, co przecież nie zawsze jest łatwe.

Jak odbywało się to w projekcie sekwencjonowania DNA, realizowanym między innymi przez programistów pracujących w Sii?

Mieliśmy szczęście, że osoba, która kierowała projektem, chciała wprowadzić pewne innowacje do firmy, m.in. architekturę mikroserwisową. Niekiedy jednak na przeszkodzie wprowadzenia kolejnych nowości stały regulacje dotyczące procesu wytwarzania oprogramowania dla urządzeń stosowanych w diagnostyce medycznej.

Aby móc używać nowszych rozwiązań, musieliśmy przygotować testy oraz odpowiednią dokumentację stwierdzającą przewagę nowej technologii nad tą, która była wykorzystywana dotychczas.  Każda biblioteka zewnętrzna lub narzędzie używane (CI, automatyczny formater kodu, narzędzie do statycznej analizy, framework do testów) wymagały przygotowania dokumentów które potwierdzały pomyślne przejście testów – mówi Marcin Grzebieluch, Senior Developer w Sii.

Pewnie brzmi to dla Was trochę nierozsądnie, ale łatwo wyobrazić sobie sytuację, w której używana biblioteka ma błąd. Nagle test, który powinien pokazać, że kod nie działa zgodnie z oczekiwaniami, kończy się pozytywnie. Byłby to duży problem w każdym projekcie. Jednak w branży medycznej chodzi o ludzkie życie. Po takim błędzie zdrowa osoba mogłaby zostać wysłana na chemioterapię.

Okazało się, że użycie nowszych wersji oprogramowania: kompilatora, bibliotek, paczek systemu operacyjnego, znacząco przyspieszyło projekt. Kompilatory są coraz lepsze. Nowsze narzędzia pozwalają zapobiec błędom, których starsze wersje nie wychwytywały. Jednocześnie algorytmy optymalizacji w ciągu ostatnich kilku lat rozwinęły się tak mocno, że w niektórych przypadkach sama zmiana kompilatora może przyspieszyć kod nawet o 20%. Oczywiście nic nie jest za darmo. Jeżeli kod, który piszemy, jest niezgodny ze standardem i zawiera błędy, może się zdarzyć, że po zmianie wersji coś, co działało do tej pory, nagle przestaje. Na szczęście można temu zapobiec, jeżeli dba się o to, aby kod był zgodny ze standardem i używa się więcej niż jednego kompilatora w środowisku deweloperskim.

Nie prowadzimy statystyk, ile błędów udało się wychwycić dzięki lepszym narzędziom. Jednak przez prawie 2 lata developmentu mieliśmy tylko dwa razy problem z wyścigami w wielowątkowym kodzie. I dało się te problemy rozwiązać w ciągu jednego dnia. Wszystko dzięki użyciu sanitizerów dostarczanym przez nowe kompilatory Clang i GCC. – dodaje Marcin Grzebieluch.

A co z tymi wszystkimi danymi? Przecież jest ich cała masa!

Nie ulega wątpliwości, że przy tego typu projektach przetwarzanych jest sporo różnego rodzaju danych. NGS (Next Generation Sequencing) jest, przede wszystkim, procesem chemicznym. Odczytywane DNA zostaje mnożone wielokrotnie za pomocą polimerazy, żeby dało się je zaobserwować po naświetleniu pod mikroskopem. Sama polimeraza jako proces chemiczny nie jest bezbłędna. W większości daje dobre wyniki, ale czasem może się mylić.  Aby wyeliminować pomyłki, trzeba zbadać bardzo dużo próbek. To generuje olbrzymie ilości danych do przetworzenia.

W ogromnym skrócie proces taki polega na potraktowaniu milionów próbek DNA chemią, która sprawi, że odpowiedni  fragment będzie świecił w danym momencie. Następnie wykonuje się zdjęcie płytki, przesyła je do analizy i powtarza cały proces. Mikroskop generuje w ten sposób około 160Mb danych na sekundę w postaci obrazów. To stwarza poważne wyzwanie, polegające na przetwarzaniu tych wszystkich danych w czasie rzeczywistym. Dodatkowo musimy pamiętać, że aby móc rozpocząć analizę, musimy mieć zebranych dostatecznie dużo informacji. Nie możemy zatem po obróbce pojedynczego zdjęcia rozpocząć jego analizy. Kiedy już odpowiednia liczba danych zostanie wczytana, możemy zacząć nad nimi pracować. Jednocześnie nie możemy zapomnieć o oprogramowaniu, bo proces chemiczny cały czas działa, a my w każdej sekundzie dostajemy kolejne porcje cennych informacji. Nie da się tego w żaden sposób spowolnić lub powtórzyć. Danych jest za dużo, żeby zapisać wszystkie obrazy, więc spotykamy się tu z problemami podobnymi do tych, znanych nam z zagadnień Big Data.

Algorytmy, których używamy, są wolniejsze niż generacja tych obrazów, więc trzeba było wymyślić inne sposoby na nadążanie za producentem danych. Dzielimy całość na kawałki i na maszynie, na której przeprowadzana jest analiza, przetwarzamy je równolegle. Przetworzenie 160Mb zajmuje o rząd wielkości więcej niż wyprodukowanie. Jeżeli wyprodukowanie zajmuje nam 40 sec, to musimy przetwarzać jednocześnie 40 takich kawałków na raz. Na szczęście dane są dostatecznie niezależne od siebie, aby można było takie zrównoleglanie przeprowadzić. Oczywiście, dochodzi do tego oprogramowanie zarządzające zadaniami tak, aby dwa procesy analizy nie przetwarzały tego samego, a jednocześnie przetwarzały dokładnie ten kawałek, który jest w danej chwili najważniejszy.

Warto jeszcze wspomnieć, że do zrównoleglania dochodzi także kwestia buforowania i zapisu wyników. Nie używamy systemu czasu rzeczywistego, więc to, że analiza zazwyczaj zajmuje czas X, wcale nie znaczy, że przetwarzanie będzie zajmować zawsze tyle. W związku z tym musimy gdzieś przychodzące dane zapisywać. Jeżeli nie ma kto (mam na myśli CPU) ich analizować. RAMu oczywiście nie wystarczy na sensowne bufory, więc do takich operacji potrzebny jest dysk twardy. Jednocześnie, gdy tylko jakaś jednostka przetwarzania się zwolni, chcemy, aby dane dla niej już były gotowe w RAMie, bez oczekiwania na odczyt z dysku. Spędziliśmy całkiem sporo czasu obserwując wykresy wydajności dysków twardych i prowadząc symulacje systemu tylko po to, aby dobrać polityki bufforowania, cachowania oraz konfigurację systemu RAID i sprostać wymaganiom. – mówi Marcin Grzebieluch.

Problemów, na jakie natrafili programiści Sii, było dość sporo. Ciekawym przykładem jest sposób, w jaki domyślnie system Linux obsługuje zapisy na dysk. Domyślnie, podczas zapisu i odczytu z dysku, Linux tworzy cache w pamięci i zabiera tyle RAMu, ile jest dostępne. Nie powoduje to problemu w przypadku czytania z dysku, bo gdy pamięć zajęta przez cache jest potrzebna, to wszystko, co zostało odczytane, można po prostu zwolnić i oddelegować pamięć do programu, który jej potrzebuje. Problem pojawia się w przypadku zapisu. Aplikacja prosi „jądro systemu” o zapisanie pamięci na dysk. System operacyjny zapisuje więc w RAMie to, co ma umieścić na dysku, i przekazuje sterowanie oprogramowaniu, aby nie blokować go dłużej niż jest to konieczne. W dalszej kolejności pałeczkę przejmuje dysk twardy komputera. Jego obsługa niestety jest nieco bardziej skomplikowana, przykładowo w dysku talerzowym (HDD) głowica może być w innym miejscu, niż byśmy sobie tego życzyli. W takim wypadku musimy poczekać na obrót talerza. Taka sytuacja powoduje pewne opóźnienie w stosunku do obsługi pamięci RAM. Co więcej, gdy dojdzie do sytuacji, w której cała pamięć operacyjna przeznaczona na dane do zapisu zostanie zużyta przez cache, następna alokacja pamięci w systemie będzie blokująca dopóki, dopóty jej zawartość nie zostanie trwale zapisana na dysku. Oznacza to, że alokacja przykładowo 1GB pamięci może nam zwolnić z 200ms do 5s.

Programistom udało się to rozwiązać poprzez zmianę maksymalnego rozmiaru cache do zapisu na dysku. W systemach tego typu zależy przede wszystkim na stabilności. Co z tego, że część danych obsłużymy bardzo szybko, skoro potem nastąpi moment, w którym zwolnimy 50-krotnie. Przypomina to stabilny frame rate w grach komputerowych. Lepiej wykonać działanie wolniej, ale stabilnie. Możemy wówczas oszacować czas trwania eksperymentu, nie tracąc danych przy pierwszej niespodziewanej sytuacji.

Jeśli mówimy o oprogramowaniu dla branży medycznej, to niewątpliwe prawdziwym wyzwaniem jest również aspekt testowania

Na wstępie warto dodać, że testowanie w projekcie medycznym jest częściowo uregulowane prawnie. To znaczy, że każde wymaganie, jakie jest stawiane przez Agencję Żywności i Leków, musi posiadać techniczny opis stwierdzający spełnienie wymagań oraz opis testu weryfikującego poprawność implementacji. Na zakończenie projektu wszystkie te testy muszą być przeprowadzone zgodnie z opisem.

Do czasu naszego projektu, testy zazwyczaj były przeprowadzane ręcznie przez testerów z QA.

Bardzo chcieliśmy uniknąć rosnącej listy testów manualnych i pisania dokumentacji do tych testów. Jak już wspomniałem wcześniej, nasz system składał się z wielu mikroserwisów, które realizowały poszczególne wymagania. W związku z tym stworzyliśmy framework testowy na nasze potrzeby w języku skryptowym, który pozwolił na testowanie całego systemu lub poszczególnych jego elementów w izolacji. Następnie powstało narzędzie, które potrafiło z tych testów i ich nazw wygenerować opis. W komentarzu jedynie musieliśmy zawrzeć odnośniki do dokumentacji dotyczącej funkcjonalności. W rezultacie developer tworzący daną funkcjonalność pisał do niej test, a zadaniem QA było sprawdzenie, czy wszystkie przypadki testowe zostały zaimplementowane i opisane – mówi Marcin Grzebieluch.

Jak widać, w ten sposób dokumentacja do testów niezbędnych do zakończania projektu jest zawsze aktualna i dostępna, a testerzy nie muszą poświęcać czasu na powtarzanie tych samych testów za każdym razem, gdy deweloperzy dokonają zmian. Oczywiście, trzeba tutaj dodać, że te testy nie eliminują potrzeby unit testów, ale znacząco ułatwiają pracę przy funkcjonalnościach, które muszą przechodzić przez więcej niż jeden serwis w systemie.

Ciekawy projekt to spore wyzwania, a te wszyscy lubimy najbardziej

Praca w takim projekcie to niewątpliwie wyzwanie, które dla wielu osób może być naprawdę bardzo interesujące. Programista nie mierzy się tu z odpowiednim zdefiniowaniem architektury oprogramowania czy zakodowaniem danej klasy/metody/funkcji, lecz z dostosowaniem dostępnych technologii do wymagań stawianych przez obowiązujące przepisy prawne oraz, rzecz jasna, przez klienta. Takimi projektami zajmują się między innymi deweloperzy pracujący w firmie Sii, która stawia na rozwój swoich pracowników. Jeśli chcielibyście dołączyć do zespołu, to zachęcam was do odwiedzenia strony sii.pl/oferty-pracy, na której znajdziecie aktualne oferty pracy. Jeśli macie natomiast jakieś pytania dotyczącego tego projektu lub chcielibyście poznać jego szczegóły, gorąco zachęcam do komentowania.

Materiał powstał przy współpracy z firmą Sii.

Przeczytaj również

, , , , , , , , , ,