TD; DR:
Było trochę zamieszania co do tego, o co pytałem, więc oto najważniejszy pomysł na pytanie:
Zawsze chciałem, aby pytanie było takie, jakie jest. Być może początkowo nie wyraziłem tego dobrze. Ale intencja zawsze była „ modułowa, oddzielona, luźno sprzężona, odsprzężona, zrefaktoryzowany kod ” znacznie wolniejszy z samej swojej natury niż „ monolityczna pojedyncza jednostka, rób wszystko w jednym miejscu, jeden plik, ściśle powiązany ” kod. Reszta to tylko szczegóły i różne przejawy tego, na które natknąłem się wtedy, teraz, czy później. Na pewno jest wolniejszy na pewną skalę. Podobnie jak nie defragmentowany dysk, musisz odbierać elementy zewsząd. Jest wolniejszy. Na pewno. Ale czy powinno mnie to obchodzić?
Pytanie nie dotyczy…
nie chodzi o mikrooptymalizację, przedwczesną optymalizację itp. Nie chodzi o „optymalizację tej czy innej części na śmierć”.
Co to jest?
Chodzi o ogólną metodologię, techniki i sposoby myślenia o pisaniu kodu, które pojawiły się z czasem:
- „wstrzyknij ten kod do swojej klasy jako zależność”
- „napisz jeden plik na klasę”
- „oddziel widok od bazy danych, kontrolera, domeny”.
- nie pisz jednorodnego pojedynczego kodu spaghetti, ale pisz wiele osobnych modułowych komponentów, które współpracują ze sobą
Chodzi o sposób i styl kodu, który jest obecnie - w ciągu tej dekady - postrzegany i zalecany w większości ram, zalecany na konwencjach, przekazywany przez społeczność. Jest to zmiana myślenia z „monolitycznych bloków” na „mikrousługi”. A do tego przychodzi cena pod względem wydajności i narzutu na poziomie maszyny, a także narzutu na poziomie programisty.
Pierwotne pytanie brzmi:
W dziedzinie informatyki zauważyłem znaczącą zmianę myślenia w zakresie programowania. Często spotykam się z radą, która wygląda następująco:
- napisz mniejszy pod względem funkcjonalnym kod (w ten sposób bardziej testowalny i konserwowalny)
- przekształć istniejący kod na coraz mniejsze fragmenty kodu, aż większość metod / funkcji będzie miała tylko kilka wierszy i nie będzie jasne, jaki jest ich cel (co tworzy więcej funkcji w porównaniu z większym blokiem monolitycznym)
- pisz funkcje, które wykonują tylko jedną rzecz - rozdzielenie problemów itp. (co zwykle tworzy więcej funkcji i więcej ramek na stosie)
- tworzyć więcej plików (jedna klasa na plik, więcej klas do celów dekompozycji, do celów warstw, takich jak MVC, architektura domeny, wzorce projektowe, OO itp., co tworzy więcej wywołań systemu plików)
Jest to zmiana w porównaniu do „starych” lub „przestarzałych” lub „przestarzałych” praktyk kodowania, w których istnieją metody obejmujące 2500 linii, a duże klasy i boskie obiekty robią wszystko.
Moje pytanie brzmi:
gdy wywołanie sprowadza się do kodu maszynowego, 1 i 0, instrukcji montażu, talerzy HDD, czy powinienem się w ogóle martwić, że mój doskonale odseparowany klasowo kod OO z różnymi refaktoryzowanymi funkcjami i metodami od małych do małych generuje też dużo dodatkowych kosztów ogólnych?
Detale
Chociaż nie jestem do końca zaznajomiony z tym, w jaki sposób kod OO i jego wywołania metod są obsługiwane w ASM, a także w jaki sposób wywołania DB i wywołania kompilatora przekładają się na ruchome ramię siłownika na talerzu HDD, mam pewien pomysł. Zakładam, że każde dodatkowe wywołanie funkcji, wywołanie obiektu lub wywołanie „#include” (w niektórych językach) generuje dodatkowy zestaw instrukcji, zwiększając w ten sposób objętość kodu i dodając różne koszty związane z okablowaniem kodu, bez dodawania rzeczywistego „użytecznego” kodu . Wyobrażam sobie również, że dobre optymalizacje można przeprowadzić w ASM, zanim zostanie on faktycznie uruchomiony na sprzęcie, ale ta optymalizacja może zrobić tylko tyle.
Stąd moje pytanie - ile narzutu (w przestrzeni i szybkości) robi dobrze oddzielony kod (kod podzielony na setki plików, klas i wzorców projektowych itp.) W rzeczywistości w porównaniu do posiadania „jednej dużej metody, która zawiera wszystko w jednym pliku monolitycznym ”, z powodu tego narzutu?
AKTUALIZACJA dla jasności:
Zakładam, że przyjęcie tego samego kodu i podzielenie go, przeredagowanie go, rozdzielenie go na coraz więcej funkcji i obiektów oraz metod i klas spowoduje, że coraz więcej parametrów będzie przekazywanych między mniejszymi częściami kodu. Ponieważ na pewno kod refaktoryzacji musi podtrzymywać działanie wątku, a to wymaga przekazania parametrów. Więcej metod lub więcej klas lub więcej wzorców projektowych metod fabrycznych powoduje więcej narzutu związanego z przekazywaniem różnych bitów informacji w większym stopniu niż w przypadku pojedynczej klasy monolitycznej lub metody.
Gdzieś powiedziano (zacytuj TBD), że do 70% całego kodu składa się z instrukcji MOV ASM - ładowanie rejestrów CPU odpowiednimi zmiennymi, a nie faktyczne obliczenia. W moim przypadku ładujesz czas procesora instrukcjami PUSH / POP, aby zapewnić powiązanie i przekazywanie parametrów między różnymi częściami kodu. Im mniejsze są fragmenty kodu, tym bardziej wymagane jest ogólne „powiązanie”. Obawiam się, że to powiązanie zwiększa rozdęcie i spowolnienie oprogramowania i zastanawiam się, czy powinienem się tym przejmować i ile, jeśli w ogóle, ponieważ obecne i przyszłe generacje programistów, którzy budują oprogramowanie na następny wiek , będzie musiał żyć i korzystać z oprogramowania zbudowanego przy użyciu tych praktyk.
AKTUALIZACJA: Wiele plików
Piszę teraz nowy kod, który powoli zastępuje stary kod. W szczególności zauważyłem, że jedną ze starych klas był plik linii ~ 3000 (jak wspomniano wcześniej). Teraz staje się zestawem 15-20 plików znajdujących się w różnych katalogach, w tym w plikach testowych, a nie w frameworku PHP, którego używam do łączenia niektórych rzeczy. Nadchodzi także więcej plików. Jeśli chodzi o dyskowe operacje we / wy, ładowanie wielu plików jest wolniejsze niż ładowanie jednego dużego pliku. Oczywiście nie wszystkie pliki są ładowane, są ładowane w razie potrzeby, istnieją opcje buforowania dysku i buforowania pamięci, ale nadal uważam, że loading multiple files
wymaga to więcej przetwarzania niż loading a single file
do pamięci. Dodam to do mojej troski.
AKTUALIZACJA: Zależność Wstrzyknij wszystko
Wracając do tego po chwili .. Myślę, że moje pytanie zostało źle zrozumiane. A może postanowiłem źle zrozumieć niektóre odpowiedzi. Nie mówię o mikrooptymalizacji, ponieważ niektóre odpowiedzi zostały wyszczególnione (przynajmniej myślę, że nazywanie tego, co mówię o mikrooptymalizacji jest mylące), ale o ruchu „kodu refaktoryzacji w celu poluzowania ścisłego sprzężenia” jako całości , na każdym poziomie kodu. Niedawno przyjechałem z Zend Con, gdzie ten styl kodu był jednym z głównych punktów i głównych elementów konwencji. Oddziel logikę od widoku, widok od modelu, model od bazy danych, a jeśli możesz, odsprzęgnij dane od bazy danych. Zależność - wstrzykuje wszystko, co czasami oznacza po prostu dodanie kodu okablowania (funkcje, klasy, płyta kotłowa), który nic nie robi, ale służy jako punkt łączenia / zaczepu, w większości przypadków łatwo podwajając rozmiar kodu.
AKTUALIZACJA 2: Czy „dzielenie kodu na więcej plików” znacząco wpływa na wydajność (na wszystkich poziomach przetwarzania)
Jak filozofia compartmentalize your code into multiple files
wpływa na dzisiejsze obliczenia (wydajność, wykorzystanie dysku, zarządzanie pamięcią, zadania związane z przetwarzaniem procesora)?
mówię o
Przed...
W hipotetycznej, ale dość realnej, nie tak odległej przeszłości, możesz łatwo napisać jeden blok mono pliku, który ma model i widok oraz kontroler spaghetti lub nie-kodowany spaghetti, ale uruchamia wszystko, gdy jest już załadowany. Robiąc pewne testy porównawcze w przeszłości za pomocą kodu C, odkryłem, że O wiele szybsze jest ładowanie pojedynczego pliku 900 Mb do pamięci i przetwarzanie go w dużych porcjach niż ładowanie wiązki mniejszych plików i przetwarzanie ich w mniejszy posiłek części wykonujące tę samą pracę w końcu.
.. I teraz*
Dzisiaj patrzę na kod, który pokazuje księgę, która ma takie funkcje jak ... jeśli element jest „zamówieniem”, blok HTML pokazujący zamówienie. Jeśli element zamówienia można skopiować, wydrukuj blok HTML, który wyświetla ikonę i parametry HTML za nim, umożliwiając wykonanie kopii. Jeśli element można przenieść w górę lub w dół, wyświetl odpowiednie strzałki HTML. Itd. Mogę, dzięki Zend Framework, tworzyćpartial()
wywołań, co w zasadzie oznacza „wywołanie funkcji, która pobiera parametry i wstawia je do osobnego pliku HTML, który również wywołuje”. W zależności od tego, jak szczegółowo chcę uzyskać, mogę utworzyć osobne funkcje HTML dla najmniejszych części księgi. Jeden dla strzałki w górę, strzałki w dół, jeden dla „czy mogę skopiować ten element” itp. Łatwe tworzenie kilku plików w celu wyświetlenia niewielkiej części strony. Biorąc mój kod i zakulisowy kod Zend Framework, system / stos prawdopodobnie wywołuje blisko 20-30 różnych plików.
Co?
Interesują mnie aspekty, zużycie urządzenia, które jest tworzone przez dzielenie kodu na wiele mniejszych osobnych plików.
Na przykład ładowanie większej liczby plików oznacza umieszczenie ich w różnych miejscach systemu plików oraz w różnych miejscach fizycznego dysku twardego, co oznacza więcej czasu na wyszukiwanie i czytanie dysku twardego.
Dla procesora prawdopodobnie oznacza to większe przełączanie kontekstu i ładowanie różnych rejestrów.
W tym podbloku (aktualizacja nr 2) bardziej interesuje mnie, w jaki sposób użycie wielu plików do wykonania tych samych zadań, które można wykonać w jednym pliku, wpływa na wydajność systemu.
Korzystanie z interfejsu API Zend Form vs prosty HTML
Użyłem Zend Form API z najnowszymi i najlepszymi nowoczesnymi praktykami OO, aby zbudować formularz HTML z weryfikacją, przekształcając się POST
w obiekty domeny.
Wykonanie go zajęło mi 35 plików.
35 files =
= 10 fieldsets x {programmatic fieldset + fieldset manager + view template}
+ a few supporting files
Wszystkie z nich można zastąpić kilkoma prostymi plikami HTML + PHP + JS + CSS, być może 4 lekkimi plikami.
Czy lepiej? Czy warto? ... Wyobraź sobie, że ładujesz 35 plików + liczne pliki biblioteki Zend Zramework, które sprawiają, że działają, a 4 proste pliki.