Postawiłem emdashcms.pl i emdash.pl, zrobiłem 4 732 pomiarów — WordPress wygrał 6,4×

Ten portal (emdashcms.pl) i bliźniacza strona na WordPressie (emdash.pl) były głównymi obiektami mojego benchmarku — 4 732 pomiarów na 13 stronach przez 3,5 doby. WordPress okazał się 6,4× szybszy od EmDasha na czystym czasie przetwarzania. Tłumaczę uczciwie dlaczego, co próbowałem naprawić i co to znaczy dla EmDasha.

Pełna transparentność na start

Ten portal — emdashcms.pl — postawiłem jako pokazówkę EmDash CMS. Razem z bliźniaczą stroną na WordPressie (emdash.pl) były głównymi obiektami badania, które właśnie opublikowałem na blogu SHIFT64. Wszystkie cyfry, które za chwilę zobaczysz, to liczby tej strony — tej, którą właśnie czytasz.

I nie wyglądają dobrze.

emdashcms.pl jest naszym informacyjnym portalem o EmDash CMS — ma być uczciwym źródłem o tej technologii, więc musi pokazywać też te wyniki, które są dla niej niewygodne. Stąd ten artykuł tutaj, a nie tylko na shift64.com.

Co właściwie porównałem

Zbudowałem dwie strony z identyczną treścią:

  • emdashcms.pl — Astro 6 + EmDash + Cloudflare Workers + D1 (oficjalny szablon emdash-blog, bez modyfikacji)
  • emdash.pl — ręcznie napisany motyw WordPressa na PHP 8.4 + MySQL, hosting Hetzner we Frankfurcie, tylko Object Cache (żadnego full-page cache, żadnego Elementora, żadnych wtyczek-potworów)

Obie strony były scaffoldowane z Claude Code. Świadomie nie włączyłem cache HTML po stronie WordPressa — Worker EmDasha i tak na każde żądanie wykonuje pracę (pyta D1, składa HTML), więc porównanie z gotowym pliczkiem byłoby nieuczciwe.

Z serwera VPS w Warszawie odpaliłem curl 4 732 razy w 182 przebiegach przez ~3,5 doby, na 13 stronach, w losowej kolejności, bez keep-alive, z wymuszaniem zimnych startów Workera w rytmie 3-godzinnym.

Wyniki — czysty czas przetwarzania serwera

TTFB minus DNS, TCP i SSL — czyli sama praca backendu (zapytania do bazy, składanie HTML):

  • Średnia: EmDash 543 ms vs WordPress 84 ms — WP 6,4× szybszy
  • Mediana: 543 ms vs 69 ms — WP 7,9× szybszy
  • P95: 864 ms vs 148 ms — WP 5,8× szybszy
  • P99: 1 121 ms vs 234 ms — WP 4,8× szybszy
  • Maksimum: 2 196 ms vs 246 ms — WP 8,9× szybszy

Najgorszy pomiar WordPressa (246 ms) jest szybszy od najlepszego pomiaru strony postu na EmDashu. To nie jest margines, który się dooptymalizuje — to inna kategoria opóźnienia.

Skąd ta różnica?

Z architektury, nie z kodu. Strona postu na EmDashu wymaga sekwencji 8+ zapytań do D1 (entry → tagi → powiązane wpisy → ich tagi → widgety w sidebarze i stopce). Każdy round-trip to 40–100 ms na edge'u. WordPress robi to samo na lokalnym MySQL-u na tej samej maszynie w 0,5 ms łącznie (Object Cache + OPcache), przy 2–3 zapytaniach na całą stronę.

Plus: zimne starty Workerów są realne i mają dzienny cykl. W godzinach pracy (10:00 UTC) 35% żądań przekraczało 800 ms, widziałem maksima 2,2 s. W nocy spadało do ~8%. WordPress: zero efektu zimnego startu — pula PHP-FPM nigdy nie idzie spać.

Co próbowałem naprawić

Po pierwszej rundzie przepisałem stronę EmDasha:

  1. `server:defer` na widget areas — sidebar i stopka wychodzą z odpowiedzi początkowej, ładują się dogrywką. Wymaga ręcznego pisania komponentów-wrapperów (dyrektywa nie działa na importach z node_modules) i fallbacków-szkieletów. Tego nie ma w dokumentacji jako wymaganego kroku.
  2. Batchowanie tagów (`getTermsForEntries`) — niedokumentowane API EmDasha. Zabija N+1 na listingach: zamiast jednego query na każdy z N postów, jeden WHERE entry_id IN (...). Znalazłem przez czytanie sourców.
  3. Edge cache na poziomie Workera — caches.open() ręcznie w src/worker.ts, bo Workery na custom domenie domyślnie omijają CDN cache Cloudflare. Cache-Control po prostu nie zadziała, dopóki nie owiniesz handlera.

Co dało: średnia 543 → 322 ms (−41%), P99 1094 → 424 ms (−61%), maksimum 2196 → 745 ms (−66%). Realna wygrana. Niewystarczająca — WordPress dalej był 4,1× szybszy. Te ~320 ms to floor, którego nie ruszę bez D1 read replicas.

Werdykt zewnętrznego eksperta

Wykresy z Search Console (czas pobierania) wysłałem ślepo Jackowi Żmudzińskiemu (Head of GEO & SEO w Makolab). Bez etykiet, bez komentarza. Cztery tiery:

  • ~1 300 ms — „do złomu i przepisać” → tutaj wylądowała strona postu EmDasha na zimnych startach
  • typowy zoptymalizowany WP — „normalne, zgodne z oczekiwaniami” → tutaj wylądował emdash.pl
  • dobrze nastrojony WP — „przyzwoite”
  • klasa Astro — „wzorowe” → klasa, w której EmDash powinien być

Ta sama technologia (Astro), do której EmDash dokłada własną warstwę danych, dostaje najwyższą notę. Aktualne wykonanie EmDasha — najniższą.

Po tygodniu w Search Console: WordPress ma 4,3× więcej kliknięć, 3,9× wyższy CTR i wygrywa rankingiem nawet na zapytaniu „emdash cms”. Strona pisana o EmDashu przegrywa na własnej brandowej frazie z hand-coded WP, do którego nikt nie zaglądał od dnia publikacji.

Czy to znaczy, że EmDash jest „zły”?

Nie. To znaczy, że jest zły dla mojego use case'a — małych stron firmowych z niewielkim ruchem, gdzie Googlebot to znaczna część odwiedzin, a Worker przez większość doby śpi. Wszystkie problemy z benchmarku odwracają się przy dużym ruchu: Worker nie stygnie, edge cache faktycznie się zapełnia, D1 ma ciepłe shardy. Nie zmierzyłem tego scenariusza i nie zdziwiłbym się, gdyby tam EmDash wyglądał świetnie.

Wracam do EmDasha kiedy Cloudflare wyda D1 read replicas (jedyna zmiana, która zbije floor latency 5–10×) i gdy experimental.cache Astro doczeka się providera dla adaptera Cloudflare. Wtedy będę mógł go z czystym sumieniem polecić małym firmom — co pierwotnie było całym sensem tego eksperymentu.

Pełny artykuł, dane, repo

To wersja skrócona. Pełny artykuł — z rozkładem na godziny doby, wpływem płatnego planu Workers, screenshotami Query Monitor z WordPressa, komentarzem Jacka Żmudzińskiego i polemiką z Maćkiem Palmowskim — jest na blogu SHIFT64:

I Bought the Domain Before I Ran the Test. EmDash Still Lost to WordPress.

Oba repozytoria są publiczne (bench.sh, analyze.py, results.csv, surowe dane do reanalizy) — linki w pełnym artykule. Jeśli ktoś znajdzie konfigurację, która zbija EmDasha do klasy WordPressa bez ręcznego przepisywania połowy warstwy danych — z chęcią opublikuję follow-up.

Brak komentarzy