Boost.Asio の Timer精度調査 (Windows環境)

要旨

  • 目的: Boost.Asio には Timerクラスである deadline_timerがある.この時間精度について調べてみる.
  • 調査環境: Boost 1.44.0, Windows XP (x64)
  • 結論: date_timeライブラリに依存している.調査環境におけるdate_timeライブラリの時間取得関数はGetSystemTime/GetSystemTimeAsFileTimeである.これらの精度は10ms程度.

boost::asio::deadline_timerクラス

主に関係するヘッダファイルは次の通り:

  • boost/asio/deadline_timer.hpp
    • basic_deadline_timer に boost::posix_time::ptime を与えてtypedef
  • boost/asio/basic_deadline_timer.hpp
    • クラス定義.動作はdeadline_timer_serviceに投げる.
  • boost/asio/deadline_timer_service.hpp
    • 動作の記述.詳細はdetail::deadline_timer_serviceに投げる
  • boost/asio/detail/deadline_timer_service.hpp
    • 動作の詳細定義.

これより,wait()やasync_***()系の定義はを参照すればよい.

これらのファイルの中では,時間に関係する部分はtemplateによって記述されている.最終的に,(現在の実装では)で与えられた boost::posix_time::ptime に依存するようになっている.

読み進めると,タイマーなど分解能を要する時間に関係する部分は,boost::posix_time::microsec_clock に依存するように書かれている.

date_time の時間精度

主に関係するヘッダファイルは次の通り:

  • boost/date_time/microsec_time_clock.hpp
  • boost/date_time/filetime_functions.hpp

micro秒の扱いについて定義しているmicrosec_time_clock.hppを見てみると,82行目あたりに次のような定義を見る.

#ifdef BOOST_HAS_GETTIMEOFDAY
      timeval tv;
      gettimeofday(&tv, 0); //gettimeofday does not support TZ adjust on Linux.
      std::time_t t = tv.tv_sec;
      boost::uint32_t sub_sec = tv.tv_usec;
#elif defined(BOOST_HAS_FTIME)
      winapi::file_time ft;
      winapi::get_system_time_as_file_time(ft);
      uint64_t micros = winapi::file_time_to_microseconds(ft); // it will not wrap, since ft is the current time
                                                               // and cannot be before 1970-Jan-01
      std::time_t t = static_cast<std::time_t>(micros / 1000000UL); // seconds since epoch
      // microseconds -- static casts supress warnings
      boost::uint32_t sub_sec = static_cast<boost::uint32_t>(micros % 1000000UL);
#else
#error Internal Boost.DateTime error: BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK is defined, however neither gettimeofday nor FILETIME support is detected.
#endif

winapi::get_system_time_as_file_time関数 などは filetime_functions.hpp 内で定義されており,たとえば66行目あたりを見ると次のように定義されている.

    typedef FILETIME file_time;
    typedef SYSTEMTIME system_time;

    inline void get_system_time_as_file_time(file_time& ft)
    {
#if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3205))
        // Some runtime library implementations expect local times as the norm for ctime.
        file_time ft_utc;
        GetSystemTimeAsFileTime(&ft_utc);
        FileTimeToLocalFileTime(&ft_utc, &ft);
#elif defined(BOOST_NO_GETSYSTEMTIMEASFILETIME)
        system_time st;
        GetSystemTime(&st);
        SystemTimeToFileTime(&st, &ft);
#else
        GetSystemTimeAsFileTime(&ft);
#endif
    }

すなわち,調査環境においては,時間計測はGetSystemTime / GetSystemTimeAsFileTime 関数に依存していることがわかる.これらの関数は精度10ms程度程度である.

結論

調査環境では,Boost.Asioのタイマー系の精度は10ms程度のようであるから,これにあった運用をすべきである.

備考1

Windows環境では,clock (GetSystemTimeAsFileTime依存), GetTickCount, timeGetTime, QueryPerformanceCounter などが利用できる.(TODO: 暇ができたらまとめる・・・か?)

シビアな時間制御をする際は,timeGetTimeや QueryPerformanceCounter を用いて自前で用意をしたほうがよいだろう.

しかし,そもそもAsioの想定しているSocket通信や,非RTOSであるWindowsでそれほどシビアな運用を要求するアプリケーションを作成するのは無茶というもの・・・かもしれない.

備考2

boostのバージョンによって実装は日々変化しているようだ.手元にあった1.44と1.36のソースをざっと見比べてみたが,ファイル構成や関数定義について,さまざまな部分で違いがみられた.