logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

C++ Builder: Jak uzyskać precyzyjny czas opóźnienia poniżej 1ms?

rokita 07 Gru 2005 08:02 3854 13
  • #1 2061207
    rokita
    Poziom 13  
    Posty: 96
    Pomógł: 2
    Ocena: 15
    Potrzebuje funkcję dla kompilatora C++ Buildre o działaniu jak Sleep() lecz o jeszcze krótszym diałaniu.
    Działająca na czasie krótszym niż 1mS.
    Spotkałem się z funkcją np nanosleep() ale niedziała ona pod tym kompilatorem.
    :cry:
  • Pomocny post
    #2 2061262
    fantom
    Poziom 31  
    Posty: 1649
    Pomógł: 108
    Ocena: 41
    Zapomnij.Zadna funkcja nie uspi ci procesu na czas krotszy niz 1 ms (a i to jest przesadzone).Po prostu nie ma takiej mozliwosci w zwyklych systemach operacyjnych (tym bardziej w windowsie).
  • #3 2061342
    rokita
    Poziom 13  
    Posty: 96
    Pomógł: 2
    Ocena: 15
    Mam pytanie czy podane niżej zagadnienia będą działać pod windows


    Opóźnianie za pomocą operacji I/O na porcie
    Inną metodą opóźniania o niewielkie ilości mikrosekund są operacje I/O na portach. Czytanie/zapisywanie jakiegokolwiek bajtu z/do portu 0x80 (patrz wyżej jak to się robi) powinno dać opóźnienie prawie dokładnie 1 mikrosekundy bez względu na typ procesora i jego prędkość. Możesz tak robić wiele razy aby uzyskać opóźnienie rzędu kilku mikrosekund. Sposób ten nie powinien wywoływać żadnych szkodliwych efektów ubocznych na żadnym standardowym komputerze (nawet niektóre moduły jądra go używają). W ten sposób uzyskują opóźnienie funkcje {in|out}[bw]_p() (zobacz asm/io.h).

    W zasadzie, instrukcje I/O na większości portów w obszarze 0-0x3ff zbierają prawie dokładnie 1 mikrosekundę, więc jeśli na przykład używasz bezpośrednio portu równoległego po prostu wykonaj kilka razy inb() z tego portu aby uzyskać opóźnienie.


    Opóźnianie za pomocą instrukcji asemblerowych
    Jeśli znasz typ procesora i prędkośc zegara w komputerze na którym będzie działał twój program, możesz na stałe uzyskać krótsze opóźnienia poprzez zastosowanie pewnych rozkazów asemblerowych (pamiętaj jednak że twój proces może się zacząć w dowolnym momencie wobec czego opóźnienia te mogą być większe od czasu do czasu) W tabeli poniżej wewnętrzna prędkość procesora określa liczbę cykli np dla procesora 50MHz (np. 486DX-50 lub 486DX2-50) jeden cykl zegara zajmuje 1/50000000 sekundy. (200 nanosekund).

    Instrukcja cykle zegara na 386 cykle zegara na 486
    nop 3 1
    xchg %ax,%ax 3 3
    or %ax,%ax 2 1
    mov %ax,%ax 2 1
    add %ax,0 2 1

    (Przykro mi ale niewiele wiem o Pentium. Pewnie podobnie do 486. Nie mogę znaleźć żadnej instrukcji która zajmowała by tylko jeden cykl zegara na 386. Jeśli możesz, używaj instrukcji jednocyklowych, w przeciwnym razie architektura potokowa (pipelining) używana w nowoczesnych procesorach może dać jeszcze krótsze czasy)
    Rozkazy nop i xchg z tabeli powyżej nie powinny powodować żadnych skutków ubocznych. Reszta może modyfikować rejestr znaczników ale nie powinno mieć to znaczenia bo gcc powinno to wykryć. Użycie nop jest dobrym wyborem.

    Aby skorzystać z powyższego, trzeba w swoim programie wywołać funkcję asm("instrukcja") Składnia instrukcji jest taka sama jak w tabeli powyżej. Jeśli chcesz użyć kilku instrukcji w jednym wywołaniu funkcji asm rozdziel je średnikami. Na przykład asm("nop ; nop ; nop ; nop") wywoła cztery razy instrukcję nop opóźniając nasz program o 4 cykle na 486 lub Pentium (lub 12 cykli na 386)

    Funkcja asm() jest przez gcc tłumaczona na wstawkę w asemblerze a więc nie ma zagrożenia przekroczenia limitu wywołań funkcji.

    Opóźnienia krótsze niż jeden cykl zegara nie są możliwe w architekturze Intel i386.


    Instrukcja rdtsc w Pentium
    Jeśli masz Pentium, możesz odczytać ilość cykli zegara które upłynęły od ostatniego uruchomienia komputera. Robi się to za pomocą takiego kodu w C:


    --------------------------------------------------------------------------------

    extern __inline__ unsigned long long int rdtsc()
    {
    unsigned long long int x;
    __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
    return x;
    }


    --------------------------------------------------------------------------------

    Możesz odczytywać tą wartość w celu opóźniania o dowolną ilość cykli.
  • Pomocny post
    #4 2061353
    fantom
    Poziom 31  
    Posty: 1649
    Pomógł: 108
    Ocena: 41
    Pierwsza opcja to zdecydowanie Linux i nijak sie to ma do windowsa.Druga czesc moze byc juz zastosowana do windowsa ale cudow bym sie raczej nie spodziewal.Pamietaj ze proces moze byc w kazdej chwili wywlaszczony dzieki czemu nie jestes w stanie zapewnic ze opoznienie bedzie zawsze wynosilo tyle ile chcesz.
  • Pomocny post
    #5 2061374
    Sam Sung
    Poziom 33  
    Posty: 2023
    Pomógł: 227
    Ocena: 602
    Metoda z RDTSC działa pod Windows. Tyle, że wynik jest obarczony pewnym błędem, bo wliczane są też instrukcje wykonywane przez inne procesy.
  • Pomocny post
    #6 2061377
    bis
    Poziom 21  
    Posty: 274
    Pomógł: 54
    Ocena: 3
    Będą działać ale wyłącznie gdy program będzie częścią niskopoziomowych funkcji kernela, ale i wtedy na współczesnych PC-tach nie uzyskasz predykcji czasu wykonania w 100% (poza koniecznością wyłączenia przerwań coś trzeba by zrobić z pipeline i cachami) W trybie normalnych aplikacji Windows zapomnij o takich rozwiązaniach, to nie będzie tak działało. Podstawowy problem tkwi w tym że instrukcje dostępu do portów są całkowicie wirtualizowane, w momencie ich wykonywania uruchamiany jest cały wątek kernela który pozwala (lub nie) na osiągnięcie realnego portu, zero kontroli ile to się bedzie wykonywało. Inne zagadnienie to podział czasu pomiędzy pozostałe procesy uruchomione w systemie (Windows potrafi całkiem skutecznie zajmować się sam sobą)
  • #7 2061404
    rokita
    Poziom 13  
    Posty: 96
    Pomógł: 2
    Ocena: 15
    A pod czystym Dos-em
  • Pomocny post
    #8 2061484
    bis
    Poziom 21  
    Posty: 274
    Pomógł: 54
    Ocena: 3
    Pod czystym DOS-em twoja aplikacja jest jedynym uruchomionym procesem (nie licząc przerwań, ale te można wyłączyć). Czysty DOS oznacza też prace wtrybie real procesora (żadnej wirtualizacji). W zasadzie masz do dyspozycji procesor w stanie "czystym". Prawie uzysksz spodziewane rezultat. Na ich dokładność wpłynie jedynie instruction pipelining i ew. cache. Mozna terz "podkręcać standardowe przerwania zegarowe, aby uzyskać synchronizację w twoim programie. W pewnym generatorze stosowałem się podkręcanie przerwania do 50kHz, ale praktycznie całość działania była zawarta w przerwaniach. Jedyny minus takiej metody "rozjeżdzanie się" zegara systemowego DOS (Dos bierze czas astronomiczny z RTC tylko na starcie, a potem odlicza tiki zegara).
  • #9 2061506
    Sam Sung
    Poziom 33  
    Posty: 2023
    Pomógł: 227
    Ocena: 602
    bis napisał:
    Jedyny minus takiej metody "rozjeżdzanie się" zegara systemowego DOS

    Jeśli zwiększamy częstotliwość zegara przez całkowitą wielokrotność, to można ten efekt bardzo łatwo wyeliminować - wystarczy przechwycić IRQ0 i z jego wnętrzna co n-ty raz wywoływać oryginalną procedurę obsługi.
  • #10 2061532
    rokita
    Poziom 13  
    Posty: 96
    Pomógł: 2
    Ocena: 15
    A może ktoś mi napisać taką funkcję
  • #11 2062247
    JanuszPulit
    Poziom 17  
    Posty: 175
    Pomógł: 14
    Ocena: 4
    A można wiedziec do czego ci to potrzebne?
  • #12 2064720
    rokita
    Poziom 13  
    Posty: 96
    Pomógł: 2
    Ocena: 15
    sterowanie silnikiem krokowym
    oraz sonar ultra dzwiękowy przez LPT
  • #13 2065636
    JanuszPulit
    Poziom 17  
    Posty: 175
    Pomógł: 14
    Ocena: 4
    NO to nie wiem czy nie sensowniej byloby skonstruować jakieś urządzenie peryferyjne które bedzie sobie generować samo takie przebiegi a z lpt bedzie tylko sterowanie lecialo - zaprzeganie calego PC do generowania sygnałów o takiej precyzji to dosyć karkołomna metoda.
  • #14 2067386
    rokita
    Poziom 13  
    Posty: 96
    Pomógł: 2
    Ocena: 15
    Tak też powoli się skłaniam do takiego obrania kierunk sprawy.

Podsumowanie tematu

✨ W dyskusji poruszono problem uzyskania precyzyjnego opóźnienia poniżej 1 ms w środowisku C++ Builder na systemie Windows. Standardowe funkcje takie jak Sleep() nie oferują tak krótkich opóźnień, a nanosleep() nie działa pod tym kompilatorem. W systemach Windows i innych współczesnych OS-ach nie jest możliwe dokładne usypianie procesu na czas krótszy niż 1 ms ze względu na mechanizmy zarządzania procesami i wirtualizację dostępu do portów. Propozycje obejmowały użycie operacji I/O na porcie 0x80, które mogą generować opóźnienia rzędu mikrosekund, jednak ich precyzja jest ograniczona przez wirtualizację i wywłaszczanie procesów. Metoda wykorzystująca instrukcję RDTSC (odczyt licznika cykli procesora) działa pod Windows, ale jest obarczona błędami wynikającymi z wykonywania instrukcji przez inne procesy. W trybie DOS, gdzie aplikacja ma pełną kontrolę nad procesorem i przerwaniami, można uzyskać bardziej precyzyjne opóźnienia, m.in. przez podkręcanie przerwań zegarowych, jednak jest to środowisko znacznie różniące się od Windows. W kontekście zastosowań takich jak sterowanie silnikiem krokowym i sonar ultradźwiękowy przez port LPT, sugerowano raczej wykorzystanie dedykowanego urządzenia peryferyjnego generującego sygnały o wymaganej precyzji, zamiast polegać na precyzyjnym generowaniu opóźnień przez PC.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA