Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > pl.comp.os.linux.programowanie > #2248 > unrolled thread

::read i ::poll

Started byheby <heby@poczta.onet.pl>
First post2020-11-04 10:31 +0100
Last post2020-11-05 19:16 +0100
Articles 7 — 2 participants

Back to article view | Back to pl.comp.os.linux.programowanie


Contents

  ::read i ::poll heby <heby@poczta.onet.pl> - 2020-11-04 10:31 +0100
    Re: ::read i ::poll michal.lyszczek@bofc.pl - 2020-11-05 11:45 +0100
      Re: ::read i ::poll heby <heby@poczta.onet.pl> - 2020-11-05 12:57 +0100
        Re: ::read i ::poll michal.lyszczek@bofc.pl - 2020-11-05 13:43 +0100
          Re: ::read i ::poll heby <heby@poczta.onet.pl> - 2020-11-05 15:26 +0100
            Re: ::read i ::poll michal.lyszczek@bofc.pl - 2020-11-05 16:01 +0100
              Re: ::read i ::poll heby <heby@poczta.onet.pl> - 2020-11-05 19:16 +0100

#2248 — ::read i ::poll

Fromheby <heby@poczta.onet.pl>
Date2020-11-04 10:31 +0100
Subject::read i ::poll
Message-ID<rntsee$3b1$1@dont-email.me>
Cześć.

Czytam z rurki za pomocą ::read. Blokująco, bo na tym polega 
synchronizacja w tym konkretnym przypadku.

Ale muszę raz na jakiś czas stwierdzić, czy nie czas awaryjnie się 
składać, obejrzeć jakas flagę, zawołać jakiś watchdog itd.

::read blokującego nie da się przerwać. Co prawda jest niby EINTR ale ja 
czekam w wielu wątkach na wielu rurach i musze precyzyjnie przerwać 
bardzo konkretny ::read, ten, a nie inny.

Rozwiązanie okazało się proste: użyć ::poll. Czeka on aż pojawią się 
jakieś dane do odczytu, ale ma też timeout po którym zwraca mi 
sterowanie i to załatwia temat przerywalnosci odczytu.

I wszystki super działa, tylko jest ... znacznie wolniejsze. Wywołanie 
::read jest szybsze niż pary ::poll->::read. No i co z tym teraz zrobić? 
Jest jakaś inna metoda na posiadanie blokującego ::read ale jednocześnie 
możliwosci przerwania tego read na żądanie?

PS. Mogę zamknąc deskryptor pliku i wtedy ::read wylautuje z błedem. Ale 
to jest nieeleganckie, ponadto nie dośc że powoduje race-conditions, to 
jeszcze jest destrukcyjne, tzn komunikacja jest już niemożliwa do 
wznowienia. A ja chce przerwać read, coś sprawdzić i wrócić do czekania 
na dane.

[toc] | [next] | [standalone]


#2249

Frommichal.lyszczek@bofc.pl
Date2020-11-05 11:45 +0100
Message-ID<20201105104532.zdht7h4fy4vutyah@marchewa.redembedded.pl>
In reply to#2248

[Multipart message — attachments visible in raw view] — view raw

On 2020-11-04 10:31:56, heby wrote:
> Rozwiązanie okazało się proste: użyć ::poll. Czeka on aż pojawią się jakieś
> dane do odczytu, ale ma też timeout po którym zwraca mi sterowanie i to
> załatwia temat przerywalnosci odczytu.
>
> I wszystki super działa, tylko jest ... znacznie wolniejsze. Wywołanie
> ::read jest szybsze niż pary ::poll->::read. No i co z tym teraz zrobić?
> Jest jakaś inna metoda na posiadanie blokującego ::read ale jednocześnie
> możliwosci przerwania tego read na żądanie?

Najprościej będzie wysłać sygnał do konkretnego wątku przy pomocy
pthread_kill(3). Jak kod ma być przenośny między unixami, należy
pamiętać by wyłączyć SA_RESTART na systemach z rodziny BSD przy
pomocy siginterrupt(3).

-- 
.-----------------.-------------------.---------------------.------------------.
| Michal Lyszczek | Embedded C, Linux |   Company Address   |  .-. open source |
| +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 |  oo|  supporter  |
| https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\      &      |
| GPG FF1EBFE7E3A974B1 | Bits of Code | NIP:  813 349 58 78 |(\_;/) programer  |
`----------------------^--------------^---------------------^------------------'

[toc] | [prev] | [next] | [standalone]


#2250

Fromheby <heby@poczta.onet.pl>
Date2020-11-05 12:57 +0100
Message-ID<ro0pai$b5d$1@dont-email.me>
In reply to#2249
On 05/11/2020 11:45, michal.lyszczek@bofc.pl wrote:
> Najprościej będzie wysłać sygnał do konkretnego wątku przy pomocy
> pthread_kill(3).

Co z taką sytuacją:
{
  ...
  std::mutex::scoped_lock lock(mutex);
   ...
  ::read(...)
}

Zakładając że zainstaluje globalny handler sygnałów (co już jest błędem 
projektowym, może nie tylko ten wątek go potrzebuje), czy mogę wsyłać 
coś co spowoduje że ::read wyskoczy z EINTR?

> Jak kod ma być przenośny między unixami, należy
> pamiętać by wyłączyć SA_RESTART na systemach z rodziny BSD przy
> pomocy siginterrupt(3).

No i tu właśnie jest problem. To ogromna apliakcja. Z powodu kilku 
::read mam manipulować krytyczm, globlanym stanem aplikacji? Brzmi jak 1 
z kolokwium z inżynierii programowania.

[toc] | [prev] | [next] | [standalone]


#2251

Frommichal.lyszczek@bofc.pl
Date2020-11-05 13:43 +0100
Message-ID<20201105124355.ixk6djsio5dqsuh2@marchewa.redembedded.pl>
In reply to#2250

[Multipart message — attachments visible in raw view] — view raw

On 2020-11-05 12:57:04, heby wrote:
> On 05/11/2020 11:45, michal.lyszczek@bofc.pl wrote:
> > Najprościej będzie wysłać sygnał do konkretnego wątku przy pomocy
> > pthread_kill(3).
> 
> Co z taką sytuacją:
> {
>  ...
>  std::mutex::scoped_lock lock(mutex);
>   ...
>  ::read(...)
> }

Nic, w takim przypadku nie pomoże pthread_kill(3) tylko select/poll/epoll.
> 
> Zakładając że zainstaluje globalny handler sygnałów (co już jest błędem
> projektowym, może nie tylko ten wątek go potrzebuje), czy mogę wsyłać coś co
> spowoduje że ::read wyskoczy z EINTR?

Globalny handler sygnałów nie jest błędem projektowym, błędne użycie go
jest błędem projektowym. Signal handler, którego jedyne zadanie to przerwać
syscall, może mieć jedynie 'return;' w body i nie musi operować na żadnych
danych. Każdy sygnał przerywa syscall, możesz wyemitować np. SIGUSR1. Ale
jeżeli kilka wątków czyta ci jeden deskryptor plików to musiałbyś się bawić
w zmienne w stylu "thread_id_in_read", i na tej podstawie słać sygnały - ale
to już zabawa w synchronizację.

read(2) nie jest przystosowany do wielowątkowego obsługiwania, lepiej byłoby
aby 1 wątek robił read(2) na fd, jakoś to wstępnie procesował, po czym
wrzucał na jakąś synchronizowaną kolejkę, która może już być odczytywana
przez wiele wątków.

t1:
    while (1) {
        struct data_frame frame;
        read_until_full_frame_is_received(&frame);
        queue_push(&frame);
    }

t2, t3, t4...:
    while (1) {
        struct data_frame frame;
        queue_pop(&frame);
        process_frame(&frame);
    }

W takim przypadku to nawet nie powinno być potrzeby bawić się w EINTR. Nie
znam konkretnego użycia więc też ciężko się odnieść do tego trochę.

> 
> > Jak kod ma być przenośny między unixami, należy
> > pamiętać by wyłączyć SA_RESTART na systemach z rodziny BSD przy
> > pomocy siginterrupt(3).
> 
> No i tu właśnie jest problem. To ogromna apliakcja. Z powodu kilku ::read
> mam manipulować krytyczm, globlanym stanem aplikacji? Brzmi jak 1 z
> kolokwium z inżynierii programowania.

Jak 1 z kolokwium to brzmi "To ogromna aplikacja" ;-) Dodatkowo, zmierzyłeś
że problem z wydajnością jest spowodowany przez select/poll? Jak masz tam
raptem kilka deskryptorów, to ciężko mi uwierzyć żeby to miało taki narzut
na wydajność i problem jest zapewne gdzie indziej. To, albo optymalizujesz
program na siłę.

Jak bardzo chcesz mieć nieblokujący read(2) oraz nie używać polla to możesz
zawsze ustawić przy pomocy fcntl(2) ustawić flagę O_NONBLOCK, i przy każdym
read(2) gdy nie będzie danych dostaniesz zwrotkę -1 oraz errno na EAGAIN.

-- 
.-----------------.-------------------.---------------------.------------------.
| Michal Lyszczek | Embedded C, Linux |   Company Address   |  .-. open source |
| +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 |  oo|  supporter  |
| https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\      &      |
| GPG FF1EBFE7E3A974B1 | Bits of Code | NIP:  813 349 58 78 |(\_;/) programer  |
`----------------------^--------------^---------------------^------------------'

[toc] | [prev] | [next] | [standalone]


#2252

Fromheby <heby@poczta.onet.pl>
Date2020-11-05 15:26 +0100
Message-ID<ro122h$9gj$1@dont-email.me>
In reply to#2251
On 05/11/2020 13:43, michal.lyszczek@bofc.pl wrote:
> Jak 1 z kolokwium to brzmi "To ogromna aplikacja" ;-)

To raczej problem z okolic "w ksiąze do programowani jest taki przykłąd 
na 3 linijki i działa", a potem masz bazę kodu na ~500MB (nie 
przesadzam) i zastanawiasz się jaki idiota wymyślił globalny handler 
który ma obsługiwać setki równoległych i kompletnie niezależnych części 
tej aplikacji.

> Dodatkowo, zmierzyłeś
> że problem z wydajnością jest spowodowany przez select/poll?

Apliakcja wykonuje miliony ::read( *foo, 4 );. Ogólnie wykonuje bardzo 
dużo małych read na namedpipe, bezustannie komunikujac się z inną 
apliakcją (inny proces). Przypuszczam że każdy ::read to switch 
kontekstu do kernela, albo przynajmniej jakieś cieżkie operacje na 
granicy kernel/user space.

> Jak masz tam
> raptem kilka deskryptorów, to ciężko mi uwierzyć żeby to miało taki narzut
> na wydajność i problem jest zapewne gdzie indziej. To, albo optymalizujesz
> program na siłę.

Nie, zostało to dokładnie rozpracowane. ::read jest znaczaco szybszy niż 
::poll->::read. Nawet w syntetycznym przykładzie da się to zobaczyć.

> Jak bardzo chcesz mieć nieblokujący read(2) oraz nie używać polla to możesz
> zawsze ustawić przy pomocy fcntl(2) ustawić flagę O_NONBLOCK, i przy każdym
> read(2) gdy nie będzie danych dostaniesz zwrotkę -1 oraz errno na EAGAIN.

Jesli teraz zawolam ::read ponownie i tak w kółko to właśnie przerobiłem 
komputer na grzejnik. A jak wstawie usleep... no własnie...

[toc] | [prev] | [next] | [standalone]


#2253

Frommichal.lyszczek@bofc.pl
Date2020-11-05 16:01 +0100
Message-ID<20201105150117.satsi3mksuslsmjg@marchewa.redembedded.pl>
In reply to#2252

[Multipart message — attachments visible in raw view] — view raw

On 2020-11-05 15:26:23, heby wrote:
> On 05/11/2020 13:43, michal.lyszczek@bofc.pl wrote:
> > Dodatkowo, zmierzyłeś
> > że problem z wydajnością jest spowodowany przez select/poll?
> 
> Apliakcja wykonuje miliony ::read( *foo, 4 );. Ogólnie wykonuje bardzo dużo
> małych read na namedpipe, bezustannie komunikujac się z inną apliakcją (inny
> proces). Przypuszczam że każdy ::read to switch kontekstu do kernela, albo
> przynajmniej jakieś cieżkie operacje na granicy kernel/user space.

I to jest twój główny problem. Wołanie ciągle read(2) z tak małym
parametrem count rozwala ci całkowicie wydajność. Jak sam piszesz,
każdy read(2) to przejście do kernela, a to jest bardzo droga operacja,
zwłaszcza jak robisz "miliony" tego.

Moja propozycja rozwiązania jak wcześniej. Niech 1 wątek czyta ciągle
w pętli deskryptor, z dużym rozmiarem (jak 10kB), jak będzie mniej
danych niż 10kB to read(2) wyjdzie i zwróci liczbę odczytanych bajtów.
Te dane wrzucaj na kolejkę synchronizowaną i niech sobie reszta wątków
czyta to nawet i po 1 bajcie. Wołanie funkcji jest kilka rzędów wielkości
szybsze niż wołanie syscalla.

-- 
.-----------------.-------------------.---------------------.------------------.
| Michal Lyszczek | Embedded C, Linux |   Company Address   |  .-. open source |
| +48 727 564 419 | Software Engineer | Leszczynskiego 4/29 |  oo|  supporter  |
| https://bofc.pl `----.--------------: 50-078 Wroclaw, Pol | /`'\      &      |
| GPG FF1EBFE7E3A974B1 | Bits of Code | NIP:  813 349 58 78 |(\_;/) programer  |
`----------------------^--------------^---------------------^------------------'

[toc] | [prev] | [next] | [standalone]


#2254

Fromheby <heby@poczta.onet.pl>
Date2020-11-05 19:16 +0100
Message-ID<ro1fic$idl$1@dont-email.me>
In reply to#2253
On 05/11/2020 16:01, michal.lyszczek@bofc.pl wrote:
> Moja propozycja rozwiązania jak wcześniej. Niech 1 wątek czyta ciągle
> w pętli deskryptor

Wtedy tracę coś co nazywa się synchronizacją.

Konkretnie: mam przeczytać 3 bajty, stwierdzić że to oznacza "skasuj 
plik", podpowiedzeć "ack", druga strona utworzy nowy itd itp.

Bezustannie są wymieniane polecenia pomiędzy dwoma procesami "zrób to i 
to","gotowe","zrób coś innego","a ty zrób to" itd.

Mam pewna przestrzeń na grupowanie tych read/write, ale to mi nijak nie 
pomaga w poprawnym, przerywalnym read, bo problem z ::poll dalej będzie 
istniał, tyle że zredukuje jego siłę spowolnienia.

Na razie wydaje mi się że nie da się tego zrobić dobrze, po prostu 
konepcja posixa z sygnałami jest gówno warta do większych zastosowań, 
::pool+::read są drogie, innych rozwiązań nie ma.

> Wołanie funkcji jest kilka rzędów wielkości
> szybsze niż wołanie syscalla.

Wszystko jest oczywiste, niesety messages latające po rurach mają na 
wyższym proziomie abstrakcji znaczenie synchronizujące i nie da się tego 
tak trywialnie z mienić.

[toc] | [prev] | [standalone]


Back to top | Article view | pl.comp.os.linux.programowanie


csiph-web