Goto page Previous 1, 2, 3, 4, 5, 6 Next
Grzegorz Niemirowski
Guest
Thu Feb 14, 2019 10:44 am
Janusz <janusz_kk@o2.pl> napisał(a):
Quote:
A to " (czyli że wyłączy wszystko inne, co może zmienić jej
wartość w trakcie dostępu -- czy to wątki, czy przerwania, czy
zewnętrzny
sprzęt)."
nic takiego kompilator nie robi.
A to "`volatile` nie oznacza, że kompilator gwarantuje atomiczny dostęp do
zmiennej (czyli że wyłączy"?
Nie dość, że masz problem z czytaniem ze zrozumieniem, to jeszcze wyciąłeś
kluczowy fragment, który pokazuje, że czepiasz się bez sensu.
Quote:
Przerwania są wyłączane tylko w atomic blok.
Tak właśnie napisał Queequeg.
Quote:
Nie, wcale o tym nie pisałeś.
Masz popsuty czytnik?
--
Grzegorz Niemirowski
https://www.grzegorz.net/
J.F.
Guest
Thu Feb 14, 2019 11:35 am
Użytkownik "Mateusz Viste" napisał w wiadomości grup
dyskusyjnych:5c650c2f$0$5597$426a74cc@news.free.fr...
On Wed, 13 Feb 2019 23:07:39 +0100, Grzegorz Niemirowski wrote:
Quote:
J.F. <jfox_xnospamx@poczta.onet.pl> napisał(a):
to wątki, czy przerwania, czy zewnętrzny sprzęt).
No ale czy nie powinien ?
Ma kontrolować wszystkie te mechanizmy? Byłaby niezła paranoja.
Może
niech jednak kompilator zajmie się kompilacją...
Dokładnie. Automatyczne majstrowanie przy przerwaniach podchodziłoby
pod
sabotaż, a nie kompilację
Cos w tym jest, ale z drugiej strony - skoro uzywamy volatile, to
wiadomo ze zmienna moze sie zmieniac w przerwaniach czy w inny
niekontrolowany sposob,
i co - kompilator to olewa ?
No coz, przejsc na ARM i zapomniec o problemie ... na chwile, bo i tam
sie moga podobne cuda zrobic, jak zmiennych wiecej :-)
J.
J.F.
Guest
Thu Feb 14, 2019 11:42 am
Użytkownik "Queequeg" napisał w wiadomości grup
dyskusyjnych:2e88332d-3364-4f9b-b65d-7ecbd98a9931@trust.no1...
Irek.N. <tajny@jakis.taki.jest.pl> wrote:
Quote:
A swoja droga - czy Keil sam ich nie wylacza ? Dla zmiennych
volatile
powinien.
Niestety ale ignoruje zupełnie volatile, a nie powinien moim
zdaniem.
Skąd wiesz?
Zrób test. Napisz funkcję:
int i;
void fn(void) { do { } while (i != 42); }
Skompiluj z optymalizacją, zobacz kod. Potem dodaj volatile, zobacz
kod.
Sprawdziłem na raspi (arm), gcc -O2 -S.
Bez volatile:
ldr r3, .L5
ldr r3, [r3] ; tu ładuje zmienną do rejestru
.L2:
cmp r3, #42 ; tu porównuje rejestr
bne .L2 ; tu skacze z powrotem do porównania
kiepski optymalizator ... czemu porownuje w petli ?
Quote:
Jeszcze ciekawiej się robi, gdy dodasz modyfikator `static`. Bez
volatile,
arm:
.L2:
b .L2
I to jest prawidlowe :-)
Quote:
Dlaczego tak? Bo kompilator wie, że inna jednostka kompilacji nie
zmieni
wartości tej zmiennej (bo jest statyczna, widoczna tylko w obrębie
danej
jednostki kompilacji), więc nie ma sensu żadne sprawdzanie, bo
zmienna i
tak jest zero (bo zmienne globalne są inicjalizowane zerami; spróbuj
zamiast 42 wstawić 0 i zobacz, jaki kod wtedy kompilator wygeneruje).
wstaw 41.
Albo w jakiejs funkcji wpisz i=40 ...
J.
Grzegorz Niemirowski
Guest
Thu Feb 14, 2019 11:49 am
J.F. <jfox_xnospamx@poczta.onet.pl> napisał(a):
Quote:
Cos w tym jest, ale z drugiej strony - skoro uzywamy volatile, to wiadomo
ze zmienna moze sie zmieniac w przerwaniach czy w inny niekontrolowany
sposob,
i co - kompilator to olewa ?
Nie olewa przecież, bo przestaje względem niej wykonywać optymalizacje.
Natomiast nie robi nadgorliwych przeróbek, które spowodowałyby
niekontrolowane zachowanie programu. Naprawdę chciałbyś żeby kompilator
gdzieś po cichu włączał albo wyłączał Ci przerwania?
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Queequeg
Guest
Thu Feb 14, 2019 11:57 am
Janusz <janusz_kk@o2.pl> wrote:
Quote:
Wbrew intuicji -- nie

Modyfikator `volatile` oznacza tylko tyle, że
kompilator nie może robić założeń co do zawartości zmiennej opatrzonej tym
modyfikatorem, bo zmienna może zmienić wartość poza ścieżką wykonania (np.
jest to sprzętowy rejestr, lub inny wątek ją modyfikuje, lub właśnie
przerwanie). `volatile` nie oznacza, że kompilator gwarantuje atomiczny
dostęp do zmiennej (czyli że wyłączy wszystko inne, co może zmienić jej
wartość w trakcie dostępu -- czy to wątki, czy przerwania, czy zewnętrzny
sprzęt).
G..o prawda.
A co konkretnie?
A to " (czyli że wyłączy wszystko inne, co może zmienić jej wartość w
trakcie dostępu -- czy to wątki, czy przerwania, czy zewnętrzny
sprzęt)."
nic takiego kompilator nie robi.
Ale zauważyłeś, że przed tym jest napisane: "`volatile` nie oznacza, że"?
Quote:
Przerwania są wyłączane tylko w atomic blok.
I nigdzie nie napisałem inaczej.
Quote:
jakbyś zerknął do kodu to byś zobaczył że dla volatile kompilator
nigdy nie robi atomic bloków, to musi programista w kodzie
zrobić.
Tak (i prawidłowo), o tym właśnie piszę.
Nie, wcale o tym nie pisałeś.
Przeczytaj jeszcze raz fragment, który (celowo) zostawiłem niewycięty na
górze. Ze zrozumieniem.
--
Eksperymentalnie:
http://facebook.com/groups/pl.misc.elektronika
Queequeg
Guest
Thu Feb 14, 2019 12:59 pm
J.F. <jfox_xnospamx@poczta.onet.pl> wrote:
Quote:
Cos w tym jest, ale z drugiej strony - skoro uzywamy volatile, to
wiadomo ze zmienna moze sie zmieniac w przerwaniach czy w inny
niekontrolowany sposob, i co - kompilator to olewa ?
Ale co kompilator może zrobić, jak sam procesor nie obsługuje atomicznego
dostępu do tej zmiennej (bo np. jest 8-bitowy, a zmienna 16-bitowa)?
Zresztą nawet w przypadku zmiennych o rozmiarze równym szerokości
magistrali danych nie ma gwarancji chociażby w przypadku operacji
read-modify-write. Przykład:
#v+ test.c
volatile int i;
void fn(void) { ++i; }
#v-
#v+ test.s
ldr r3, [r2] ; odczyt zmiennej z pamięci do rejestru r3
add r3, r3, #1 ; dodanie do rejestru r3 liczby 1
str r3, [r2] ; zapis rejestru r3 z powrotem do pamięci
#v-
Zmienna może się zmienić między każdą z tych instrukcji.
Bardziej realny przykład, AVR. Chcemy zmienić stan bitu 2 w porcie na
przeciwny.
#v+ avr.c
#include <avr/io.h>
void fn(void) { PORTB ^= _BV(2); }
#v-
Kompilacja: avr-gcc -mmcu=atmega8 -O2 -S avr.c
#v+ avr.s
in r24,0x18 ; załadowanie adresu portu do r24
ldi r25,lo8(4) ; załadowanie wartości _BV(2) do r25
eor r24,r25 ; wykonanie r24 xor r25, zapis do r24
out 0x18,r24 ; wysłanie wartości z r24 do portu
#v-
Między każdą z tych instrukcji również może wystąpić przerwanie, które np.
ustawi inny bit w tym porcie, który to bit zostanie ładnie wyczyszczony
podczas otatniej operacji (zapisu r24 do portu).
Quote:
No coz, przejsc na ARM i zapomniec o problemie ...
Czemu? Co ma do tego ARM? Chodzi o szerokość magistrali danych? To
rozwiązuje tylko jeden problem, ale inne (chociażby ten pierwszy
przykład wyżej) pozostają.
--
Eksperymentalnie:
http://facebook.com/groups/pl.misc.elektronika
J.F.
Guest
Thu Feb 14, 2019 1:07 pm
Użytkownik "Queequeg" napisał w wiadomości grup
dyskusyjnych:4217c591-37b4-4703-974b-66fbe15cdae0@trust.no1...
J.F. <jfox_xnospamx@poczta.onet.pl> wrote:
Quote:
Dlaczego tak? Bo kompilator wie, że inna jednostka kompilacji nie
zmieni
wartości tej zmiennej (bo jest statyczna, widoczna tylko w obrębie
danej
jednostki kompilacji), więc nie ma sensu żadne sprawdzanie, bo
zmienna i
tak jest zero (bo zmienne globalne są inicjalizowane zerami;
spróbuj
zamiast 42 wstawić 0 i zobacz, jaki kod wtedy kompilator
wygeneruje).
wstaw 41.
Będzie to samo, bo 41 to nie 0.
ale bylo
void fn(void) { do { } while (i != 42); }
Quote:
Albo w jakiejs funkcji wpisz i=40 ...
Ok, robimy:
#v+ test.c
int i;
ale jeszcze static
Quote:
void fn1(void) { while (i != 42) ; }
void fn2(void) { i = 40; }
#v-
gcc -O2 -S test.c
#v+ test.s, fn1
ldr r3, .L5 ; ładuje do r3 adres zmiennej
ldr r3, [r3] ; ładuje do r3 wartość zmiennej
.L2:
cmp r3, #42 ; porównuje zapamiętaną wartość z 42
bne .L2 ; skacze do porównania
#v-
#v+ test.s, fn2
ldr r3, .L8 ; ładuje do r3 adres zmiennej
mov r2, #40 ; ładuje do r2 wartość 40
str r2, [r3] ; zapisuje wartość r2 pod adresem zmiennej
#v-
W sumie poprawnie - jeszcze nie wie ktore funkcje beda uzyte, to musi
skompilowac poprawnie.
Ale trzeba bylo sprawdzic ze static.
A potem ...
static void fn2(void) { i = 40; }
i jej nie wywolywac.
Tudziez
static void fn2(void) { i = 40; }
main()
{
fn() ;
fn2();
}
No i ciekawe, na ile dobrze zoptymalizuje :-)
J.
J.F.
Guest
Thu Feb 14, 2019 1:11 pm
Użytkownik "Queequeg" napisał w wiadomości grup
dyskusyjnych:c6cda8fa-82c3-48a0-a47d-aba030a5831b@trust.no1...
J.F. <jfox_xnospamx@poczta.onet.pl> wrote:
Quote:
Cos w tym jest, ale z drugiej strony - skoro uzywamy volatile, to
wiadomo ze zmienna moze sie zmieniac w przerwaniach czy w inny
niekontrolowany sposob, i co - kompilator to olewa ?
Ale co kompilator może zrobić, jak sam procesor nie obsługuje
atomicznego
dostępu do tej zmiennej (bo np. jest 8-bitowy, a zmienna 16-bitowa)?
To samo, co programista ma zrobic :-)
Quote:
Zresztą nawet w przypadku zmiennych o rozmiarze równym szerokości
magistrali danych nie ma gwarancji chociażby w przypadku operacji
read-modify-write. Przykład:
#v+ test.c
volatile int i;
void fn(void) { ++i; }
#v-
#v+ test.s
ldr r3, [r2] ; odczyt zmiennej z pamięci do rejestru r3
add r3, r3, #1 ; dodanie do rejestru r3 liczby 1
str r3, [r2] ; zapis rejestru r3 z powrotem do pamięci
#v-
Zmienna może się zmienić między każdą z tych instrukcji.
Cos w tym jest.
Quote:
No coz, przejsc na ARM i zapomniec o problemie ...
Czemu? Co ma do tego ARM? Chodzi o szerokość magistrali danych? To
rozwiązuje tylko jeden problem,
Tak, problemu z int nie bedzie :-)
Quote:
ale inne (chociażby ten pierwszy przykład wyżej) pozostają.
Ciezkie jest zycie programisty.
I co sie dziwic, ze MS w C# zrobil stringi niemodyfikowalne ...
J.
Queequeg
Guest
Thu Feb 14, 2019 1:14 pm
J.F. <jfox_xnospamx@poczta.onet.pl> wrote:
Quote:
.L2:
cmp r3, #42 ; tu porównuje rejestr
bne .L2 ; tu skacze z powrotem do porównania
kiepski optymalizator ... czemu porownuje w petli ?
Tak... też nie rozumiem tej logiki. Albo jedno albo drugie, a tu jest
takie pół na pół :)
Quote:
Dlaczego tak? Bo kompilator wie, że inna jednostka kompilacji nie zmieni
wartości tej zmiennej (bo jest statyczna, widoczna tylko w obrębie danej
jednostki kompilacji), więc nie ma sensu żadne sprawdzanie, bo zmienna i
tak jest zero (bo zmienne globalne są inicjalizowane zerami; spróbuj
zamiast 42 wstawić 0 i zobacz, jaki kod wtedy kompilator wygeneruje).
wstaw 41.
Będzie to samo, bo 41 to nie 0.
Quote:
Albo w jakiejs funkcji wpisz i=40 ...
Ok, robimy:
#v+ test.c
int i;
void fn1(void) { while (i != 42) ; }
void fn2(void) { i = 40; }
#v-
gcc -O2 -S test.c
#v+ test.s, fn1
ldr r3, .L5 ; ładuje do r3 adres zmiennej
ldr r3, [r3] ; ładuje do r3 wartość zmiennej
..L2:
cmp r3, #42 ; porównuje zapamiętaną wartość z 42
bne .L2 ; skacze do porównania
#v-
#v+ test.s, fn2
ldr r3, .L8 ; ładuje do r3 adres zmiennej
mov r2, #40 ; ładuje do r2 wartość 40
str r2, [r3] ; zapisuje wartość r2 pod adresem zmiennej
#v-
Możemy sprawdzić to empirycznie. Program:
#v+
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
static int g_signo;
static void sighnd(int signo)
{
printf("Wywolano sighandler\n");
g_signo = signo;
}
int main(void)
{
signal(SIGALRM, sighnd);
alarm(2);
printf("Czekanie na sygnal\n");
while (!g_signo) ;
printf("Odebrano sygnal\n");
return 0;
}
#v-
Kod bez optymalizacji (gcc -O0) widzi zmianę w g_signo. Kod z -O1 i -O2
nie widzi. Dodanie `volatile` do definicji zmiennej sprawia, że kod nawet
z optymalizacjami widzi zmianę.
Testowane na raspberry (Raspbian), gcc 4.9.2.
--
Eksperymentalnie:
http://facebook.com/groups/pl.misc.elektronika
J.F.
Guest
Thu Feb 14, 2019 1:15 pm
Użytkownik "Grzegorz Niemirowski" napisał w wiadomości grup
dyskusyjnych:q43h41$kh6$1@node1.news.atman.pl...
J.F. <jfox_xnospamx@poczta.onet.pl> napisał(a):
Quote:
Cos w tym jest, ale z drugiej strony - skoro uzywamy volatile, to
wiadomo ze zmienna moze sie zmieniac w przerwaniach czy w inny
niekontrolowany sposob,
i co - kompilator to olewa ?
Nie olewa przecież, bo przestaje względem niej wykonywać
optymalizacje. Natomiast nie robi nadgorliwych przeróbek, które
spowodowałyby niekontrolowane zachowanie programu. Naprawdę chciałbyś
żeby kompilator gdzieś po cichu włączał albo wyłączał Ci przerwania?
Na kilka instrukcji ... czemu nie.
Szczegolnie, ze ... sam musze je wylaczyc, jesli nie chce takich
niespodzianek.
To co przyniesie wiecej szkody - jak kompilator bedzie je wylaczal
automatycznie, czy jak ja zapomne ? :-)
Natomiast na niektorych procesorach moze byc problem z odtworzeniem
stanu przerwan,
no i kwestia robienia tego w procedurze obslugi przerwania czy
zagniezdzania przerwan.
J.
Grzegorz Niemirowski
Guest
Thu Feb 14, 2019 1:36 pm
J.F. <jfox_xnospamx@poczta.onet.pl> napisał(a):
Quote:
Na kilka instrukcji ... czemu nie.
Których instrukcji? Wszystkich odwołujących się do tej zmiennej? Nadmierna,
ukryta ingerencja w kod. Efektem będzie np. niepożądany jitter. Kod robi się
mniej przewidywalny.
Quote:
Szczegolnie, ze ... sam musze je wylaczyc, jesli nie chce takich
niespodzianek.
Albo przepisać tak, żeby wyłączanie nie było konieczne.
Quote:
To co przyniesie wiecej szkody - jak kompilator bedzie je wylaczal
automatycznie, czy jak ja zapomne ?
Zdecydowanie jak kompilator będzie wyłączał automatycznie. Ty sobie
przypomnisz i będzie OK. A miliony programistów będą się męczyć z dziwnym,
nieprzewidywalnym zachowaniem kompilatora, który próbuje być mądrzejszy od
nich.
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Grzegorz Niemirowski
Guest
Thu Feb 14, 2019 2:01 pm
antispam@math.uni.wroc.pl <antispam@math.uni.wroc.pl> napisał(a):
Quote:
Januszowi wlasnie chodzilo o to ze nie: 'volatile' w intencji sluzy
do obsugi sprzetu: jak np. urzadzenie zlicza ile razy byl zrobiony
zapis to wynik ma byc taki jak wynika jak napisal programista,
nic mniej, nic wiecej. Sprzet zwykle nie wymaga atomicznego
zapisu wiec 'volatile' nie daje takiej gwarancji. 'volatile'
moze tez byc uzyte do innych celow, dlatego standard zawiera
bardzo ogolne sformulowanie. Ale _nie_ ma zadnej gwarancji
atomicznosci.
Ależ czy ktoś pisał inaczej?
PS. tin nie obsługuje polskich liter?
--
Grzegorz Niemirowski
https://www.grzegorz.net/
Queequeg
Guest
Thu Feb 14, 2019 2:25 pm
J.F. <jfox_xnospamx@poczta.onet.pl> wrote:
Quote:
Na kilka instrukcji ... czemu nie.
Ale skąd kompilator ma wiedzieć, że dana zmienna jest akurat modyfikowana
w przerwaniu?
--
Eksperymentalnie:
http://facebook.com/groups/pl.misc.elektronika
Guest
Thu Feb 14, 2019 2:52 pm
Grzegorz Niemirowski <gnthexfiles@poczta.onet.pl> wrote:
Quote:
Janusz <janusz_kk@o2.pl> napisa?(a):
A to " (czyli ?e wy??czy wszystko inne, co mo?e zmieni? jej
warto?? w trakcie dost?pu -- czy to w?tki, czy przerwania, czy
zewn?trzny
sprz?t)."
nic takiego kompilator nie robi.
A to "`volatile` nie oznacza, ?e kompilator gwarantuje atomiczny dost?p do
zmiennej (czyli ?e wy??czy"?
Nie do??, ?e masz problem z czytaniem ze zrozumieniem, to jeszcze wyci??e?
kluczowy fragment, kt?ry pokazuje, ?e czepiasz si? bez sensu.
Januszowi wlasnie chodzilo o to ze nie: 'volatile' w intencji sluzy
do obsugi sprzetu: jak np. urzadzenie zlicza ile razy byl zrobiony
zapis to wynik ma byc taki jak wynika jak napisal programista,
nic mniej, nic wiecej. Sprzet zwykle nie wymaga atomicznego
zapisu wiec 'volatile' nie daje takiej gwarancji. 'volatile'
moze tez byc uzyte do innych celow, dlatego standard zawiera
bardzo ogolne sformulowanie. Ale _nie_ ma zadnej gwarancji
atomicznosci.
--
Waldek Hebisch
Queequeg
Guest
Thu Feb 14, 2019 2:59 pm
J.F. <jfox_xnospamx@poczta.onet.pl> wrote:
Quote:
Ale co kompilator może zrobić, jak sam procesor nie obsługuje
atomicznego dostępu do tej zmiennej (bo np. jest 8-bitowy, a zmienna
16-bitowa)?
To samo, co programista ma zrobic
Programista ma kontekst, którego nie ma kompilator. Programista wie, kiedy
chce mieć sekcję krytyczną (która nie musi obejmować wyłącznie atomicznego
dostępu do zmiennej szerszej niż magistrala adresowa).
Quote:
Zmienna może się zmienić między każdą z tych instrukcji.
Cos w tym jest.
Jest jest... po to są sekcje krytyczne.
Quote:
Czemu? Co ma do tego ARM? Chodzi o szerokość magistrali danych? To
rozwiązuje tylko jeden problem,
Tak, problemu z int nie bedzie
Z odczytem int nie, ale np. z read-modify-write już tak.
Quote:
ale inne (chociażby ten pierwszy przykład wyżej) pozostają.
Ciezkie jest zycie programisty.
A to swoją drogą... ale raczej przez ludzi a nie przez komputery :)
Trzeba po prostu pamiętać, że (przynajmniej w przypadku C i C++)
kompilator nie tłumaczy kodu na język maszynowy jeden do jednego. Kod to
tylko pewien abstrakcyjny opis, który kompilator może traktować z dosyć
dużą dowolnością. Dochodzą do tego chociażby zagadnienia związane z
reorderingiem.
Przykładowo:
https://www.nongnu.org/avr-libc/user-manual/optimization.html
1. Dzielimy (powolna operacja)
2. Wyłączamy przerwania cli
3. Zapisujemy wynik dzielenia do zmiennej (szybka operacja)
4. Włączamy przerwania
A optymalizator twierdzi, że wyłączy sobie przerwania przed dzieleniem, bo
tak mu mnieszy kod wychodzi :)
Gdy zagłębimy się w optymalizowanie "undefined behavior", to robi się
jeszcze ciekawiej. Przykładowo:
#v+
void fn(int *p)
{
int a = *p; // martwy kod
if (p == 0) return; // nadmiarowe sprawdzenie
*p = 1;
}
#v-
Spodziewalibyśmy się, że `if (p == 0) return;` nie zostanie usunięte, ale
ponieważ programista wcześniej dokonał dereferencji, a dereferencja null
pointera jest UB, to kompilator może usunąć ten drugi, nadmiarowy test bo
zakłada, że nie ma UB (czyli że p nie może być 0). Moje gcc nie usuwa, bo
najpierw usuwa martwy kod, ale wcale nie musi -- inna wersja lub inny
kompilator może zachowywać się inaczej i najpierw usunąć nadmiarowe
sprawdzenie, a dopiero potem martwy kod.
Kolejny przykład.
#v+
#include <limits.h>
#include <stdio.h>
int fn(int i)
{
if (i + 1 > i)
printf("Nie ma integer overflow\n");
else
printf("Uwaga: integer overflow\n");
}
int main(void)
{
fn(INT_MAX);
return 0;
}
#v-
Kompilujemy z -O0, dostajemy wynik, że jest integer overflow. Kompilujemy
z -O2, optymalizator stwierdza, że przepełnienie typu ze znakiem jest UB,
więc usuwa nam ten warunek (ustawia go na true).
Quote:
I co sie dziwic, ze MS w C# zrobil stringi niemodyfikowalne ...
W C# pisałem coś tylko raz i tylko dlatego, że musiałem

Zupełnie nie
moja działka.
--
Eksperymentalnie:
http://facebook.com/groups/pl.misc.elektronika
Goto page Previous 1, 2, 3, 4, 5, 6 Next