Jak wykonywać operację kilka milionów razy na sekundę?

0

Piszę sobie emulatorek własnego wymyślonego 8-bitowego procka (coś a-la DCPU-16 Notcha), no i natknąłem się na problem jak wykonywać jakąś procedurę/funkcję wielokrotnie w ciągu sekundy. Procek ma być taktowany zegarem 3MHz, czyli będzie wykonywać 3000000 operacji na sekundę.

Mógłbym użyć TTimer, problem jednak w tym że z jego pomocą mogę "odpalać" funkcję max. tysiąc razy na sekundę (przy wartości interval:=1), a ja potrzebuję niestety robić to więcej razy. Nie mogę też użyć "normalnej" pętli, bo procek wtedy będzie taktowany... zegarem realnego procesora użytkownika, a ma być zawsze 3MHz. Jakieś pomysły jak to zrobić?

0

Zrób pętlę i licz, ile zajmuje wykonanie pojedynczej instrukcji, potem to przeliczaj i dodawaj jakiegoś sleep'a.

0

Tylko że nie za bardzo wiem jak (liczenie ile trwa pojedyncza instrukcja)). Jakiś konkretny przykład?

0

Najprościej to coś w tym rodzaju:

Var Time: Cardinal;
{...}
While (true) Do
Begin
 Time := GetTickCount;

 [wykonanie danej instrukcji, inkrementacja pozycji etc.]

 Time := GetTickCount-Time;
 [w zmiennej `Time` masz ile czasu trwa wykonanie pojedynczej instrukcji (w milisekundach)]
End;
0
Var Time: Cardinal;
    Factor: Integer;
{...}
While (true) Do
Begin
 Inc(Factor);
 Factor := Factor mod 1000; //może być inna liczba, zamiast 1000;
 if Factor = 0 then Time := GetTickCount;
 
 [wykonanie danej instrukcji, inkrementacja pozycji etc.]
 
 if Factor = 0 then 
   begin
     Time := GetTickCount-Time;
      //w zmiennej `Time` masz ile czasu trwa wykonanie pojedynczej instrukcji (w milisekundach)]
   end;
End;
0

Zamiast GetTickCount lepiej Jak zmierzyć czas wykonywania operacji poza tym trochę niezbyt udana nazwa zmienne Time.

0

@kAzek, od 2002 roku minęło trochę czasu, teraz używa się metod QueryPerformanceCounter/QueryPerformanceFrequency

0

teraz używa się metod QueryPerformanceCounter/QueryPerformanceFrequency

To po pierwsze, używaj tego jeżeli chcesz mieć dokładne odstępy.

  1. Zrób pętle w której dajesz sleepa zależnego od tego czy mamy nadmiar czasu czy niedoczas.
  2. Licz ile czasu mineło od ostatniego wykonania naszej procedury z naszej pętli, wyliczaj ile to będzie taktów twojej maszyny i wykonuj ich tyle.
  3. W twojej pętli wykonuj conajmniej parę razy na sekundę Application.ProcessMessages co spowoduje że twoja appka wykona bieŻące zadania i odda ci kontrolę - tak możesz robić nieblokujące pętle.

A jeżeli potrzebujesz naprawdę szybkiego wykonania to można się pokusić o TThread ale to wyższa szkoła jazdy.

Patryk27 napisał(a)

Wtedy mamy jeszcze instrukcję rdtsc.

RDTSC jest niebezpieczne z paru powodów: Teoretycznie możliwe jest że w zależności od tego który procesor wykona twoje polecenie inna będzie wartość RDTSC - Musisz skojarzyć swój wątek z jednym procesorem. Poza tym współczesne systemy dysponują dokładniejszymi pomiarami np. na płycie głównej które można odczytać poprzez WinAPI.

0

No niestety, ale WinAPI całkowicie odpada, bo chcę mieć przenośny kod, aby później skompilować pod Linuksa, itp.

0
Darkhog napisał(a):

No niestety, ale WinAPI całkowicie odpada, bo chcę mieć przenośny kod, aby później skompilować pod Linuksa, itp.

To poszukaj przenośnych biblotek do dokładnego mierzenia czasu, reszta J.W.

0

Ja bym zrobił odwrotnie. Czekać jakiś kwant czasu (jakiś Sleep, ponoć ten windowsowy minimalnie śpi na 6ms) i zmierzyć ile czekaliśmy. Policzyć ile instrukcji powinien wykonać nasz wirtualny procesor w tym czasie i tyleż instrukcji wykonać.

Będziesz potrzebował dwie funkcje zależne od systemu operacyjnego:

  • dokładne mierzenie czasu
  • uśpienie programu (to od bidy można też TTimer'em zrealizować)
SysGetTime - pobranie aktualnego czasu w wysokiej rozdzielczości
SysSleep - uśpienie programu na kwant czasu
Run - wykonanie zadanej ilości wirtualnych tyknięć procesora
Freq - częstotliwość wirtualnego procesora
MaxCPUTime - maksymalna ilość wirtualnego czasu w jednej pętli

start = SysGetTime
for ...
    delta = min(SysGetTime - start, MaxCPUTime)
    start = start + delta
    Run(Freq / delta)
    Application.ProcessMessages
    if delta < MaxCPUTime
        SysSleep

// EDIT
Z timerem możesz spróbować coś na ten kształt:

start = SysGetTime
Timer.Interval = 1
Timer.Start
...
OnTick
    delta = min(SysGetTime - start, MaxCPUTime)
    start = start + delta
    Run(Freq / delta)
0

Ja bym zrobił odwrotnie.

Odwrotnie względem czego? Bo raczej nie mnie, robisz dokładnie to co napisałem.

Będziesz potrzebował dwie funkcje zależne od systemu operacyjnego

Bo nie ma bibliotek które zatrzymują wykonywanie albo mierzą czas i są OS independent.

Timer.Interval = 1

Pointless whatsoever. Zamiast męczyć windę krótkimi odstępami, lepiej ustawić ~50ms, nie będziesz dławić procesu swoim obliczaniem delty żeby wykonać parę taktów wirtualnego procesora.

Application.ProcessMessages

Wiesz ile może trwać wykonanie processMessages dla większych projektów? Dlatego znowuż dławisz tym proces.

Zdaję sobię sprawę, że twój model jest raczej pokazowy, ale mógłbyś o tym wspomnieć i powiedzieć że o tym wszystkim wcześniej wspomniałem.

1

Jeeeeeeju, Ty się zawsze potrafisz do czegoś przyczepić jeśli ktoś napisze więcej od Ciebie.

-123oho napisał(a):

Ja bym zrobił odwrotnie.

Odwrotnie względem czego? Bo raczej nie mnie, robisz dokładnie to co napisałem.
...
i powiedzieć że o tym wszystkim wcześniej wspomniałem.

Sugeruję aby nie dostosowywać czasu do instrukcji tylko dostosować instrukcje do czasu. Wykonywać instrukcje całymi pakietami w ilości dostosowanej do upływającego czasu. Same uśpienia mogą być nieregularne. Chodzi przede wszystkim o to, że czas w komputerze nie jest ciągły, a podzielony na pojedyncze takty. Chodzi o pokazanie, że nie musimy za wszelką cenę starać się, by poszczególne wirtualne takty rozkładały się w czasie równomiernie. Jest ich tak dużo, że wystarczy by reguła była zachowana w uśrednieniu, lokalnie mogą się pojawiać wahania, ważne by sumarycznie się zgadzało.

-123oho napisał(a):

Będziesz potrzebował dwie funkcje zależne od systemu operacyjnego

Bo nie ma bibliotek które zatrzymują wykonywanie albo mierzą czas i są OS independent.
Ewidentne czepialstwo o nie-wiadomo-co. Te biblioteki służą właśnie po to, by dostarczyć Ci owych "funkcji zależnych od systemu operacyjnego" w zunifikowany sposób. Nie ważne czy biblioteka, czy bezpośrednie wołanie API systemu. Chodziło mi o to, że nie obejdzie się nie wychodząc poza bibliotekę standardową. Ale oczywiście musisz się czepiać.

-123oho napisał(a):

Timer.Interval = 1

Pointless whatsoever. Zamiast męczyć windę krótkimi odstępami, lepiej ustawić ~50ms, nie będziesz dławić procesu swoim obliczaniem delty żeby wykonać parę taktów wirtualnego procesora.

Application.ProcessMessages

Wiesz ile może trwać wykonanie processMessages dla większych projektów? Dlatego znowuż dławisz tym proces.

Czas liczenia delty będzie rzędu czasu wykonania JEDNEJ wirtualnej instrukcji procesora. Poza tym w ciągu 6ms ma zostać wykonanych ok. 6ms*3MHz=2000 instrukcji a nie kilka. Natomiast co do Application.ProcessMessages - jeśli wołamy często (czyli zazwyczaj żadne komunikaty nie będą czekać) to wykonanie będzie szybkie (sprowadza się do wywołania PeekMessage). W dużych projektach Application.ProcessMessages trwa długo dla tego, że przetwarzanie pojedynczego komunikatu trwa długo. Komunikatów tak czy siak musimy obsłużyć zawsze tyle samo więc tak naprawdę nie ma większego znaczenia jak często wołamy Application.ProcessMessages.
Tak poza tym w przykładzie który podałem wszystko samo-reguluje się gdy zaczyna brakować mocy obliczeniowej komputera. Gdy nasza maszyna wirtualna przestaje nadążać wtedy dzieją się dwie rzeczy - sleep'y zaczynają być pomijane oraz Application.ProcessMessages jest wykonywane co raz rzadziej (bo co raz więcej wykonujemy instrukcji w jednym pakiecie).

A tak na prawdę zamiast gdybać trzeba zrobić testy i dostroić algorytm. Podałem tylko zalążek, główną ideę.

I nie - nie napisałem tego samego co Ty. Twoja odpowiedź nie była wyczerpująca.

0

Sugeruję aby nie dostosowywać czasu do instrukcji tylko dostosować instrukcje do czasu.

Ja sugeruję że jedno do drugiego i drugie do pierwszego...

Ewidentne czepialstwo o nie-wiadomo-co. Te biblioteki służą właśnie po to, by dostarczyć Ci owych "funkcji zależnych od systemu operacyjnego" w zunifikowany sposób. Nie ważne czy biblioteka, czy bezpośrednie wołanie API systemu. Chodziło mi o to, że nie obejdzie się nie wychodząc poza bibliotekę standardową. Ale oczywiście musisz się czepiać.

RDTSC jest OS-independent ;) . Ale tak, czepiam się, wiem.

Czas liczenia delty będzie rzędu czasu wykonania JEDNEJ wirtualnej instrukcji procesora. Poza tym w ciągu 6ms ma zostać wykonanych ok. 6ms*3MHz=2000 instrukcji a nie kilka. Natomiast co do Application.ProcessMessages - jeśli wołamy często (czyli zazwyczaj żadne komunikaty nie będą czekać) to wykonanie będzie szybkie (sprowadza się do wywołania PeekMessage). W dużych projektach Application.ProcessMessages trwa długo dla tego, że przetwarzanie pojedynczego komunikatu trwa długo. Komunikatów tak czy siak musimy obsłużyć zawsze tyle samo więc tak naprawdę nie ma większego znaczenia jak często wołamy Application.ProcessMessages.

Nie uwzględniłeś wszystkich rzeczy: calle do processmessages, wykonania dokładnych pomiarów czasu, wykonanie funkcji, krótkie (=mało opłacalne) pętle. Ale może trochę przesadziłem. Nie mniej nadal uważam że lepiej częściej ale na dłużej.

Jeeeeeeju, Ty się zawsze potrafisz do czegoś przyczepić jeśli ktoś napisze więcej od Ciebie.

Ja się przyczepiam gdy uważam że pierwszy coś zauważyłem a ktoś nie udzielił mi 'creditsów'. Tutaj tak właśnie uważałem.

0

RDTSC może i jest OS-independent, ale nie jest CPU-independent, a ja chcę żeby to chodziło nawet na takim złomie jak Pentium MMX. A jeśli nawet Pentium MMX ma ową instrukcję to większość procków AMD nie i tu jest szkopuł.

0

RDTSC może i jest OS-independent, ale nie jest CPU-independent, a ja chcę żeby to chodziło nawet na takim złomie jak Pentium MMX.

RDTSC jest wspierane od Pentium. Ale przecież jesteś zbanowany na googlach i poszukać nie możesz, bo za trudne. Lepiej powiedzieć jak ci się wydaje bo tobie się wydaje że coś wiesz.

A jeśli nawet Pentium MMX ma ową instrukcję to większość procków AMD nie i tu jest szkopuł.

Powodzenia w wymyślaniu rzeczywistości od nowa. Zaraz się okaże że kod trzeba w FPC oddzielnie na AMD kompilować bo jest platforma x86 (Intel) i AMD64, prawda?

Jak widać jednak nic nie wiesz ani o RDTSC, ani o x86, ani o AMD. Dodając do tego twoje przekonania że wszystko wiesz, można powiedzieć że nigdy nic ci się nie uda, bo będziesz za głupi żeby znaleźć cokolwiek co wykracza poza twoją obecną (niewielką) wiedzę. Twoje brednie przebijają głupoty Furiousa o przepełnieniu systemowego bufora...

Jak chcesz żeby ktoś/coś ci tutaj pomogło, to zacznij słuchać mądrzejszych, TBrain i google. Póki co nie skorzystałeś z ani jednej opcji.

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