Pomysł na przyspieszenie uruchamiania testów które korzystają z zasobów?

0

Piszę sobie parser, nie ważne czego, i mam około 1000 testów do tego parsera. Znakomita większość tych testów, jakieś 85-90% ładuje input do testów z zasobów, żeby trzymać pliki małe, przykładowy test może wyglądać jakoś tak:

test() {
  input = resource("resources/test1/test2/input.txt");
  output = parse(input)
  // jakieś asercje
  assert(output, resource("/resources/test1/test2/expected.txt"))
}

I zauważyłem, otwieranie plików faktycznie zajmuje dużą część czasu tych testów, chciałbym to jakoś przyspieszyć. Na pewno nie chcę wsadzać treści tych plików do kodu testów, bo stałyby się za duże.

Na razie pomysł jaki mam, to:

  1. Przerobić funkcje resource(), tak żeby przed odpaleniem testów przeleciała wszystkie pliki, wrzuciła je do jednego np w JSON'ie, i zapisala w jakimś pliku cache.
  2. Jak jakiś z plików się zmieni, to zapisze je od nowa (trzeba by pewnie jakąs checksume liczyć żeby porównać je)
  3. potem testy by wczytywały tylko ten jeden duży plik.

Pomysł jest taki że czytanie i parsowanie dużego pliku będzie szybsze niż otwieranie wielu malutenkich plików. Tylko to się rozbija o to że i tak żeby obliczyć checksume i tak je trzeba otworzyć :/

Drugi pomysł jaki miałem to funkcja która po prostu wczytuje pliki (być może równolegle) i trzyma je w mapie, i resource() by potem je podglądnął. Macie jakiś pomysł inny?

1

Możesz powiedzieć jaka technologia?

Jeśli chodzi o najprostsze rozwiązanie to pewnie zrobiłbym coś w stylu private static Future<string> fileContent = openInBackground(path). openInBackground od razu startuje akcję w tle (w nowym wątku). Przez to, że pola są statyczne to na start aplikacji wszystko nam się wczyta równolegle.

I jeszcze na koniec. Jak bardzo otwieranie plików spowalnia kod? W jaki sposób to zmierzyłeś?

0
slsy napisał(a):

Możesz powiedzieć jaka technologia?

A jakie to ma znaczenie? Implementacje sobie napiszę, ja pytam o sposób.

Jeśli chodzi o najprostsze rozwiązanie to pewnie zrobiłbym coś w stylu private static Future<string> fileContent = openInBackground(path). openInBackground od razu startuje akcję w tle (w nowym wątku). Przez to, że pola są statyczne to na start aplikacji wszystko nam się wczyta równolegle.

Czyli to sam co opisałem? Wczytać wartości na początku i trzymać w mapie/czymś dostępnym w testach?

I jeszcze na koniec. Jak bardzo otwieranie plików spowalnia kod? W jaki sposób to zmierzyłeś?

Profilerem. Na tyle znacząco że to zauważam i mi przeszkadza.

1

Odpal testy wielowatkowo.

0
WhiteLightning napisał(a):

Odpal testy wielowatkowo.

Dzieki za pomysł, ale ja pytałem jak przyspieszyć wczytywanie plików.

1

Mógłbyś też odpalać tylko część z testów jak jesteś w trakcie robienia, a pełne testy przy commicie.

2

Dysk w pamięci (ram disk). Możliwe, że znajdziesz biblioteki dla swojej technologii, które taki dysk pozwolą utworzyć programowo i wrzucić tam zasoby testowe.

1
yarel napisał(a):

Dysk w pamięci (ram disk).

jest jakaś komenda do tego, nie pamiętam jaka. To może?
https://en.wikipedia.org/wiki/Mmap

1

Emuluj pliki w pamięci, zmienią Ci się uprawnienia do katalogu i nie odpalisz testów.

0
LukeJL napisał(a):

Mógłbyś też odpalać tylko część z testów jak jesteś w trakcie robienia, a pełne testy przy commicie.

Nie, dziękuje, bardzo słaby pomysł, przynajmniej dla mnie.

0

A z ciekawości ile teraz u Ciebie trwa wczytanie tych plików? Zrobiłem właśnie test z ładowaniem 1000 plików w pętli w C# i przy rozmiarze pliku 1 kB ładują się 60 ms, a przy 1 MB 2,5 s.

2

Co to za system operacyjny? Co to za system plików? U mnie na Linuksie otwarcie 100k+ małych plików jeśli są w cache zajmuje pojedyncze sekundy. Jeśli nie są w cache to kilkanaście sekund (SSD). Pewien jesteś że Ci antywirus Windows nie wchodzi w drogę?

0
Krolik napisał(a):

Co to za system operacyjny? Co to za system plików? U mnie na Linuksie otwarcie 100k+ małych plików jeśli są w cache zajmuje pojedyncze sekundy. Jeśli nie są w cache to kilkanaście sekund (SSD). Pewien jesteś że Ci antywirus Windows nie wchodzi w drogę?

Dla testów jak się robi TDD to pojedyncze sekundy to dużo. Ja szukam czegoś rzędu 30-50ms.

1

Nie używać plików, wkleić zawartość do stringa w teście.

0
somekind napisał(a):

Nie używać plików, wkleić zawartość do stringa w teście.

Tylko że te stringi mają po 10-15 linijek. Na input i output to 20-30 linijek per test, a z plikami jeden test ma 3 linijki. Szkoda mi tak psuć.

2

Na moje oko, to skoro założyłeś wątek, to oznacza, że teraz masz coś zepsute.

Jak dla mnie, to nie ma znaczenia, ile linijek - plik to plik, ja testuję swój kod, a nie czytanie z plików.

0
somekind napisał(a):

Na moje oko, to skoro założyłeś wątek, to oznacza, że teraz masz coś zepsute.

No po prostu chciałbym przyspieszyć wczytywanie z pliku.

Jak dla mnie, to nie ma znaczenia, ile linijek - plik to plik, ja testuję swój kod, a nie czytanie z plików.

Ja też nie testuje czytania plików. Po prostu chcę mieć czyste i przejrzyste testy. Czasami mam 10 testów, które dostają bardzo podobny input, ale różnią się jakimś jednym znakiem, dlatego wolę napisać wiele małych testów, i gdzieś wsadzić te inputy. Mógłbym inputy trzymać w teście, ale mogę też w pliku.

Mam też kilka testów które przymują input na 100 linijek, i tego też nie zamierzam trzymać w kodzie źródłowym w testach tylko wynieść sobie gdzieś

0
Riddle napisał(a):
  1. Jak jakiś z plików się zmieni, to zapisze je od nowa (trzeba by pewnie jakąs checksume liczyć żeby porównać je)

Checksuma nie musi być potrzebna, bo da się watchować pliki. Np. jest taka biblioteka Chokidar, jeśli piszesz w JS https://github.com/paulmillr/chokidar

3
Riddle napisał(a):
Krolik napisał(a):

Co to za system operacyjny? Co to za system plików? U mnie na Linuksie otwarcie 100k+ małych plików jeśli są w cache zajmuje pojedyncze sekundy. Jeśli nie są w cache to kilkanaście sekund (SSD). Pewien jesteś że Ci antywirus Windows nie wchodzi w drogę?

Dla testów jak się robi TDD to pojedyncze sekundy to dużo. Ja szukam czegoś rzędu 30-50ms.

Ale te sekundy to zajmuje otwarcie wszystkich stukilkudziesięciu tysięcy plików, wczytanie ich zawartości, obliczenie hashy i zamknięcie. To jest kilkanaście, może kilkadziesiąt mikrosekund na plik. Natomiast sam dostęp do metadanych pliku to jakieś milion plików na sekundę. To ile tych testów macie że taka predkosc to byłby problem?

Oczywiście można szybciej. Można te dane, jeśli nie ma ich dużo, wrzucić do zasobów programu żeby były w binarce, wtedy system operacyjny przy ładowaniu binarki wczyta je tylko raz i będą zamapowane na pamięć i dostępne po prostu jako tablica bajtów. Można też je po prostu umieścić w segmencie danych jako stałe, których zawartość będzie wczytywana z plików w czasie kompilacji programu. Nie napisałeś jaki język, więc nie wiem czy masz taką możliwość.

Nie jestem jednak pewien czy w przypadku takim jak TDD taka zabawa ma sens, bo sumarycznie i tak wczytać te pliki musisz, tyle że przesuwasz to zadanie do czasu kompilacji. To by się opłacało jeśli dany plik wykorzystujesz wielokrotnie.

0
Krolik napisał(a):

Ale te sekundy to zajmuje otwarcie wszystkich stukilkudziesięciu tysięcy plików, wczytanie ich zawartości, obliczenie hashy i zamknięcie. To jest kilkanaście, może kilkadziesiąt mikrosekund na plik. Natomiast sam dostęp do metadanych pliku to jakieś milion plików na sekundę. To ile tych testów macie że taka predkosc to byłby problem?

No mi w profilerze wyszło że troche wiecej :/ Może coś źle policzyłem

1
Riddle napisał(a):
somekind napisał(a):

Nie używać plików, wkleić zawartość do stringa w teście.

Tylko że te stringi mają po 10-15 linijek. Na input i output to 20-30 linijek per test, a z plikami jeden test ma 3 linijki. Szkoda mi tak psuć.

Zawsze można te dane trzymać w kodzie źródłowym, ale poza testem i będzie dokładnie tak samo jak z plikami tj. w obu przypadkach przygotowanie sekcji arrange jest poza ciałem testu.
Przykładowo:

[test_identifier np A4jxbC] <-- żeby nie wiązać po nazwie testu
test_case()
{
    expectedResult = parse_data.A4jxbC.expectedOutput;

    actualResult = sut.Parse(parse_data.A4jxbC.input);

    actualResult == expectedResult otherwise not pass
}

static class parse_data
{
    static class test_identifier
    {
        const string input;
        const string expectedOutput;
    }
    ..
    ..
}
2

Podmontowanie tmpfs na pewno trochę przyspieszy, dla wielokrotnego odczytu.
A jak nie przyspieszy to dużo czasu sprawdzenie tego nie zajmie.

mount -t tmpfs -o size=500m tmpfs /ramdisk

Teraz jak wrzucisz pliki do /ramdisk to wszystkie będą raz wczytane z dysku i będą przechowywane jako pliki w ramie.

Potem możesz symbolik link zrobić, o nazwie resources -> ramdisk, żeby w testach nie trzeba było zmieniać nazwy katalogu.
ln -s /ramdisk resources

Pliki tylko raz będą wczytane z dysku i zapisane w dysku w ramie, przy pierwszym kopiowaniu i dopóki nie odmontujesz tymczasowego file systemu, to pliki będą sobie siedzieć w ramie.

Oczywiście w czasie pracy przyspieszy to testowanie po jakichś modyfikacjach, jeśli dużo razy będą wykonywane testy, tak jeśli dopalone jeden raz to chyba się nic nie zmieni bo i tak będzie musiało być przynajmniej raz odczytane z dysku.
Raczej do developmentu.

0

albo po prostu ładować pliki do pamięci, a później odpalając testy od nowa nie odpalając procesu od nowa, ale raczej odpalać same przypadki testowe od nowa w tym samym uruchomionym już procesie, który ma już załadowane pliki do pamięci

Przy czym pytanie, czy framework testów na to pozwoli, ale myślę, że można pokombinować.

Jakbyś pisał konkretne technologie, z których używasz, to łatwiej byłoby coś wymyslec

0

z tego co pamietam to nie jest takie proste do zrobienia w junit (wywolaj jeden serwis przed wszystkimi testami populajacy pamiec z danymi testowymi i potem reuzywaj te dane testowe miedzy wszystkimi test classes)

0
Hedgehog.Follower napisał(a):
Riddle napisał(a):
somekind napisał(a):

Nie używać plików, wkleić zawartość do stringa w teście.

Tylko że te stringi mają po 10-15 linijek. Na input i output to 20-30 linijek per test, a z plikami jeden test ma 3 linijki. Szkoda mi tak psuć.

Zawsze można te dane trzymać w kodzie źródłowym, ale poza testem i będzie dokładnie tak samo jak z plikami tj. w obu przypadkach przygotowanie sekcji arrange jest poza ciałem testu.
Przykładowo:

Tak, to jest prawda, kiedyś tak robiłem, i doszedłem w pewnym momencie do wniosku że to nadal nie są tak "czyste" testy jakbym chciał. Ciężej wtedy rozróżnić taki funkcyjny fixture testów od po prostu inputów. Przy okazji członkowie teamu maja tendencję do bardziej "swawolnej" edycji tych danych i psucia testów :/

LukeJL napisał(a):

albo po prostu ładować pliki do pamięci, a później odpalając testy od nowa nie odpalając procesu od nowa, ale raczej odpalać same przypadki testowe od nowa w tym samym uruchomionym już procesie, który ma już załadowane pliki do pamięci

To jest w sumie spoko pomysł, mógłbym spróbować to zrobić

Jakbyś pisał konkretne technologie, z których używasz, to łatwiej byłoby coś wymyslec

Specjalnie nie napisałem, bo zaraz by się znalazły dziwne odpowiedzi frameworko-specific. A jak dostanę pomysł to sobie go napiszę.

0

Napisz rozszerzenie do kompilatora PHP który podmieni w testach wywołanie metody resource z stringiem jako arg na wywołanie metody resource z stringiem o wartości z pliku spod ścieżki z oryginalnym stringiem.

Efektywnie nic to nie zmieni, pewnie nawet będzie wolniej, ale who knows.


A może trzymaj te wszystkie teksty w jednym pliku i wczytuj efektywnie tylko 1 plik przed testami i zrób z niego jakąś kolekcje tych tekstów w pamięci :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1