Jak mierzyć czas w milisekundach za pomocą ANSI C?


127

Używając tylko ANSI C, czy istnieje sposób mierzenia czasu z dokładnością do milisekund lub większą? Przeglądałem czas.h, ale znalazłem tylko funkcje drugiej precyzji.


4
Zwróć uwagę na różnicę między precyzją a dokładnością. Możesz uzyskać czas z dokładnością do milisekund , biorąc czas w sekundach i mnożąc go przez 1000, ale to nie ma sensu. Funkcje precyzji ms niekoniecznie mają dokładność ms - chociaż generalnie mają dokładność lepszą niż 1 s.
Steve Jessop

2
Prosta odpowiedź brzmi NIE, ANSI C nie obsługuje dokładności milisekundowej lub wyższej. Bardziej złożona odpowiedź zależy od tego, co próbujesz zrobić - szczerze mówiąc, cała okolica to koszmar - nawet jeśli pozwolisz na korzystanie z powszechnie dostępnych funkcji Posix. Używasz terminu „miara”, więc zakładam, że interesuje Cię interwał, a nie „zegar ścienny”. Ale czy próbujesz zmierzyć bezwzględny okres czasu lub użycie procesora przez swój proces?
Dipstick

2
Chciałem tylko powiedzieć SOF, że znowu uratowałem mój bekon ;-)
corlettk

Odpowiedzi:


92

Nie ma funkcji ANSI C, która zapewnia rozdzielczość lepszą niż 1 sekunda, ale funkcja POSIX gettimeofdayzapewnia rozdzielczość mikrosekundową. Funkcja zegara mierzy tylko ilość czasu, jaką proces spędził na wykonywaniu i nie jest dokładna w wielu systemach.

Możesz użyć tej funkcji w następujący sposób:

struct timeval tval_before, tval_after, tval_result;

gettimeofday(&tval_before, NULL);

// Some code you want to time, for example:
sleep(1);

gettimeofday(&tval_after, NULL);

timersub(&tval_after, &tval_before, &tval_result);

printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);

To powraca Time elapsed: 1.000870na moim komputerze.


30
Niewielkie zastrzeżenie: gettimeofday () nie jest monotoniczne, co oznacza, że ​​może przeskakiwać (a nawet cofać się), na przykład, jeśli twój komputer próbuje utrzymać synchronizację z sieciowym serwerem czasu lub innym źródłem czasu.
Dipstick

3
Aby być precyzyjnym: W ISO C99 (co moim zdaniem jest zgodna w tej części z ANSI C) nie jest jeszcze gwarancją dowolnej rozdzielczości czasowej. (ISO C99, 7.23.1p4)
Roland Illig

6
Warto zauważyć, że timeval::tv_usecjest zawsze poniżej jednej sekundy, jest zapętlony. Tj. Aby wziąć różnice czasu większe niż 1 sekundę, należy:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
Alexander Malakhov

4
@Dipstick: Ale pamiętaj, że np. NTP nigdy nie przesuwa twojego zegara do tyłu, dopóki nie wyraźnie mu tego powiesz.
thejh

1
Logika odejmowania czasu Aleksandra Malachowa jest zawarta w timersubfunkcji. Możemy użyć tval_resultwartości (tv_sec i tv_usec) bez zmian.
x4444

48
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);

CLOCKS_PER_SEC jest ustawiony na 1000000 w wielu systemach. Wydrukuj jego wartość, aby upewnić się, zanim użyjesz go w ten sposób.
ysap

16
Ponieważ są to zegary na sekundę, nie ma znaczenia, jaka to wartość, wynikowa wartość z clock () / CLOCKS_PER_SEC będzie w sekundach (przynajmniej powinna). Dzielenie przez 1000 zamienia to na milisekundy.
David Young,

14
Zgodnie z podręcznikiem C Reference Manual, wartości clock_t mogą zawijać się od około 36 minut. Jeśli mierzysz długie obliczenia, musisz być tego świadomy.
CyberSkull

4
Uważaj również, że dzielenie liczb całkowitych CLOCKS_PER_SEC / 1000może być niedokładne, co może wpłynąć na wynik końcowy (chociaż z mojego doświadczenia CLOCKS_PER_SECzawsze wynikało wielokrotność 1000). Robi (1000 * clock()) / CLOCKS_PER_SECto mniej podatne na podział niedokładność, ale z drugiej strony jest bardziej podatny na przepełnienie. Tylko kilka kwestii do rozważenia.
Cornstalks

4
Czy to nie mierzy czasu procesora, a nie czasu ściany?
krs013

28

Zawsze używam funkcji clock_gettime (), zwracając czas z zegara CLOCK_MONOTONIC. Zwrócony czas to ilość czasu wyrażona w sekundach i nanosekundach od jakiegoś nieokreślonego punktu w przeszłości, takiego jak uruchomienie systemu w danej epoce.

#include <stdio.h>
#include <stdint.h>
#include <time.h>

int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
  return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
           ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}

int main(int argc, char **argv)
{
  struct timespec start, end;
  clock_gettime(CLOCK_MONOTONIC, &start);

  // Some code I am interested in measuring 

  clock_gettime(CLOCK_MONOTONIC, &end);

  uint64_t timeElapsed = timespecDiff(&end, &start);
}

5
clock_gettime () to nie ANSI C.
PowerApp101

1
Również CLOCK_MONOTONIC nie jest zaimplementowany w wielu systemach (w tym na wielu platformach Linux).
Dipstick

2
@ PowerApp101 Nie ma dobrego / niezawodnego sposobu ANSI C. Wiele innych odpowiedzi opiera się raczej na standardzie POSIX niż ANCI C. Biorąc to pod uwagę, wierzę, że dzisiaj. @Dipstick Dzisiaj uważam, że większość nowoczesnych platform [potrzebne źródło] obsługuje clock_gettime(CLOCK_MONOTONIC, ...)i jest nawet makro testowania funkcji _POSIX_MONOTONIC_CLOCK.
omninonsense

22

Wdrożenie rozwiązania przenośnego

Jak już tutaj wspomniano, nie ma odpowiedniego rozwiązania ANSI o wystarczającej precyzji dla problemu pomiaru czasu, chcę napisać o sposobach uzyskania przenośnego i, jeśli to możliwe, rozwiązania do pomiaru czasu o wysokiej rozdzielczości.

Zegar monotoniczny a znaczniki czasu

Ogólnie rzecz biorąc, istnieją dwa sposoby pomiaru czasu:

  • zegar monotoniczny;
  • aktualny (data) znacznik czasu.

Pierwsza używa monotonicznego licznika zegara (czasami nazywanego licznikiem tików), który zlicza tyknięcia z predefiniowaną częstotliwością, więc jeśli masz wartość tyknięć i częstotliwość jest znana, możesz łatwo przekonwertować tyknięcia na upływający czas. W rzeczywistości nie ma gwarancji, że zegar monotoniczny w jakikolwiek sposób odzwierciedla bieżący czas systemowy, może również liczyć tyknięcia od momentu uruchomienia systemu. Ale to gwarantuje, że zegar jest zawsze uruchamiany w sposób rosnący, niezależnie od stanu systemu. Zwykle częstotliwość jest związana ze sprzętowym źródłem o wysokiej rozdzielczości, dlatego zapewnia wysoką dokładność (zależy od sprzętu, ale większość współczesnego sprzętu nie ma problemów ze źródłami zegara o wysokiej rozdzielczości).

Drugi sposób zapewnia (datę) wartość czasu na podstawie bieżącej wartości zegara systemowego. Może mieć również wysoką rozdzielczość, ale ma jedną poważną wadę: na ten rodzaj wartości czasu mogą wpływać różne ustawienia czasu systemu, tj. Zmiana strefy czasowej, zmiana czasu letniego (DST), aktualizacja serwera NTP, hibernacja systemu i tak dalej. na. W niektórych okolicznościach można uzyskać ujemną wartość czasu, który upłynął, co może prowadzić do nieokreślonego zachowania. W rzeczywistości tego rodzaju źródło czasu jest mniej niezawodne niż pierwsze.

Tak więc pierwszą zasadą pomiaru odstępów czasu jest użycie zegara monotonicznego, jeśli to możliwe. Zwykle ma wysoką precyzję i jest niezawodny z założenia.

Strategia rezerwowa

Wdrażając rozwiązanie przenośne, warto rozważyć strategię awaryjną: użyj zegara monotonicznego, jeśli jest dostępny, i podejścia rezerwowego do znaczników czasu, jeśli w systemie nie ma zegara monotonicznego.

Windows

Jest świetny artykuł zatytułowany Uzyskiwanie znaczników czasu w wysokiej rozdzielczości w witrynie MSDN na temat pomiaru czasu w systemie Windows, w którym opisano wszystkie szczegóły, które mogą być potrzebne do obsługi oprogramowania i sprzętu. Aby uzyskać precyzyjny znacznik czasu w systemie Windows, należy:

  • zapytaj o częstotliwość zegara ( takty na sekundę) za pomocą QueryPerformanceFrequency :

    LARGE_INTEGER tcounter;
    LARGE_INTEGER freq;    
    
    if (QueryPerformanceFrequency (&tcounter) != 0)
        freq = tcounter.QuadPart;

    Częstotliwość timera jest ustalana podczas rozruchu systemu, więc musisz ją pobrać tylko raz.

  • Zapytaj o bieżącą wartość ticków za pomocą QueryPerformanceCounter :

    LARGE_INTEGER tcounter;
    LARGE_INTEGER tick_value;
    
    if (QueryPerformanceCounter (&tcounter) != 0)
        tick_value = tcounter.QuadPart;
  • przeskaluj znaczniki do upływającego czasu, tj. do mikrosekund:

    LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);

Według Microsoftu w większości przypadków nie powinno być żadnych problemów z takim podejściem na Windows XP i nowszych wersjach. Ale możesz też użyć dwóch rozwiązań awaryjnych w systemie Windows:

  • GetTickCount podaje liczbę milisekund, które upłynęły od uruchomienia systemu. Owija się co 49,7 dnia, dlatego należy uważać przy mierzeniu dłuższych interwałów.
  • GetTickCount64 to 64-bitowa wersja programu GetTickCount, ale jest dostępna począwszy od systemu Windows Vista i nowszych.

OS X (macOS)

OS X (macOS) ma własne jednostki czasu bezwzględnego Macha, które reprezentują zegar monotoniczny. Najlepszym sposobem na rozpoczęcie jest artykuł firmy Apple Technical Q&A QA1398: Mach Absolute Time Units, w którym opisano (z przykładami kodu), jak używać interfejsu API specyficznego dla Macha, aby uzyskać monotoniczne tiki. Istnieje również lokalne pytanie na ten temat, zwane alternatywą clock_gettime w systemie Mac OS X, które na końcu może sprawić, że będziesz nieco zdezorientowany, co zrobić z możliwym przepełnieniem wartości, ponieważ częstotliwość licznika jest używana w postaci licznika i mianownika. A więc krótki przykład, jak obliczyć upływ czasu:

  • pobierz licznik i mianownik częstotliwości zegara:

    #include <mach/mach_time.h>
    #include <stdint.h>
    
    static uint64_t freq_num   = 0;
    static uint64_t freq_denom = 0;
    
    void init_clock_frequency ()
    {
        mach_timebase_info_data_t tb;
    
        if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) {
            freq_num   = (uint64_t) tb.numer;
            freq_denom = (uint64_t) tb.denom;
        }
    }

    Musisz to zrobić tylko raz.

  • zapytaj o aktualną wartość ticka za pomocą mach_absolute_time:

    uint64_t tick_value = mach_absolute_time ();
  • przeskaluj takty do upływającego czasu, tj. do mikrosekund, używając wcześniej zadanego licznika i mianownika:

    uint64_t value_diff = tick_value - prev_tick_value;
    
    /* To prevent overflow */
    value_diff /= 1000;
    
    value_diff *= freq_num;
    value_diff /= freq_denom;

    Główną ideą zapobiegania przepełnieniu jest zmniejszenie taktów do pożądanej dokładności przed użyciem licznika i mianownika. Ponieważ początkowa rozdzielczość timera jest w nanosekundach, dzielimy ją przez, 1000aby uzyskać mikrosekundy. Możesz znaleźć to samo podejście, które zastosowano w time_mac.c Chromium . Jeśli naprawdę potrzebujesz dokładności w nanosekundach, rozważ przeczytanie Jak mogę używać mach_absolute_time bez przepełnienia? .

Linux i UNIX

clock_gettimeRozmowa jest najlepszym sposobem na każdym systemie POSIX w obsłudze. Może sprawdzać czas z różnych źródeł zegara, a ten, którego potrzebujemy, to CLOCK_MONOTONIC. Nie wszystkie systemy, które mają clock_gettimewsparcie CLOCK_MONOTONIC, więc pierwszą rzeczą, którą musisz zrobić, jest sprawdzenie jego dostępności:

  • jeśli _POSIX_MONOTONIC_CLOCKjest zdefiniowany do wartości, >= 0to znaczy, że CLOCK_MONOTONICjest dostępny;
  • jeśli _POSIX_MONOTONIC_CLOCKjest zdefiniowany 0to znaczy że należy dodatkowo sprawdzić czy działa w runtime, proponuję użyć sysconf:

    #include <unistd.h>
    
    #ifdef _SC_MONOTONIC_CLOCK
    if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
        /* A monotonic clock presents */
    }
    #endif
  • w przeciwnym razie zegar monotoniczny nie jest obsługiwany i powinieneś użyć strategii awaryjnej (patrz poniżej).

Użycie clock_gettimejest dość proste:

  • pobierz wartość czasu:

    #include <time.h>
    #include <sys/time.h>
    #include <stdint.h>
    
    uint64_t get_posix_clock_time ()
    {
        struct timespec ts;
    
        if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
            return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000);
        else
            return 0;
    }

    Skróciłem czas do mikrosekund.

  • obliczyć różnicę w stosunku do poprzedniej wartości czasu otrzymanej w ten sam sposób:

    uint64_t prev_time_value, time_value;
    uint64_t time_diff;
    
    /* Initial time */
    prev_time_value = get_posix_clock_time ();
    
    /* Do some work here */
    
    /* Final time */
    time_value = get_posix_clock_time ();
    
    /* Time difference */
    time_diff = time_value - prev_time_value;

Najlepszą strategią rezerwową jest użycie gettimeofdaywywołania: nie jest on monotoniczny, ale zapewnia całkiem niezłą rozdzielczość. Pomysł jest taki sam jak w przypadku clock_gettime, ale aby uzyskać wartość czasu, należy:

#include <time.h>
#include <sys/time.h>
#include <stdint.h>

uint64_t get_gtod_clock_time ()
{
    struct timeval tv;

    if (gettimeofday (&tv, NULL) == 0)
        return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
    else
        return 0;
}

Ponownie wartość czasu jest zmniejszana do mikrosekund.

SGI IRIX

IRIX ma clock_gettimewezwanie, ale brakuje CLOCK_MONOTONIC. Zamiast tego ma własne monotoniczne źródło zegara zdefiniowane jako, CLOCK_SGI_CYCLEktórego należy używać zamiast CLOCK_MONOTONICwith clock_gettime.

Solaris i HP-UX

Solaris ma własny interfejs timera o wysokiej rozdzielczości, gethrtimektóry zwraca bieżącą wartość timera w nanosekundach. Chociaż nowsze wersje Solaris mogą mieć clock_gettime, możesz trzymać się, gethrtimejeśli potrzebujesz obsługiwać stare wersje Solaris.

Użycie jest proste:

#include <sys/time.h>

void time_measure_example ()
{
    hrtime_t prev_time_value, time_value;
    hrtime_t time_diff;

    /* Initial time */
    prev_time_value = gethrtime ();

    /* Do some work here */

    /* Final time */
    time_value = gethrtime ();

    /* Time difference */
    time_diff = time_value - prev_time_value;
}

Brakuje HP-UX clock_gettime, ale obsługuje, z gethrtimektórego należy korzystać w taki sam sposób, jak w Solarisie.

BeOS

BeOS ma również własny interfejs timera o wysokiej rozdzielczości, system_timektóry zwraca liczbę mikrosekund, które upłynęły od momentu uruchomienia komputera.

Przykładowe użycie:

#include <kernel/OS.h>

void time_measure_example ()
{
    bigtime_t prev_time_value, time_value;
    bigtime_t time_diff;

    /* Initial time */
    prev_time_value = system_time ();

    /* Do some work here */

    /* Final time */
    time_value = system_time ();

    /* Time difference */
    time_diff = time_value - prev_time_value;
}

OS / 2

OS / 2 ma własny interfejs API do pobierania precyzyjnych znaczników czasu:

  • Zapytaj o częstotliwość timera (takty na jednostkę) z DosTmrQueryFreq(dla kompilatora GCC):

    #define INCL_DOSPROFILE
    #define INCL_DOSERRORS
    #include <os2.h>
    #include <stdint.h>
    
    ULONG freq;
    
    DosTmrQueryFreq (&freq);
  • zapytaj o bieżącą wartość ticków za pomocą DosTmrQueryTime:

    QWORD    tcounter;
    unit64_t time_low;
    unit64_t time_high;
    unit64_t timestamp;
    
    if (DosTmrQueryTime (&tcounter) == NO_ERROR) {
        time_low  = (unit64_t) tcounter.ulLo;
        time_high = (unit64_t) tcounter.ulHi;
    
        timestamp = (time_high << 32) | time_low;
    }
  • przeskaluj znaczniki do upływającego czasu, tj. do mikrosekund:

    uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);

Przykładowa realizacja

Możesz rzucić okiem na bibliotekę plibsys, która implementuje wszystkie opisane powyżej strategie (szczegóły w ptimeprofiler * .c).


„nie ma odpowiedniego rozwiązania ANSI o wystarczającej precyzji dla problemu pomiaru czasu”: jest C11 timespec_get: stackoverflow.com/a/36095407/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
To wciąż zły sposób mierzenia czasu wykonania kodu. timespec_getnie jest monotonna.
Alexander Saprykin

11

timespec_get z C11

Zwraca do nanosekund, w zaokrągleniu do rozdzielczości implementacji.

Wygląda na zdzierstwo ANSI z POSIX clock_gettime.

Przykład: a printfjest wykonywane co 100 ms na Ubuntu 15.10:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static long get_nanos(void) {
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);
    return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}

int main(void) {
    long nanos;
    long last_nanos;
    long start;
    nanos = get_nanos();
    last_nanos = nanos;
    start = nanos;
    while (1) {
        nanos = get_nanos();
        if (nanos - last_nanos > 100000000L) {
            printf("current nanos: %ld\n", nanos - start);
            last_nanos = nanos;
        }
    }
    return EXIT_SUCCESS;
}

Wersja standardowa C11 N1570 7.27.2.5 "Funkcja timespec_get mówi":

Jeśli podstawa ma wartość TIME_UTC, element członkowski tv_sec jest ustawiany na liczbę sekund od zdefiniowanej przez implementację epoki, obcięty do pełnej wartości, a element członkowski tv_nsec jest ustawiany na całkowitą liczbę nanosekund, zaokrągloną do rozdzielczości zegara systemowego. (321)

321) Chociaż obiekt struct timespec opisuje czasy z rozdzielczością nanosekund, dostępna rozdzielczość jest zależna od systemu i może być nawet większa niż 1 sekunda.

C ++ 11 ma również std::chrono::high_resolution_clock: C ++ Cross-Platform High-Resolution Timer

implementacja glibc 2.21

Można znaleźć sysdeps/posix/timespec_get.cjako:

int
timespec_get (struct timespec *ts, int base)
{
  switch (base)
    {
    case TIME_UTC:
      if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
        return 0;
      break;

    default:
      return 0;
    }

  return base;
}

tak wyraźnie:

  • tylko TIME_UTCjest obecnie obsługiwany

  • przekazuje do __clock_gettime (CLOCK_REALTIME, ts), który jest interfejsem API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

    Linux x86-64 ma clock_gettimewywołanie systemowe.

    Należy pamiętać, że nie jest to niezawodna metoda mikro-benchmarkingu, ponieważ:

    • man clock_gettimemówi, że miara ta może mieć nieciągłości, jeśli zmienisz ustawienia czasu systemowego podczas działania programu. Powinno to być oczywiście rzadkie zdarzenie i możesz je zignorować.

    • mierzy czas na ścianie, więc jeśli planista zdecyduje się zapomnieć o zadaniu, będzie ono działać dłużej.

    Z tych powodów getrusage()może być lepszym lepszym narzędziem do testów porównawczych POSIX, pomimo mniejszej maksymalnej precyzji w mikrosekundach.

    Więcej informacji: Mierz czas w Linuksie - czas vs zegar vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?


to jest właściwa odpowiedź od 2017 roku, nawet MSVC ma tę funkcję; jeśli chodzi o testy porównawcze, poszukaj czegoś, co czyta rejestr chipów (nowsze wersje procesorów x86 z rozszerzeniami PT i odpowiadające im nowsze wersje jądra / perf Linuksa)

4

Najlepsza precyzja, jaką można uzyskać, to użycie instrukcji "rdtsc" tylko dla x86, która może zapewnić rozdzielczość na poziomie zegara (nie musi oczywiście uwzględniać kosztu samego wywołania rdtsc, który można łatwo zmierzyć uruchomienie aplikacji).

Głównym haczykiem jest tutaj pomiar liczby zegarów na sekundę, co nie powinno być zbyt trudne.


3
Być może będziesz musiał martwić się koligacją procesora, ponieważ na niektórych komputerach możesz wysyłać wywołania RDTSC do więcej niż jednego procesora, a ich liczniki RDTSC mogą nie być zsynchronizowane.
Will Dean,

1
Co więcej, niektóre procesory nie mają monotonicznie rosnącego TSC - pomyśl o trybach oszczędzania energii, które zmniejszają częstotliwość procesora. Używanie RDTSC do wszystkiego oprócz bardzo krótkich zlokalizowanych czasów jest BARDZO złym pomysłem.
snemarch

Przy okazji, dryft rdzenia wspomniany przez @WillDean i użycie rdtsc do synchronizacji jest powodem, dla którego wiele gier nie działało na (wczesnych?) Wielordzeniowych procesorach AMD64 - musiałem ograniczyć koligację jednordzeniową na moim x2 4400+ dla wiele tytułów.
snemarch

2

Przyjęta odpowiedź jest wystarczająca, ale moje rozwiązanie jest prostsze: po prostu testuję w Linuksie, używam gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Poza tym gettimeofday, tv_secjest częścią sekundy, a tv_usecjest to mikrosekundy , a nie milisekundy .

long currentTimeMillis() {
  struct timeval time;
  gettimeofday(&time, NULL);

  return time.tv_sec * 1000 + time.tv_usec / 1000;
}

int main() {
  printf("%ld\n", currentTimeMillis());
  // wait 1 second
  sleep(1);
  printf("%ld\n", currentTimeMillis());
  return 0;
 }

To drukuje:

1522139691342 1522139692342, dokładnie sekundę.


-4

Pod oknami:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);

1
czy to jest ansi C zgodnie z żądaniem?
Gyom
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.