Wartości ponownie używane w wysokopoziomowym kodzie programu

0

Kie­dy pi­sa­łem pro­ce­du­rę ‘ser­we­ra’ HTTP, to za­u­waży­łem, że cięż­ko jest po­dzielić ją na mniej­sze fun­kcje ze wzglę­du na to, że po­d­czas jej wy­ko­ny­wa­nia się po­wsta­wa­ły zmien­ne po­ś­re­d­nie, któ­re mog­ły być wy­ko­rzys­ty­wa­ne pó­ź­niej bez niepotrzebnego obliczania od nowa.
Występowało coś w rodzaju wartości ponownie używanych, jakie wykorzystuje kompilator podczas tworzenia kodu asemblera do optymalizacji programu. Jednak tutaj w kodzie wysokopoziomowym.
Natomiast nie wiem, pod jakie przekształcenie programu wysokopoziomowego ten sposób pisania programu podpada, by można było utworzyć mniejsze funkcje. Te wartości pośrednie można by trzymać w jakichś zmiennych stanu, ale wtedy musiałyby one być globalne... więc raczej nie.

Przykładowo:
‘Serwer’ otrzymuje blok danych, w którym są nagłówki żądania HTTP. Procedura ‘serwera’ sprawdza poprawność formatu tych nagłówków i od razu oznacza w zmiennych lokalnych, gdzie się znajdują te nagłówki w bloku danych, czyli ustawia sobie wskaźniki do bloku danych (ściśle to ustawia adres początku i końca danego nagłówka HTTP). To są właśnie niektóre z tych wartości pośrednich, ponownie używanych.
Później w tej procedurze ‘serwera’, gdy potrzeba skorzystać z tekstu nagłówków żądania HTTP, to od razu czytam z przechowywanych wskaźników (lub oczywiście w przypadku braku danych nagłówków — pomijam je).

Takie postępowanie powoduje unikanie ponownego przetwarzania tekstu i powstają wartości ponownie używane w wysokopoziomowym kodzie programu w postaci zmiennych lokalnych.
Jak przekształcić taki kod na taki, w którym możliwe będzie użycie mniejszych funkcji, a może podejście z ponownym użyciem wartości raz obliczonych jest błędne?

3

Hmm.... Nie rozumiem i trochę rozumiem.
Czy Ty piszesz od nowa jakąś bibliotekę serwera HTTP. Jeżeli tak. to po co ?
Jeżeli nie, to po co Ci "znajomość", gdzie rozpoczyna się blok nagłówków ? Wszak dobra biblioteka zrobi to za Ciebie.

1

Te wartości pośrednie można by trzymać w jakichś zmiennych stanu, ale wtedy musiałyby one być globalne... więc raczej nie.

Czym są zmienne stanu i dlaczego musiałyby być globalne?

1
Robert Karpiński napisał(a):

Hmm.... Nie rozumiem i trochę rozumiem.
Czy Ty piszesz od nowa jakąś bibliotekę serwera HTTP. Jeżeli tak. to po co ?
Jeżeli nie, to po co Ci "znajomość", gdzie rozpoczyna się blok nagłówków ? Wszak dobra biblioteka zrobi to za Ciebie.

I co to za pomysł ze zmiennymi lokalnymi ?? !!!

+1
Pachnie utra-ultra optymalizacją (na ile znam życie: Premature Optimization, które "It’s the “Root of All Evil” )

@overcq:

To jakieś arduino?

0
ZrobieDobrze napisał(a):

Pachnie utra-ultra optymalizacją (na ile znam życie: Premature Optimization, które "It’s the “Root of All Evil” )

Jak ja kocham te "optymalizacje" i benchmarki różnych bibliotek. Pięknie to wygląda na papierze - a w praktyce bywa różnie, a czasami dramatycznie ..
Ale to w/w nie jest głównym tematem, więc dajmy się wypowiedzieć autorowi wątku. Bo może robi coś zajebistego, a my tacy niegodziwcy !

3
overcq napisał(a):

Kie­dy pi­sa­łem pro­ce­du­rę ‘ser­we­ra’ HTTP,

co to za język? Wygląda składniowo jak C, ale ten kod chyba mocno niskopoziomowy jest, bo już assembler jest bardziej czytelny niż funkcje typu D czy VO1 albo Mt_ (czy to kod poddany obfuskacji albo wypluty przez automat?)

Jednak tutaj w kodzie wysokopoziomowym.

Tylko, że kod z tego linka wcale nie wygląda jak kod wysokopoziomowy nawet jak na standardy C, może w tym jest problem.

Serwer’ otrzymuje blok danych, w którym są nagłówki żądania HTTP. Procedura ‘serwera’ sprawdza poprawność formatu tych nagłówków i od razu oznacza w zmiennych lokalnych, gdzie się znajdują te nagłówki w bloku danych,

A nie możesz stworzyć struktury ze zmiennymi i przekazywać do kolejnych funkcji tej struktury? Jeżeli chcesz współdzielić stan.

Takie postępowanie powoduje unikanie ponownego przetwarzania tekstu i powstają wartości ponownie używane w wysokopoziomowym kodzie programu w postaci zmiennych lokalnych.

Nie wiem jak C, ale w wysokopoziomowych językach programowania można zrobić domknięcie/closure(funkcję w funkcji) i wtedy taka funkcja widzi zmienne lokalne z zewnętrznej funkcji.

Natomiast mam wrażenie, że tutaj znowu mamy do czynienia z problemem XY i że zamiast myśleć o przekazywaniu zmiennych, należałoby się cofnąć i pomyśleć, co naprawdę chcesz osiągnąć.

1
LukeJL napisał(a):
overcq napisał(a):

Kie­dy pi­sa­łem pro­ce­du­rę ‘ser­we­ra’ HTTP,

co to za język? Wygląda składniowo jak C, ale ten kod chyba mocno niskopoziomowy jest, bo już assembler jest bardziej czytelny niż funkcje typu D czy VO1 albo Mt_ (czy to kod poddany obfuskacji albo wypluty przez automat?)

+1

Bosch, dlaczego ja w ten kod popatrzyłem przed nocą .. będę miał koszmary :(
To jest straszne.

0
Robert Karpiński napisał(a):

Czy Ty piszesz od nowa jakąś bibliotekę serwera HTTP. Jeżeli tak. to po co ?
Jeżeli nie, to po co Ci "znajomość", gdzie rozpoczyna się blok nagłówków ? Wszak dobra biblioteka zrobi to za Ciebie.

To jest końcowy program ‘serwera’ HTTP/HTTPS 1.1 i 2, i on już działa. Testowałem w nim inne rozwiązania niż kopiowanie pamięci.

Patryk27 napisał(a):

Czym są zmienne stanu i dlaczego musiałyby być globalne?

‘Serwer’ otrzymuje blok danych, który jest ulotny. W tym bloku danych znajdują się nagłówki żądania HTTP. Więc wskaźniki do konkretnych nagłówków są tylko bieżącym stanem, do otrzymania następnego bloku danych związanego z tym strumieniem. (A właściwie — ze względu na stosowanie tego samego bufora danych — tylko do czasu przetwarzania kolejnego bloku danych.)
Teoretycznie globalne, by można było się dostać do nich z wołanych funkcji, ale wskazałem to jako ślepą uliczkę.

ZrobieDobrze napisał(a):

To jakieś arduino?

Nie, to działa w zwykłych systemach typu ‘unix’.

LukeJL napisał(a):

co to za język? Wygląda składniowo jak C, ale ten kod chyba mocno niskopoziomowy jest, bo już assembler jest bardziej czytelny niż funkcje typu D czy VO1 albo Mt_ (czy to kod poddany obfuskacji albo wypluty przez automat?)

To jest rozszerzony C czyli C+. Wymienione instrukcje tylko na początku wydają się skomplikowane. One powstały jak piktogramy. Jeden z opisów jest tutaj, a definicja jest tutaj.
Na przykład VO1_ to jest instrukcja (V) przechwytująca błąd wywołania systemowego (w systemach typu ‘unix’), które zwracają w przypadku błędu wartość -1. Tutaj uwzględnione jest ponawianie w pętli (O) takiego wywołania systemowego, jeśli wystąpiło przerwanie systemowe (EINTR). Natomiast _ na końcu oznacza, że program ma być zakończony w przypadku błędu, a nie – błąd obsłużony w następującym bloku. Instrukcja ta loguje do terminala zaistniały błąd.
Instrukcja Mt_ przydziela na stercie i przypisuje do wskaźnika podaną liczbę elementów o rozmiarze elementu, na który wskazuje ten wskaźnik.
Instrukcja D to definicja tzw. ‹zadania›, coś w rodzaju wątku bez konkurencji z innymi.

Serwer’ otrzymuje blok danych, w którym są nagłówki żądania HTTP. Procedura ‘serwera’ sprawdza poprawność formatu tych nagłówków i od razu oznacza w zmiennych lokalnych, gdzie się znajdują te nagłówki w bloku danych,

A nie możesz stworzyć struktury ze zmiennymi i przekazywać do kolejnych funkcji tej struktury? Jeżeli chcesz współdzielić stan.

Tak, to jest jedno z możliwych rozwiązań (jeśli istnieją inne ;-) ).

Takie postępowanie powoduje unikanie ponownego przetwarzania tekstu i powstają wartości ponownie używane w wysokopoziomowym kodzie programu w postaci zmiennych lokalnych.

Nie wiem jak C, ale w wysokopoziomowych językach programowania można zrobić domknięcie/closure(funkcję w funkcji) i wtedy taka funkcja widzi zmienne lokalne z zewnętrznej funkcji.

Tutaj raczej to niemożliwe.

Natomiast mam wrażenie, że tutaj znowu mamy do czynienia z problemem XY i że zamiast myśleć o przekazywaniu zmiennych, należałoby się cofnąć i pomyśleć, co naprawdę chcesz osiągnąć.

Co chcę osiągnąć to osiągnąłem: brak ponownego przetwarzania danych i brak niepotrzebnego kopiowania danych. Staram się tylko taki program czyściej zapisać.

1

Niestety nie zauważyłem pierwotnie, że załączyłeś kod. Przejrzałem go teraz.
Wg mnie przydałby się jakiś, nawet drobny refactoring, bo ciężko się ten kod czyta. Ale nie to jest tematem.

Zgadzam się, że przydałoby się podzielić ten kod na mniejsze funkcje.
Jako, że brak tutaj "obiektowości" to trzeba ją symulować poprzez strukturę ( co słusznie zauważył @LukeJL ).
czyli:
funkcja( struktura, inne_parametry )

Dobrym wyborem takiego pseudo-obiektu może być E_srv_Z_http2_accept, który reprezentuje stan sesji.
Nie wiem czy w pełni, ale to już tylko Ty sam możesz ocenić.

Moja rada jest taka. Napisz to jeszcze raz. Masz już wiedzę co trzeba zrobić, kod nie jest duży.
Przemyśl co jest twoim stanem globalnym i stanem reprezentującym sesje.
Spróbuj np. na kartce lub w oryginalnym kodzie zaznaczyć bloki, które można przenieść do funkcji określając jakie parametry powinna posiadać.
Może warto ten kod podzielić też na oddzielne pliki ?!
Generalnie zrób wstępną pracę projektową.
Gwarantuje Ci, że wersja 2 będzie bardziej czytelna, lepsza, bardziej optymalna i gotowa na przyszłe zmiany !

0

w C# jest taka dość dziwna klasa Span<T>, dziwna, bo trochę inaczej się odnosi do zarządzania pamiecią. Więc nie mów, ze nie rozumiem podstawowej idei (przetwarzania wycinków bez kopiowania). Tyle, że to boczna uliczka, dla piszących kompilatory, deserializery itd ... NIGDY nie miałem powodu się nią blizej zainteresować.

  1. Strategia "nie kopiowania" (przetwarzania in-place) ma jedną zaletę, i ogrom wad.
    Kopiowanie (out of place) np:
    a) daje ładnie przygotowany element, np prawdziwy C string z \0 na końcu - przeciwnie, masz dziwne zakończenie (nie wiem, większość '\n', ale ostatni inaczej ???), co wymaga komplikacji ustalenia końca. Co więcej, okaże się, ze tylko kilka nagłówków jest interesujących, i można je skwantować do jakiego "enuma" czy czegoś podobnego b) szybciej uwalnia bufor wejściowy. W tym zastosowania wybacz, ale onanizujesz się jedną oszczędnością, a ciągle pracuje sieć, spływają nowe bloki itd ... a ty blokujesz pamięć. c) pozwala użyć inteligentnej algorytmiki, np jakąś formę Map<>` - tu masz brutalne szeregowe przeszukiwanie

Podaj adres. Mam w szufladzie kawałek RAM 0.5MB, przyslę ci, z wielokrotnością pokryje wszytsie "dodatkowe" potzeby w RAM

  1. Język nadzbiór C. Wszystko to masz w C++. Ale wiem, rasowi wyznawcy religii C prędzej by podali rękę szatanowi, niż C++ by użyli.
    Ta kaleka eksponuje po wielokroć wszystkie wady C (preprocesor, milliard dolar bug), nie dając nic w zamian, prowadzi wstecz w duchu proceduralności itd
1

Ja czytałem kiedyś taką książkę do testowania kodu i tam było coś co wydaje mi się, że było mądre, czyli że dana funkcja nie powinna przekraczać danej ilości complexity, jeśli tak trzeba podzielić na mniejsze moduły.

2
Szalony Programista2 napisał(a):

Ja czytałem kiedyś taką książkę do testowania kodu i tam było coś co wydaje mi się, że było mądre, czyli że dana funkcja nie powinna przekraczać danej ilości complexity, jeśli tak trzeba podzielić na mniejsze moduły.

Stare ale jare - "Czysty kod" Wujka Boba - https://helion.pl/ksiazki/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykov.htm#format/d

1

źle podałem o complexity bo jakoś inaczej to się nazywa

Hm, jeszcze jakoś inaczej? Jest nawet taki ładny wskaźnik Cyclomatic complexity.
BTW kiedyś słyszałem że w kodzie krytycznym złożoność cyklomatyczna powinna wynosić max . Jeszcze się nad tym zastanawiam. Tak od pięciu lat. Na szczęście nie pisze kodu krytycznego

0

Nie rozumiem problemu.
Masz serwer, który dostaje żądanie.
Serwer tworzy obiekt "Żądanie".
W tym obiekcie możesz wywoływać "przetwórzNagłówki()"
Wynik, czyli jakaś tam struktura ląduje jako pole obiektu Request.
Jeżeli chcesz zostawić coś dla programistów aplikacji działających na twoim serwerze, to możesz im dać jakąś mapę, w której będą sobie przechowywać co chcą.

1

Czy ja dobrze widzę że stworzyłeś własny język programowania "transpilowany" do C w którym teraz tworzysz od podstaw serwer HTTP?
Jakie są zalety Twojego rozwiązania? Robisz to stricte w celach edukacyjnych? Nie myślałeś żeby przekierować swoich umiejętności i czasu na coś pożytecznego?

Odnosząc się do tematu - zmienne nie muszą być globalne, w normalnym serwerze HTTP zazwyczaj masz obiekt typu Request gdzie trzymane i przekazywane są wszystkie dane odnośnie żądania i to chyba wyczerpuje temat. Nie musisz mieć języka zorientowanego obiektowo żeby pisać obiektowo - tak jak pisali wyżej, wystarczy "obiekt" / strukturę przekazywać przez referencję w pierwszym parametrze, można nazwać go self czy this i już ci niczego nie brakuje.

Ale widzę że skupiasz się na implementacji HTTP/1.1 gdzie HTTP/2.0 jest już standardem a większe strony już przechodzą na HTTP/3.0. Podejrzewam że dojście w twoim kodzie do HTTP/3.0 to kwestia długich miesięcy lub nawet lat, jaki w tym cel?

1
obscurity napisał(a):

Czy ja dobrze widzę że stworzyłeś własny język programowania "transpilowany" do C w którym teraz tworzysz od podstaw serwer HTTP?
Jakie są zalety Twojego rozwiązania? Robisz to stricte w celach edukacyjnych? Nie myślałeś żeby przekierować swoich umiejętności i czasu na coś pożytecznego?

Sadząc po innych postach, będziemy udowadniać nanosekundowe przyśpieszenie na poziomie pojedynczych rozkazów - gdy i tak np stack sieciowy to dziesiątki tysięcy rozkazów.

0
gajusz800 napisał(a):

@obscurity: dobrze widzisz:
https://overcq.ct8.pl/oux-c-plus.html

Przy kolejnych klonach C ( ostatnio Carbon ), jeden język więcej czy mniej nie ma znaczenia.
A widać, że ten projekt sukcesywnie jest prowadzony. Świata to nie zmieni, ale satysfakcja dla autora gwarantowana !

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