カレンダ処理もFortranで
この記事は、計算時間の測定方法ではなく、カレンダ処理をFortranで簡単に実行するためのものです。カレンダ処理関数を自作する場合、うるう年や2038年問題などを気にする必要があるので割と頭が痛くなります。そこでライブラリを使うわけですが、C/C++/Pythonと同じように、Fortranにもカレンダ処理を扱うことのできるライブラリがあります。datetime-fortranです。
言語 | ライブラリ |
---|---|
C | time.h |
C++ | chrono |
python | datetime |
fortran | datetime-fortran |
datetime-fortranの利用例
このライブラリはpythonのdatetime
ライブラリによく似た利用方法となっています。(よく似たというより、ほぼそのまま。)
まず、サンプルのメインプログラムと実行結果を示します。
program main
use mydatetime
implicit none
call separator(say_hello)
call separator(use_epoch)
call separator(use_timedelta)
end program main
- say_hello: 実行時の日時を取得しコンソールに表示。その後文字列から日時データを読み取る事例。
- use_epoch: エポック秒、タイムゾーン、ローカルタイム、世界標準時を扱う事例。C言語の
tiem.h
ライブラリに寄せた書き方をしてみた。 - use_timedelta: 日時の差分計算の事例。
!-----
now :2024/11/29 23:32:37 (Friday)... == 1732890757
read from text :2024/12/25 20:00:00 (Wednesday)
!-----
timezone@mymachine: 9.0000000000000000
localtime :1970/01/01 09:00:00 (Thursday)
gmtime :1970/01/01 00:00:00 (Thursday)
!-----
2024/12/25 00:00:00 (Wednesday)
2024/12/25 00:00:00 (Wednesday)
one_day < delta : T
メインプログラムから呼び出されるサブルーチンの内部は次のコードです。
module mydatetime
use iso_fortran_env
use datetime_module !datetime-fortraライブラリ
implicit none
character(22),parameter :: strfmt = "%Y/%m/%d %H:%M:%S (%A)" ! 表示形式を指定する文字列
private
public :: separator, say_hello, use_epoch, use_timedelta
contains
subroutine say_hello
use stdlib_string_type ! stdlibライブラリのstring_type()を使う
type(datetime) :: day ! 日時情報を扱う型
integer(int64) :: epoch ! エポック秒
day = day%now() !現在日時を取得
epoch = day%secondsSinceEpoch() !取得した現在日時をエポック秒に変換
print *, "now :",day%strftime(strfmt), "... == ",string_type(epoch)
day = strptime("2024/12/25 20:00:00",strfmt) ! 引数の文字列からdatetime型を生成
print *, "read from text :",day%strftime(strfmt)
end subroutine say_hello
subroutine use_epoch
type(datetime) :: day
real(real64) :: tz !タイムゾーン(東京は+9hour)
integer(int64) :: epoch
tz = machinetimezone()
epoch = 0
print*, "timezone@mymachine:",tz
day = localtime(epoch, tz) !実行マシンの日時(標準時+9h)
print *, "localtime :",day%strftime(strfmt)
day = gmtime(epoch) !世界標準時
print *, "gmtime :",day%strftime(strfmt)
end subroutine
subroutine use_timedelta
type(datetime) :: day_a, day_b ,day_ans
type(timedelta) :: one_day, delta
integer :: i
one_day = timedelta(days=1) ! "1日分"の時間
day_a = datetime(2024,12,1)
day_b = datetime(2024,12,25)
delta = day_b - day_a !2024/12/1 ~ 2024/12/25の時間差
day_ans = day_a
do i = 1,24 !1日ずつ足し合わせ(カレンダを進めながら、勤務時間をチェックしたり。。とか)
day_ans = day_ans + one_day
end do
print*, day_ans%strftime(strfmt)
day_ans = day_a + delta !一気にdelta分の時間差を足すと..
print*, day_ans%strftime(strfmt)
print*, "one_day < delta :", one_day < delta !時間間隔の大小も比較できる
end subroutine
subroutine separator(fun)
! コンソール表示用ユーティリティ
external fun
print *, "!-----"
call fun()
print *, ""
end subroutine
end module mydatetime
力尽きたので詳しくは解説しませんが、上記のコードを見てください。ほぼpythonのdatetimeライブラリの使用感と同じです。
計算速度について
pythonのdatetimeライブラリと比較した結果、断然datetime-fortran
が早いです。
また、datetime-fortranを使うにしても、より速度を上げたいのであれば、ボトルネック部分はなるべくtype(timedelta)
を使わずにエポック秒の演算をする方が高速になります。エポック秒は整数演算なので高速になるのだと勝手に思っています。
このへんの事情はC言語も同様で、tm構造体
をループ内で多用するとあまり速度が出ませんでした。
並列計算について
Cのtime.h
は、スレッドセーフでない関数があったりするので要注意です。
time.h
にはスレッドセーフ版の関数も見つかりますが、OSによって関数名が違ったりしてちょっと使い辛く感じました。(スレッド並列版はコンパイラ拡張的な扱い?)
datetime-fortranは基本的に並列計算で利用できます。
Cへのインターフェイスとして書かれている関数は、将来的にCの影響を受ける可能性はありますが、Fortranコードを眺めると、基本的に並列計算可能なことを意図していることが明白です。
fpmを使うとビルドが簡単
pip install fpm
としたあと、
fpm new mydatetime --lib --app
などとすれば、mydatetimeというフォルダにテンプレが出来上がります。fpm.toml
の下の方に、
[dependencies]
stdlib = "*"
datetime.git ="https://github.com/wavebitscientific/datetime-fortran"
と書けば、
fpm run
でビルド&実行してくれます。(色々端折ってますので、フォルダ名やファイル名など、各自の環境に合わせてください。)
おわり
pythonでカレンダ処理を高速化する必要に迫られて、色々と調べた結果のご紹介でした。