ぺんぎんさんのおうち

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

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のことはよくわかってないので、もしかしたらもっと高速化できるかも。

23.01.2024

韓国寒い。最高気温が-2℃とか。基本的には厚着で耐えられるけど顔が厳しいね。
日本語も英語も通じないことが多く、日本に観光に来る韓国人の気持ちがわかったかもしれない。
韓国の人はみんなキムチが大好きなのか、頼んでなくてもお通し感覚(料金はかからない)で数種類のキムチが出てくる。食べきれないのでやめてほしい。今のところサンプル数2+1 (キムチバーがあった)。

木曜日の夜に帰宅予定。

29.12.2023

搭乗開始を待ちながら更新。

今年は珍しく年末年始を実家で過ごす。調布に来てから初めてかもしれない。

 

NISTによって選定された軽量暗号アルゴリズムAsconの仕様の話を誰もしていないのは何故なのか。AESやChacha20の暗号化/復号プロセスの記事は大量に出てくるのにね。