2

私は C で小さなシミュレーションを書いており、特定のことが毎秒N回発生することを望んでいます。

現在、次のコードがあります。

// Equal to 1 / N
struct timespec tick_period = (struct timespec){sec, nsec};

...

for(;;) {
    tick();
    nanosleep(&tick_period, NULL);
}

現在、このコードはメソッドの実行にかかる時間を考慮していないtickため、徐々に 1 秒あたりのティック数が少しずれます。

私は2つのことを達成したいと思います:

  • スキューなしで 1 秒あたりtick N回実行する。
  • 実行tickに時間がかかりすぎて、コンピューターが1 秒間にN回の実行に対応できない場合は、ティックの頻度を調整する方法があります。

これらのことを達成するためのよく知られている/受け入れられている方法はありますか?

4

2 に答える 2

2

標準的な方法があるかどうかはわかりませんが、これは私が使用した方法です。

簡単に言うと、フレームレートの間隔を決定し、この間隔に従って仮想クロックを進めます。各フレームで、「作業」を完了するのにかかった時間を決定します。フレーム間隔から作業時間を差し引くと、次の間隔に到達するまでに必要な睡眠時間がわかります。

これだけで、「スキューなしで 1 秒あたり N 回のティック」が提供されます。自動修正なので、たまに遅れても、追いつくまでワークロードが軽くなるとスピードアップします。

ワークロードに合わせてフレーム レートを調整する場合は、アイドル時間を調べて、それに応じて間隔を調整します。

このコードは、これを示す小さなプログラムです。Linux で動作しますが、OS X については知りません。1/2 秒間隔を選択したのは、動作を見てタイミングがスムーズに見えるかどうかを確認できるためです。

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

/* frame interval is in microseconds */
#define INTERVAL  500000
/* use a variable so it is adjustable */
int interval = INTERVAL;
int ideal = 0;

struct timeval start; /* start time */
void init_time()
{
    gettimeofday(&start, 0);
    wait((1000000 - start.tv_usec));
    gettimeofday(&start, 0);
    ideal = start.tv_usec; /* initialize ideal time */
}

int get_time()
{
    struct timeval tv;
    gettimeofday(&tv, 0);
    tv.tv_sec -= start.tv_sec; /* normalize to start time */
    int usec = (tv.tv_sec * 1000000) + (tv.tv_usec);
    return usec;
}

int wait(int usec)
{
    struct timespec ts = { 0, usec * 1000 };
    if (nanosleep(&ts, 0) != 0) {
        printf("ERROR: nanosleep interrupted\n");
    }
}

void dowork()
{
    wait((rand() % 5) * 100000); /* simulated workload */
}

void frame()
{
    dowork(); /* do your per-frame work here */

    int actual = get_time();
    int work_time = actual - ideal; /* elapsed time in dowork() */
    int idle_time = interval - work_time; /* idle delay to next frame */

#ifdef ENABLE_VARIABLE
    if (idle_time < 0) {
        /* OPTIONAL: slow frame rate 10% if falling behind */
        interval -= idle_time;
    } else if (interval > INTERVAL) {
        /* OPTIONAL: if we slowed down, but now we have idle time, increase
         * rate 10% until we get to our original target rate */
        interval -= (interval - INTERVAL)/10;
    }
#endif

    if (idle_time > 0) {
        /* sleep for the idle period */
        wait(idle_time);
    }

    printf("FRAME: time %10d (work %10d, idle %10d)\n",
        ideal, work_time, idle_time);

    ideal = ideal + interval;
}

void main()
{
    int i;
    init_time();
    /* simulate 50 frames */
    for (i=0; i<50; i++)
        frame();
}
于 2013-06-12T02:00:08.523 に答える