Pomiary i wydajność#

Wstęp#

Z tworzeniem stron i aplikacji webowych nieustannie związane są kwestie wydajnościowe. Im szybszy i mniej zasobożerny kod tym lepiej dla wszystkich. W przeszłości pomiar wydajności utworzonego kodu odbywał się poprzez ręczne profilowanie standardowymi poleceniami JavaScript, ale rozwiązanie to miało wiele ograniczeń. Na dzień dzisiejszy platforma webowa oferuje kilka nowocześniejszych mechanizmów zwiększających precyzję pomiaru oraz wydajność samego kodu:

Wszystkie te nowości okazują się na tyle interesujące i przydatne, że postanowiłem utworzyć osobny dział "Pomiary i wydajność". W razie konieczności materiał będzie uzupełniany o kolejne istotne kwestie. Pod uwagę brane będą najnowsze wersje specyfikacji, z ewentualnym wskazaniem różnić między poprzednimi wersjami dokumentów.

Za rozwój specyfikacji związanych z wydajnością Webu odpowiada grupa robocza WebPerf Working Group. Od czasu do czasu proponuję zapoznać się z aktualnymi dyskusjami prowadzonymi na dedykowanej liście mailingowej.

Trzeba zdawać sobie sprawę z tego, że najnowsze rozwiązania wciąż znajdują się w fazie rozwoju i mogą ulegać licznym zmianom. Praktyczne implementacje to perspektywa wielu przyszłych miesięcy.

Profilowanie#

Profilowanie (profiling) jest dynamiczną formą analizy programu, która mierzy zajętość pamięci lub czas wykonywania programu, wykorzystanie poszczególnych instrukcji lub częstotliwość i czas trwania wywołań funkcji. Najczęstszym zastosowaniem informacji pochodzących z profilowania jest optymalizacja programu.

Najłatwiejszym, najprostszym i w zasadzie najmniej precyzyjnym wariantem profilowania jest obserwacja przez człowieka; wystarczy uruchomić aplikację na docelowej platformie i sprawdzić, czy ma wystarczającą wydajność. W końcu zapewnienie odpowiedniej wydajności (głównie interfejsu użytkownika) ma służyć człowiekowi, więc jest to właściwy sposób przeprowadzenia takich pomiarów. Tylko nielicznie będą w stanie podać czas opóźnienia w sekundach czy milisekundach, większość posłuży się ogólniejszymi określeniami, np. "szybki", "powolny" czy "zadowalający".

W przypadku większych i precyzyjniejszych wymagań należy sięgnąć po jedną z dwóch podstawowych możliwości oprzyrządowania kodu:

Obydwa warianty są ściśle uzależnione od aktualnych mechanizmów udostępnianych przez przeglądarki internetowe, dlatego im więcej zaimplementowanych dodatkowych API tym większe i dokładniejsze możliwości pomiaru.

Profilowanie ręczne#

Profilowanie ręczne (często nazywane też logowaniem) to najbardziej pierwotna forma pomiaru, gdzie wykorzystujemy możliwości samego języka JavaScript. Korzystając z obiektu Date można w dowolnej chwili dokonać stosownego pomiaru w skrypcie. Przed pojawieniem się innych narzędzi była to najpopularniejsza metoda mierzenia czasu wykonywania skryptu i bardzo często jest stosowana po dzień dzisiejszy. Domyślnie obiekt Date zwraca aktualny czas (w milisekundach), zaś odjęcie jednej instancji obiektu Date od drugiej wyraża czas, jaki upłynął.

Prosty przykład, gdzie dokonujemy pomiaru utworzenia 10 tysięcy elementów <div> wewnątrz skryptu:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
<script>

	var start = new Date(),
		count = 10000,
		i, element, time;

	for (i = 0; i < count; i++){
		element = document.createElement("div");
	}

	time = new Date() - start;
	document.write("Utworzenie " + count + " elementów zajęło " + time + " ms");

</script>

Ten typ profilowania jest kłopotliwy i mało wygodny w implementacji, ponieważ wymaga ręcznego wykreowania własnego kodu do pomiaru. Dobrym pomysłem będzie utworzenie specjalnego obiektu Timer w globalnej przestrzeni, który pomoże przy obliczaniu czasu i przechowa wyniki. Oto przykładowa implementacja:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
<script>

	var Timer = {

		_data: {},

		start: function(key){
			Timer._data[key] = new Date;
		},

		stop: function(key){

			var time = Timer._data[key];

			if (time){
				Timer._data[key] = new Date() - time;
			}

		},

		getTime: function(key){
			return Timer._data[key];
		}

	};

	// Przykładowe zastosowanie

	Timer.start("createElement");

	for (i = 0; i < 10000; i++){
		element = document.createElement("div");
	}

	Timer.stop("createElement");

	document.write("Utworzenie 10000 elementów zajęło " + Timer.getTime("createElement") + " ms");

</script>

Rozwiązanie to nadal wymaga ręcznego opracowania, lecz zapewnia podstawowy wzorzec do budowania własnego profilera w czystym JS. Rozszerzając koncepcję obiektu Timer można skonstruować profiler, który będzie rejestrował funkcje i wyposażał je w kod do pomiaru czasu. Większość najpopularniejszych bibliotek dla JS (np. jQuery czy YUI) lub wbudowanych w przeglądarki narzędzi deweloperskich udostępnia (najczęściej poprzez globalny obiekt console) bardziej rozbudowane warianty naszego przykładowego obiektu Timer.

Przy bardziej precyzyjniejszych pomiarach warto skorzystać z czasu wysokiej rozdzielczości.

Profilowanie ręczne wymaga nieco większych nagładów pracy po stronie programisty, ale w niektórych przypadkach będzie nieodzowne (nie wszystko uda się ustalić za pomocą profilerów automatycznych). Najlepiej utworzyć i sukcesywnie rozwijać własny wariant profilera ręcznego, z którego prędzej czy później przyjdzie nam skorzystać.

Profilowanie automatyczne#

Aktualne przeglądarki internetowe posiadają wbudowane zestawy narzędzi dla deweloperów, które automatyzują cały proces profilowania na różnych płaszczyznach. Niektóre z przeglądarek można uzupełniać zewnętrznymi dodatkami (np. bardzo znanym rozszerzeniem Firebug).

Profilery mogą stosować jedną z dwóch strategii: albo będą ingerować w kod będący przedmiotem pomiarów przez dodanie specjalnego kodu zbierającego statystyki wydajności (np. poprzez automatyczne dodawanie przedstawionego wcześniej profilera ręcznego), albo pasywnie monitorować czas uruchamiania, sprawdzając, jaki fragment kodu wykonywany jest w danym momencie. Drugi wariant w mniejszym stopniu wpływa na wykonywanie profilowanego kodu, jednak odbywa się to kosztem zebrania danych o niższej jakości.

W związku z powyższym pamiętajmy, że automatyczne profilery wywołują coś zbliżonego do efektu obserwatora w fizyce: czynność obserwacji działania kodu wpływa na działanie kodu. Nie mam żadnych dodatkowych informacji odnośnie szczegółowego sposobu działania poszczególnych narzędzi (np. operowania jedynie w głównym wątku czy może przerzucenia części zadań do wątków roboczych), dlatego należy założyć, że sam pomiar generuje pewne koszta wydajnościowe. Wartości zwracane przez tego rodzaju profilery należy traktować względnie (w obrębie danej aplikacji) i nie porównywać w proporcji 1:1 z narzędziami z innego środowiska. Inaczej mówiąc, są to idealne narzędzia do kompleksowego, ale jednocześnie ogólnikowego wykrywania "wąskich gardeł" lub wolniej wykonywanych fragmentów kodu.

Oprócz samego pomiaru wykonywania kodu JavaScript/DOM istotna jest także analiza zasobów sieciowych, z których zbudowana jest nasza witryna. Dotyczy to samego pliku HTML, wszystkich skryptów, arkuszy stylów, plików graficznych i innych multimediów. Narzędzia deweloperskie przeglądarek internetowych maja specjalny panel (najczęściej pod nazwą "Sieć"), gdzie możemy odczytywać wszystkie podstawowe informacje danego zasobu, jak np. szczegóły żądania/odpowiedzi HTTP i czasy trwania poszczególnych czynności.

Ogólnie rzecz biorąc najnowsze API pozwalają z poziomu skryptu odczytywać wszystkie te informacje, ale wbudowane narzędzia deweloperskie mają wygodne interfejsy graficzne i nie wymagają żadnych dodatkowych prac po stronie programisty. Jeśli nie potrzebujemy precyzyjniejszych informacji (np. wszystkich danych związanych z nawigowaniem) to w większości przypadków odpalenie wbudowanych narzędzi będzie wystarczające.

Jeśli chodzi o jakość samych narzędzi deweloperskich to nie ma idealnego rozwiązania, każda z przeglądarek oferuje unikatowe cechy, które mogą się wzajemnie uzupełniać. Jeśli chodzi o monolit to wydaje się, że na dzień dzisiejszy Chrome zapewnia największe możliwości. Tak czy inaczej wszystko będzie zależne od konkretnych potrzeb, dlatego najlepiej zaznajomić się z jak największą liczbą przydatnych narzędzi.

Pasek społecznościowy

SPIS TREŚCI AKTUALNEJ STRONY

Pomiary i wydajność (H1) Wstęp (H2) Profilowanie (H3) Profilowanie ręczne (H4) Profilowanie automatyczne (H4)