ぺんぎんさんのおうち

日記です。たまに日記じゃないこともあります。

15.04.2024

四半期の振り返り。とは言え特段何も無い。アルバイトをやめたくらいか。

アルバイトしていて良かったことは、まず第一に金銭面。大学RAの年間54万に加えると結構余裕ができる。ギリ扶養を外れない程度(もしかしたらもうすぐ外れるかもしれん)。ありがたいことに家賃は親が出してくれているので、食費や光熱費に充ててなんとか生活できた。
一方で良くなかったのは、研究に割ける時間が圧倒的に減ってしまったこと。実験が必須の研究にもかかわらず研究室に行けない時間を作ってしまったのは完全に悪手だった。これに加えて(アルバイトとは無関係だが)、実験機材が不安定なせいで数日分のデータが飛ぶのも精神的に良くなかった。「明日業務あるし眺めに回しとくか!」で帰宅したらその数時間後に実験が止まってたなんてことも多々。あとWindows Updateはゴミ。もうWindows使うのやめたほうがいいんだろうな。
閑話休題。実験系の研究で平日の朝から夕方までのアルバイトをすると大変。ぼくがDC1,2取れなかったのが悪いんですけどねガハハ。

 

Apple watchを買った。iPad新調はまた今度。

どうやらこういう通知よく来るらしい。無視でいいかと思ったけど、シューズとウェアは揃ってたし、これを機にランニングすることにした。いつもの散歩コースを朝か夜に走る。

午前に論文reading/writing,
午後に研究打ち合わせ/勉強会,
朝か夜にランニング

残りの3四半期はこれをコンスタントにやっていきたい。

23.03.2024

スパムアカウントに絡まれるのが嫌で鍵垢にしてたけど、自分の過去のツイートが検索できなくて不便だったので鍵を外してみた。

そしたらこれが見つかった。この動画を取ったiPhone 6Sは手元にあるのにファイルは消えてたのでツイートが発掘できて嬉しい。ドイツ行ってからもう6年になるらしい。

 

DC2なぁ。

23.02.2024

牡蠣食って帰ってきて、元気だったら修正する。

C++ならスマートポインタ使えよ←やめてね📮👍
double型よりも精度が良くて、配列の要素ごとに計算できるモジュールが欲しい。要するにnumpy.array(dtype=float)に勝ちたい。 自力で実装しても絶対勝てないのでGMPに頼ることにする。GMPに頼っても勝てる気がしねえぜ。

bench1: mpf_tとmpf_t->_mp_d用にmallocでメモリを動的に確保する。定数を記述して、コンパイル時にメモリサイズがわかるように。
bench2: mpf_t用にmallocでメモリ確保を行う。_mp_dのメモリ確保はmpf_initを通してGMPにやってもらう。
bench3: 1と似ている。mpf_tとmpf_t->_mp_d用にメモリを動的に確保する。確保するメモリサイズは変数nで指定。ただしnはコンパイル時にわかっている(nは関数の呼び出し元で定義)。
bench4: std::vector<mpf_class>を使う。sample分のstd::vectorのメモリ確保に加えて、mpf_classのコンストラクタ(や値のセット)でもメモリ確保が走る。

速度は、3 > 1 > 4 > 2の順で速い。malloc(定数をベタ書き)よりもmalloc(変数を渡す)のほうが速いのが驚き。数値が小さいと変わるかな。ほんとはmalloc使いたくないけど、サイズが大きいとスタックに乗り切らずにSegmentation Faultするので諦めた。

numpy.array(dtype=float)は64ビット精度だけど、計算時には内部で128ビットのレジスタと使っているという話を聞いたことがある。実装コードが見つからないので知ってる人がいたら教えて欲しい。今回はPRECを64*4にしているのでnumpyよりも精度は倍。PRECを大きくすると当然速度は落ちる。

mpf_t init benchmark

15.02.2024

mpf_class a("0.1")とmpf_class b = 0.1は違うんだなぁ。コンストラクタで呼び出す関数がそれぞれmpf_init_set_strとmpf_set_dなので違うのは当たり前なんだが、そうじゃない。

#include <iostream>
#include <gmpxx.h>

void dump_mpf_member(mpf_t t) {
    printf("Prec : %lu\n", t->_mp_prec);
    printf("Size : %lu\n", t->_mp_size);
    for (size_t i = 0; i < t->_mp_prec; i++) {
        printf("_mp_d_[%lu]: %lu\n", i, t->_mp_d[i]);
    }
}

int main() {
    mpf_class a("0.1");
    mpf_class b = 0.1;

    puts("mpf_t a");
    dump_mpf_member(a.get_mpf_t());
    gmp_printf("%.64Ff\n", a.get_mpf_t());
    puts("\nmpf_t b");
    dump_mpf_member(b.get_mpf_t());
    gmp_printf("%.64Ff\n", b.get_mpf_t());}
/*
$ ./a.out
mpf_t a
Prec : 2
Size : 3
_mp_d_[0]: 11068046444225730969
_mp_d_[1]: 11068046444225730969
0.1000000000000000000000000000000000000000000000000000000000000000

mpf_t b
Prec : 2
Size : 2
_mp_d_[0]: 0
_mp_d_[1]: 1844674407370955264
0.1000000000000000055510000000000000000000000000000000000000000000
*/

b = 0.1で0.1を評価するときに既に丸め誤差が発生している。0.1+0.1+0.1と0.3が等しくないやつ。一方でmpf_class a("0.1")では忠実に0.1を表現してくれる(限度はありそうやけど、正直わからん)。

ちなみにdouble型をmpf_t->_mp_dにセットする実装はextract-dbl.cにある。見ないほうがいい。 double型をmpf_tにセットしたいとき、単純に_mp_d[0] = double value;と書いてもダメそうだ。

14.02.2024

はてなブログのUI変わったね。エントリを見る側からすると今のUIがカッコよくていいけど、書く側的には前のほうが好き(In my opnion)。

mpf_関数はmpf_t同士かmpf_tと整数の加減乗除しかない(mpq_t, mpz_tは考慮しない)。double型の場合はどうするか。mpf_tをinitし、mpf_set_dで値をセットしてからmpf_mulを呼び出す。
これの何が嫌かというと、mpf_classインスタンスとdoubleの乗算があるときに(たとえばこんなのmpf_class a(11.4514), b; b = a * 8.10;)、mpf_init2による動的メモリ確保が発生する。
std::vector<mpf_class>インスタンスの各要素にdouble型を足したり掛けたりするときに困ることになるので、この部分は自分で頑張って実装する。

// 一番良くない例
std::vector<mpf_class> mpf_vec(N); // define then initilize values.
double ct = 0.81019;
for (size_t i = 0; i < N; i++) {
    mpf_vec[i] = mpf_classインスタンス * ct // ctのためのメモリ確保がN回発生する。なんならfreeも走る。
}
// 改良版(まだ改善できるよ)
std::vector<mpf_class> mpf_vec(N); // define then initilize values.
mpf_class ct(0.81019); // mpf_tでもいい。ちゃんとmpf_initしてからmpf_set_dする。メモリ確保はここの一回だけ。
for (size_t i = 0; i < N; i++) {
    mpf_mul(mpf_vec[i].get_mpf_t(), mpf_classインスタンス.get_mpf_t(), ct.get_mpf_t());
}

しかしやることは単純で、mpf_t->_mp_dに動的メモリ確保した領域のアドレスではなくスタック領域のアドレスを渡すだけ。だいだい0.1 nsくらい速くなる。動的メモリ確保といってもサイズは小さいからmallocではなくbrkが呼ばれるので、元々そこまで遅くないんだと思う。

そのうちちゃんと書く。std::vectorのことはよくわかってないので、もしかしたらもっと高速化できるかも。