PostgreSQL / EDB

Wyrównywanie wydajności CPU w Proxmox. Metodologia testów na heterogenicznym klastrze

2026-09-07
Podziel się

Testowanie systemów bazodanowych na heterogenicznym klastrze często fałszuje wyniki z powodu różnic w surowej mocy maszyn. W tym artykule obalamy mit, że ujednolicenie flag instrukcji CPU pozwala zrównać wydajność generacji sprzętu. Pokazujemy, jak za pomocą parametru cpulimit w Proxmox VE oraz benchmarków sysbench i stress-ng sztucznie stłumić potencjał nowszego procesora AMD Ryzen (Zen 4), zrównując go ze starszą jednostką (Zen 3) z dokładnością do 1.5%. Omawiamy również kluczowe ryzyko tej metody – jitter (throttling), który może zniekształcić metryki opóźnień ogonowych (p95/p99) w testach baz danych.

W idealnym świecie infrastruktury IT, testy wydajnościowe systemów bazodanowych przeprowadzane są na identycznym sprzęcie. Pozwala to na stworzenie kontrolowanego środowiska laboratoryjnego, w którym jedyną zmienną jest badana architektura oprogramowania (np. wersja bazy danych, konfiguracja puli połączeń czy topologia sieciowa). Niestety, rzeczywistość projektowa rzadko bywa tak komfortowa.

Firmy często dysponują środowiskami heterogenicznymi – klastrami zbudowanymi z serwerów pochodzących z różnych lat, wyposażonymi w procesory różnych generacji. Kiedy decydujemy się na przeprowadzenie testów porównawczych (A/B testing) architektury na takim „mieszanym” sprzęcie, wkraczamy na pole minowe.

Spis treści:

Dlaczego sprzęt fałszuje wyniki?

Wyobraźmy sobie próbę udowodnienia, że rozdzielenie bazy danych i serwera aplikacji na dwie osobne maszyny przynosi wymierne korzyści. Jeżeli serwer aplikacyjny umieścimy na nowoczesnej maszynie, a bazę danych na starszym, wyeksploatowanym serwerze, wyniki testów będą obarczone pewnym błędem poznawczym.

Spadki wydajności mogą wynikać z samej architektury rozproszonej (narzut sieciowy), ale mogą być również bezpośrednią konsekwencją słabego procesora obsługującego warstwę danych. Testowanie „jabłek z pomarańczami” prowadzi do błędnych wniosków biznesowych, na podstawie których podejmuje się nietrafione decyzje o inwestycjach w sprzęt lub zmianach w kodzie.

Cel artykułu

Aby nasze testy architektury PostgreSQL opisane w poprzednim artykule dotyczącym zjawiska „Hałaśliwego sąsiada” były w 100% rzetelne i miarodajne, musieliśmy zmierzyć się z problemem dysproporcji sprzętowej. Dysponowaliśmy klastrem Proxmox złożonym z maszyn napędzanych procesorami AMD Ryzen o różnych architekturach (Zen 3 vs Zen 4) i różnym potencjale obliczeniowym.

W tym artykule pokażemy, jak za pomocą wbudowanych narzędzi hypervisora (Proxmox) oraz syntetycznych benchmarków (sysbench, stress-ng) dokonaliśmy „wyrównania szans”. Opiszemy proces sztucznego, ale precyzyjnego ograniczenia wydajności nowszego procesora, tak aby w środowisku wirtualnym zachowywał się identycznie jak jego starszy odpowiednik, tworząc tym samym idealnie równe pole gry dla testów bazodanowych.

Kluczowe założenia i ograniczenia

Węzeł 1 (Ryzen 8745HS) był znacznie nowocześniejszy. Obliczenia i próby metodą prób i błędów wykazały, że pozostawienie mu pełnej swobody dawałoby mu znaczącą przewagę. Użyliśmy parametru cpulimit, aby sztucznie stłumić jego potencjał:

4(1 sockets, 4 cores) [host][vcpus=4][cpulimit=1.8][cpuunits=1024]

Zanim przejdziemy do szczegółów technicznych, należy wyraźnie zaznaczyć granice prezentowanego podejścia. Metoda ta nie jest „złotym środkiem” na wszelkie problemy z heterogenicznym sprzętem i posiada dwa fundamentalne ograniczenia:

  • Fokus na CPU: świadomie pominęliśmy analizę wpływu różnic w podsystemach RAM (np. opóźnienia i przepustowość w zjawisku DDR4 vs DDR5), przestrzeni dyskowej (I/O) oraz interfejsach sieciowych. Skupiliśmy się wyłącznie na wyrównaniu mocy obliczeniowej procesora.
  • Ryzyko Jittera: prezentowany mechanizm ogranicza średnią przepustowość maszyny wirtualnej, wysterowując jej dostęp do fizycznego procesora. Jednak cykliczne odbieranie i przydzielanie zasobów przez jądro Linux (throttling) może negatywnie wpływać na stabilność opóźnień (tzw. jitter). Jest to kluczowe zjawisko dla środowisk bazodanowych, gdzie nagłe skoki opóźnień (piki latencji p95/p99) są bardziej dotkliwe niż niższa średnia przepustowość.

Wyzwanie sprzętowe: nowy rywalizuje ze starym

Projektując nasze środowisko testowe w klastrze Proxmox, napotkaliśmy na fundamentalny problem: dysproporcję mocy obliczeniowej dostępnych serwerów fizycznych. Aby testy w architekturze „Remote” (baza danych na osobnej maszynie, aplikacja na innej) miały sens, obie maszyny wirtualne musiały oferować identyczną wydajność procesora dla swoich systemów operacyjnych gościa.

Tymczasem nasze serwery prezentowały zupełnie inne generacje i architektury procesorów firmy AMD.

Dysproporcja na papierze

Porównując specyfikację techniczną węzłów:

  • Węzeł 1 (Maszyna „pgTest1”, .26): napędzany przez nowoczesny układ mobilny AMD Ryzen 7 8745HS. Zbudowany w oparciu o architekturę Zen 4, wyprodukowany w litografii 4nm, sparowany z szybką pamięcią 32 GB RAM DDR5. Według syntetycznych benchmarków (porównanie obu procesorów w serwisie cpubenchmark.net), osiąga on wynik 3736 pkt w teście jednordzeniowym (Single Thread) i aż 28815 pkt w wielowątkowym (CPU Mark).
  • Węzeł 2 (Maszyna „pgTest2”, .27): wyposażony w starszy, energooszczędny procesor AMD Ryzen 7 5825U. To architektura Zen 3, starszy proces technologiczny 7nm i wolniejsza pamięć 64 GB RAM DDR4. Jego wyniki to 3016 pkt (Single Thread) oraz 17931 pkt (CPU Mark).

Rozstrzał wydajnościowy jest zatem spory – nowszy procesor jest o prawie 24% szybszy w zadaniach jednowątkowych i ponad 60% szybszy w całkowitej mocy obliczeniowej. Jeżeli uruchomilibyśmy na nich obciążające testy bazy danych z wykorzystaniem surowej mocy, maszyna .26 zawsze byłaby faworytem, niezależnie od przyjętej architektury komunikacji (Local vs Remote).

Ujednolicenie warstwy logicznej

Zanim przystąpiliśmy do „osłabiania” nowszego sprzętu. Upewniliśmy się, że na poziomie logicznym (hypervisora i systemu gościa) obie maszyny wirtualne są całkowicie symetryczne. W Proxmox przygotowano kopię bazowej maszyny z następującymi ustawieniami:

  • system operacyjny: Rocky Linux 9.6
  • procesor: przypisano 4 wirtualne rdzenie, w trybie emulacji Type: host (aby system gościa miał bezpośredni dostęp do pełnego zestawu instrukcji procesora gospodarza, a nie używał ogólnych sterowników qemu)
  • pamięć RAM: 8096 MB
  • dysk: 32 GB (local SSD)
  • typ maszyny: q35.

Jednak takie ustawienie (przypisanie 4 rdzeni host) spowodowałoby, że 4 rdzenie nowszego Zen 4 byłyby szybsze w testach niż 4 rdzenie starszego Zen 3. Trzeba było sięgnąć po bardziej zaawansowane narzędzia Proxmox.

Narzędzia hypervisora: Jak zrównać „Ferrari” z „Polonezem”?

Mając zdefiniowaną różnicę w potencjale obliczeniowym serwerów fizycznych, musieliśmy na poziomie hypervisora (Proxmox VE) stworzyć mechanizm kompensujący tę dysproporcję. Problem ten rozwiązuje się za pomocą odpowiedniego wysterowania zasobów poprzez parametry ograniczające czas dostępu do procesora dla danej maszyny wirtualnej (Cgroups w systemie Linux pod spodem).

Zastosowaliśmy wirtualne ograniczniki (limitery), tak aby nowsza, szybsza maszyna (węzeł 1, .26) nie mogła pracować z pełną prędkością, sztucznie zrównując ją wydajnościowo ze starszą jednostką (węzeł 2, .27).

Użyte parametry konfiguracyjne CPU w Proxmox

Ważne rozróżnienie: w tym artykule, używając terminu cpulimit, odnosimy się wyłącznie do parametru konfiguracyjnego maszyny wirtualnej w środowisku Proxmox VE. Mechanizm ten działa na poziomie hypervisora (wykorzystując cgroups jądra Linux), płynnie wysterowując dostęp wirtualnego procesora do cykli procesora fizycznego. Nie należy mylić go z popularnym linuksowym narzędziem wiersza poleceń o tej samej nazwie (cpulimit z przestrzeni użytkownika), które sztucznie „dławi” procesy poprzez cykliczne wysyłanie sygnałów SIGSTOP i SIGCONT. To drugie narzędzie nie nadaje się do stabilnego benchmarkowania baz danych, gdyż drastycznie zaburza latencję (tworzy tzw. jitter).

Środowisko testowe: poniższe mechanizmy i konfiguracje były testowane i weryfikowane na platformie Proxmox VE w wersji 9.1.4, działającej pod kontrolą jądra Linux w wersji 6.17.4-2-pve. Należy mieć na uwadze, że implementacja i precyzja działania mechanizmów cgroups może się nieznacznie różnić w zależności od używanej wersji kernela.

Proxmox pozwala precyzyjnie modelować zachowanie wirtualnych procesorów (vCPU). Do sterowania wydajnością na poziomie jądra użyliśmy dwóch mechanizmów:

  • cpuunits (CPU Weight): jest to względna waga przydzielana maszynie wirtualnej w sytuacji, gdy na hoście toczy się walka o zasoby. W naszej wersji proxmox domyślna wartość wynosi 100. Dla celów testu została ona ustawiona dla obu maszyn na wartość 1024, co oznacza, że wybrane maszyny przy pełnym obciążeniu dostaną ponad 10 razy więcej czasu procesora niż inne maszyny. W naszym przypadku (gdzie na hostach uruchomiona była tylko jedna „ciężka” maszyna wirtualna, a reszta była wyłączona lub ich aktywność była znikoma), parametr ten ma mniejsze znaczenie, ale dla pewności został tak ustawiony dla obu węzłów. W scenariuszu, gdzie na hoście działa tylko jedna obciążająca maszyna wirtualna, zmiana tego parametru nie będzie miała żadnego wpływu na jej wydajność;
  • cpulimit (CPU Limit): to twardy, bezwzględny limit określający, ile czasu procesora maszyna wirtualna może w ogóle zużyć, niezależnie od tego, czy host ma wolne zasoby, czy nie. Limit 1.0 oznacza równowartość jednego w pełni obciążonego rdzenia. Limit 4.0 pozwala wykorzystać pełne 4 rdzenie.

Konfiguracja węzła starszego (Maszyna .27)

Na starszym serwerze (Ryzen 5825U) zależało nam, aby maszyna wirtualna mogła oddychać pełną piersią, uzyskując maksymalną możliwą wydajność, na jaką pozwalał sprzęt. Skonfigurowaliśmy ją bez nałożonych sztucznych barier:

unlim 4(1 sockets, 4 cores) [host][vcpus=4][cpuunits=1024]

Oznacza to, że system wewnątrz VM .27 mógł obciążyć procesor fizyczny na poziomie odpowiadającym pełnym 4 rdzeniom (brak zdefiniowanego cpulimit).

Konfiguracja węzła szybszego (Maszyna .26)

Węzeł 1 (Ryzen 8745HS) był znacznie nowocześniejszy. Obliczenia i próby metodą prób i błędów wykazały, że pozostawienie mu pełnej swobody dawałoby mu znaczącą przewagę. Użyliśmy parametru cpulimit, aby sztucznie stłumić jego potencjał:

4(1 sockets, 4 cores) [host][vcpus=4][cpulimit=1.8][cpuunits=1024]

Zastosowany limit cpulimit=1.8 jest kluczowy w tej architekturze badawczej. Oznacza to, że pomimo przypisania maszynie 4 logicznych rdzeni (vCPUs), hypervisor nałożył twardą blokadę na poziomie kernela: maszyna ta nie może w żadnym momencie skonsumować sumarycznie więcej czasu obliczeniowego, niż wynosi wydajność 1.8 rdzenia fizycznego.

Choć system operacyjny Rocky 9.6 „widzi” wewnątrz maszyn 4 pracujące procesory i poprawnie na nich harmonogramuje 4 wątki narzędzi testowych, jądro Proxmoxa w tle „dusi” ich aktywność tak, by nigdy nie przekroczyły ustalonego progu wydajności 1.8x pojedynczego rdzenia archtektury Zen 4.

Podsumowanie ujednolicenia sprzętowego

W ten sposób wygenerowaliśmy zrównoważone środowisko, minimalizując błąd pomiarowy wynikający z niejednorodności sprzętu, tworząc fundament pod rzetelne testy wydajności PostgreSQL, w których jedyną realną zmienną pozostaje architektura oprogramowania (połączenia po złączach lokalnych kontra obciążona sieć Pgbouncer). Działanie tej matematyki musieliśmy jednak potwierdzić narzędziami syntetycznymi. Jak jednak doszliśmy do tej optymalnej, ułamkowej wartości 1.8?

Empiryczny proces poszukiwania wartości limitu

Zamiast polegać na suchych obliczeniach wydajności i częstotliwości taktowania (co w środowiskach wirtualnych często bywa zwodnicze ze względu na mechanizmy turbo-boost, architekturę NUMA czy różnice w pamięci Cache), zdecydowaliśmy się na iteracyjne i w pełni empiryczne znalezienie właściwej granicy.

Wykorzystaliśmy autorski skrypt set_proxmox.sh, który rekonfigurował maszyny „w locie”, a następnie przeprowadzaliśmy syntetyczne testy weryfikujące za pomocą sysbench. Oprócz szukania sztywnego limitu, postanowiliśmy dodatkowo obalić pewien mit: czy samo sztuczne ujednolicenie „typu” procesora zrzucające zestaw instrukcji do starszej bazy x86, bez nałożenia twardego kagańca cpulimit, zrówna wydajność obu maszyn?

Zdefiniowaliśmy następujący plan iteracji. W każdym z testów modyfikowano parametry wyłącznie nowszej, mocniejszej maszyny (.26), podczas gdy słabsza (.27) zawsze pracowała na maksymalnych obrotach bez obostrzeń:

id_test cpulimit na .26 liczba vcpu rodzaj vcpu Cel iteracji
01 brak (0) 4 host Ustalenie punktu odniesienia i surowej przewagi nowej maszyny.
02 3.0 4 host Pierwsza próba łagodnego ograniczenia nowszej maszyny.
03 2.0 4 host Dalsze dławienie (ucina 50% mocy).
04 1.5 4 host Ekstremalne dławienie (zejście poniżej potencjału starej maszyny).
05 1.8 4 host Poszukiwanie „złotego środka” między próbą 03 a 04.
06, 07 1.8 (błąd) 4 x86-64-v2/v3 Błędne iteracje – niewyzerowanie limitu z testu 05 przed zmianą typu CPU.
08 brak (0) 4 x86-64-v2-AES Poprawny test hipotezy ujednolicenia warstwy instrukcji (poziom v2).
09 brak (0) 4 x86-64-v3 Poprawny test hipotezy ujednolicenia warstwy instrukcji (poziom v3).

W kolejnym rozdziale spojrzymy na szczegółowe wyniki tego procesu i odpowiemy na pytanie, czy ucięcie zestawu instrukcji procesora faktycznie spowalnia go do poziomu słabszej jednostki (i dlaczego w testach 06 i 07 natrafiliśmy na błąd w testach).

Dowód skuteczności: weryfikacja syntetyczna z sysbench

Konfiguracja hipernadzorcy (Hypervisora) to jedno, ale jej faktyczny wpływ na system gościa w maszynie wirtualnej to drugie. Należało uzyskać twarde, empiryczne dowody na to, że ograniczenie cpulimit=1.8 dla maszyny .26 (Ryzen 8745HS) wyrównało jej wydajność z maszyną .27 (Ryzen 5825U) pracującą bez limitów.

Do tego zadania wykorzystaliśmy popularne narzędzie sysbench, służące do syntetycznego obciążania wybranych podsystemów maszyny (w naszym przypadku procesora).

Pułapka testów jednowątkowych

Pierwszym odruchem było uruchomienie standardowego testu sysbench cpu run. Wyniki jednak szybko pokazały fundamentalny błąd w naszym rozumowaniu.

Domyślnie sysbench uruchamia test na jednym wątku. Z perspektywy hipernadzorcy (Proxmox), jeden maksymalnie obciążony wątek konsumuje zasoby na poziomie „1.0” (jeden pełny rdzeń logiczny). Ponieważ nasz limit na maszynie .26 wynosił 1.8, test jednowątkowy mieścił się z zapasem w przypisanym limicie, w ogóle go nie „dotykając”. Limit cpulimit=1.8 działa jako „pula” mocy do podziału między wszystkie vCPU przypisane do maszyny wirtualnej. Kiedy pracuje tylko jeden wątek, konsumuje on maksymalnie moc równą 1.0, a więc pula nie ulega wyczerpaniu i scheduler hypervisora nie musi interweniować, pozwalając temu procesowi działać z pełną prędkością nowszej architektury. Dopiero gdy obciążymy wiele wątków, ich sumaryczne zapotrzebowanie przekroczy ułamek 1.8, wymuszając sprzętowe dławienie.

W efekcie maszyna .26, korzystając z lepszej architektury Zen 4, z łatwością wygrywała z maszyną .27, ponieważ ograniczenie hypervisora pozostawało uśpione, a test badał jedynie wydajność pojedynczego rdzenia (IPC).

Prawidłowa metodologia: obciążenie wszystkich vCPU

Aby zmusić limit hypervisora do zadziałania, musieliśmy wygenerować obciążenie rozłożone na wszystkie przypisane rdzenie wirtualne, zmuszając maszynę gościa do zażądania od hosta więcej niż dopuszczalne „1.8” mocy procesora fizycznego.

Zastosowaliśmy odpowiednio zmodyfikowaną komendę: sysbench cpu --threads=4 run

Użycie flagi --threads=4 odpowiada liczbie skonfigurowanych wirtualnych rdzeni (vCPUs).

Wyniki: przebieg testów i obalenie mitów

Zamiast zgadywać, uruchomiliśmy pełen zestaw testów zadeklarowanych w poprzednim rozdziale. Sprawdzaliśmy wydajność maszyny nowszej i starszej w wariancie 1, 2 oraz 4 wątków (wyniki w events per second). Poniższa tabela przedstawia całościowe podsumowanie z poszukiwań idealnego ustawienia z wyszczególnieniem wyników dla maksymalnego obciążenia (4 wątki):

id_test Konfiguracja VM .26 Wątki (sysbench) Host .26 (Ryzen 8745HS) Host .27 (Ryzen 5825U) Różnica wydajności
01 cpulimit=0, cpu=host 1 5973.29 2663.32 .26 jest o 124% szybszy (1 wątek)
01 cpulimit=0, cpu=host 2 11878.52 5345.97 .26 jest o 122% szybszy (2 wątki)
01 cpulimit=0, cpu=host 4 23471.57 10731.79 .26 jest o 118% szybszy (4 wątki)
02 cpulimit=3.0, cpu=host 1 5971.43 2668.87 .26 jest o 123% szybszy
02 cpulimit=3.0, cpu=host 2 11893.94 5335.64 .26 jest o 122% szybszy
02 cpulimit=3.0, cpu=host 4 17588.20 10713.43 .26 wciąż za szybki (+64%)
03 cpulimit=2.0, cpu=host 1 5994.92 2664.89 .26 jest o 124% szybszy
03 cpulimit=2.0, cpu=host 2 11812.35 5334.61 .26 jest o 121% szybszy
03 cpulimit=2.0, cpu=host 4 11732.64 10738.21 .26 minimalnie szybszy (+9%)
04 cpulimit=1.5, cpu=host 1 5976.75 2671.14 .26 jest o 123% szybszy
04 cpulimit=1.5, cpu=host 2 8774.09 5345.59 .26 jest o 64% szybszy (limit dławi 2 wątki)
04 cpulimit=1.5, cpu=host 4 8789.57 10719.61 Przeszacowanie, .26 jest wolniejszy (-18%)
05 cpulimit=1.8, cpu=host 1 5971.53 2671.08 .26 jest o 123% szybszy
05 cpulimit=1.8, cpu=host 2 10548.28 5347.52 .26 jest o 97% szybszy (limit dławi 2 wątki)
05 cpulimit=1.8, cpu=host 4 10583.72 10744.49 Idealny kompromis (-1.5%)
06 cpulimit=1.8, cpu=x86-v2 1 5994.83 2665.67 BŁĄD W TESTACH: Niewyzerowany limit
06 cpulimit=1.8, cpu=x86-v2 2 10515.64 5341.54 BŁĄD W TESTACH: Niewyzerowany limit
06 cpulimit=1.8, cpu=x86-v2 4 10503.35 10729.20 BŁĄD W TESTACH: Niewyzerowany limit
07 cpulimit=1.8, cpu=x86-v3 1 5976.78 2670.85 BŁĄD W TESTACH: Niewyzerowany limit
07 cpulimit=1.8, cpu=x86-v3 2 10560.05 5353.52 BŁĄD W TESTACH: Niewyzerowany limit
07 cpulimit=1.8, cpu=x86-v3 4 10569.24 10759.99 BŁĄD W TESTACH: Niewyzerowany limit
08 cpulimit=0, cpu=x86-v2 1 5970.07 2667.46 .26 jest o 123% szybszy
08 cpulimit=0, cpu=x86-v2 2 11861.28 5357.79 .26 jest o 121% szybszy
08 cpulimit=0, cpu=x86-v2 4 23375.53 10733.43 Brak zrównania (+117%)
09 cpulimit=0, cpu=x86-v3 1 5997.36 2674.81 .26 jest o 124% szybszy
09 cpulimit=0, cpu=x86-v3 2 11878.39 5375.57 .26 jest o 120% szybszy
09 cpulimit=0, cpu=x86-v3 4 23413.95 10760.97 Brak zrównania (+117%)

Wnioski z procesu dostrajania

Analiza powyższych danych prowadzi do dwóch fundamentalnych wniosków:

  • Działanie CPULimit a testy jednowątkowe: test 01 wyraźnie uwidacznia, że przy obciążeniu jednego wątku maszyna nowsza wyciąga niemal 6000 events/s (względem 2660 events/s na starszej). Ograniczenie cpulimit=1.8 nałożone na całą wirtualną maszynę w ogóle nie blokuje operacji jednowątkowych (zużycie wynosi wtedy maksymalnie 1.0, co bezpiecznie mieści się w limicie 1.8). Przez to w testach z jednym wątkiem obie maszyny wciąż znacząco się od siebie różnią. Limit dławi moc całego serwera dopiero przy wielowątkowym wysyceniu (4 wątki), zrównując sumaryczną przepustowość procesorów do perfekcyjnego poziomu (Test 05). Różnica między maszyną ograniczoną a pracującą natywnie spada do około 1.5%, co czyni to środowisko wiarygodnym podłożem do miarodajnych testów obciążeniowych architektury baz danych.
  • Ułuda ujednolicania flag instrukcji (Testy 08 i 09): powszechnym mitem jest przekonanie, że zmiana typu wirtualnego procesora z host na ograniczoną pulę instrukcji (np. bazowe x86-64-v2-AES lub x86-64-v3) zniweluje przewagę nowszej architektury, wymuszając na nowszym sprzęcie zrównanie ze starym. W pierwszych podejściach do tych testów (iteracje 06 i 07) popełniliśmy klasyczny błąd metodyczny: zmieniliśmy typ procesora, ale zapomnieliśmy zresetować parametr cpulimit=1.8 nałożony w teście 05. Wyniki były „idealne”, jednak po wyzerowaniu limitu i przeprowadzeniu poprawnych prób (Testy 08 i 09) ukazała się brutalna prawda. Udowadniają one, że założenie o wyrównaniu wydajności samym zestawem instrukcji jest całkowicie fałszywe. Nawet „zmuszając” Zen 4 do korzystania z uboższego zestawu komend, w czystych obliczeniach procesor ten nadal wykonuje je ponad dwukrotnie szybciej. Wynika to z jego niezmienionej, nowocześniejszej budowy – fizycznie dysponuje lepszą mikroarchitekturą rdzenia, pojemniejszym cachem L3 oraz znacznie wyższym taktowaniem zegara bazowego i trybu Turbo, niezależnie od tego, jakim „nazewnictwem” instrukcji każe mu posługiwać się Hypervisor. Użycie parametru cpulimit jest więc tu jedyną fizyczną gwarancją zahamowania nadmiernej prędkości sprzętu z nowszej generacji.

Weryfikacja obciążeniowa w czasie (stress-ng)

Benchmarki syntetyczne takie jak sysbench są doskonałe do sprawdzania szczytowej wydajności w krótkich interwałach. Chcieliśmy jednak upewnić się, że narzucony przez Proxmox limit cpulimit=1.8 nie powoduje „dławienia” się maszyny w dłuższym oknie czasowym, zwłaszcza przy symulowaniu pracy ciągłej.

Do sprawdzenia, jak wirtualne ograniczniki (Cgroups) radzą sobie ze stabilnym, stałym obciążeniem, wykorzystaliśmy narzędzie stress-ng.

Test długotrwałego obciążenia

Zleciliśmy maszynom wirtualnym zadanie polegające na symulowaniu zachowania aplikacji zajmującej równe 50% czasu każdego z przypisanych rdzeni logicznych. Obciążenie to było aktywne przez czas potrzebny na wykonanie testów sysbench. Użyliśmy w tym celu parametru sterującego algorytmami utylizującymi procesor w stress-ng:

stress-ng --cpu 4 --cpu-load 50

Zastosowanie tego kompromisowego profilu obciążeniowego pozwala ujawnić potencjalne fluktuacje wydajności na poziomie mechanizmu zarządzania przydziałem czasu procesora (throttlingu) implementowanego przez jądro Linux hosta fizycznego.

Stabilność w czasie

Poniższa tabela zbiera informacje zwrotne pochodzące z logów metryk operacyjnych dostarczonych przez stress-ng:

Maszyna Architektura Konfiguracja Bogo Ops (Wydajność operacji)
.26 Zen 4 cpulimit=1.8 5238
.27 Zen 3 unlim 5337

Podobnie jak w szybkich testach sysbench, maszyna .26 (nowoczesna) była nieznacznie wolniejsza (około 1.8% różnicy). W kontekście testów środowisk bazodanowych takie uchybienie należy uznać za błąd pomiarowy. Zwłaszcza w porównaniu do początkowej, ponad 100-procentowej przewagi wydajnościowej, uzyskana różnica na poziomie błędu statystycznego potwierdza skuteczność zastosowanej metody. Limit systemowy utrzymał wydajność nowego procesora w „ryzach” niezależnie od upływającego czasu. Ograniczenie CPU w Proxmox zostało wdrożone prawidłowo pod kątem średniej przepustowości, utrzymując zadany poziom pomimo długotrwałego testu.

Należy jednak z całą mocą podkreślić, że wyrównanie średniej przepustowości nie gwarantuje wyrównania charakterystyki opóźnień (tzw. jittera). Mechanizmy dławienia (throttlingu) implementowane przez cgroups interweniują cyklicznie, odbierając procesowi dostęp do rdzenia fizycznego. Może to wprowadzać mikrosekundowe, a nawet milisekundowe opóźnienia z perspektywy systemu operacyjnego gościa. W testach baz danych wysoce wrażliwych na opóźnienia, maszyna wirtualna z nałożonym sztucznym limitem wciąż może wykazywać gorszą i bardziej niestabilną latencję ogonową (p95/p99) niż naturalnie słabsza maszyna pracująca bez jakichkolwiek obostrzeń na poziomie hypervisora.

Wnioski i dobre praktyki

Stworzenie wiarygodnego heterogenicznego klastra testowego dla testów architektury baz danych jest wyzwaniem inżynieryjnym. Sukces uzależniony jest od znalezienia najsłabszego ogniwa infrastruktury (w naszym ujęciu starszy procesor Ryzen 5825U) i sprawiedliwego zrównania w dół reszty ekosystemu do wyznaczonego standardu. Dzięki wykorzystaniu właściwości wbudowanych w nowoczesne systemy wirtualizacji i Hypervisory (Proxmox VE, oparte na KVM), byliśmy w stanie powołać do życia środowisko o pewnej spójności wydajnościowej.

Złota zasada weryfikacji syntetycznej

Dowiedliśmy, że samo ustawienie proporcji sprzętowych i zaufanie „dokumentacji hypervisora” to ślepa uliczka. Twarde ograniczenie nałożone na system gościa (cpulimit) należy skrupulatnie zweryfikować za pomocą zaufanych, branżowych benchmarków (np. sysbench, stress-ng), dbając przy tym o odpowiednią parametryzację testów (wykorzystanie wielu wątków do pełnego wysterowania zablokowanych rdzeni wirtualnych).

Świadomość tych mechanizmów i narzędzi, połączona z odpowiednim procesem weryfikacji powtarzalności, pozwala na projektowanie miarodajnych i bezstronnych testów środowisk i architektury IT bez konieczności ponoszenia potężnych inwestycji na jednorodny park sprzętowy w obszarze serwerowni wdrożeniowych.

Prezentowana metoda jest wysoce skuteczna i zalecana do testów porównawczych zorientowanych na maksymalną przepustowość (throughput-bound workloads). Jednakże w przypadku testów architektury dla systemów wrażliwych na opóźnienia (latency-sensitive workloads), jak np. systemy transakcyjne OLTP, należy podchodzić do niej z dużą ostrożnością. Generowany przez mechanizmy cgroups jitter może zafałszować wyniki metryk p95/p99, prowadząc do błędnych wniosków co do stabilności czasowej testowanej bazy danych.

Zobacz również