kclib1のページ#7
(11) 開発日記#1
- 2019.04.16(火)
- [1] 今日は今までに作っていたやつを一気に書いた(KAutoreleasePoolの途中まで)。まあまだ書き切れてはいないけど。
- ここまでで一番気に入っているのは、やっぱりKMallocかなあ。速くなったのはすごくうれしい。しかも難しいことをやって速くなったのではなくて、シンプルなことをして速くなったっていうのが自分としてはかなりうれしい。これから毎日使いたいくらいに気にっている。それにメモリ使用状況を確認する関数があるのも便利(reportのこと)。
- その次のお気に入りはKAutoreleasePoolかな。だってfreeをライブラリ任せにできるって結構快適だもん。まあ普通の人はいまどきC言語なんか使わないのかもしれないけど・・・。
- でも私は使う。やめる気はない。私にはC言語くらいがちょうどいい。
- [2] 昨年まではひたすら言語を作っていたのだけど、自作言語がC言語レベルになるまでには結構時間がかかって、結局それが待てなくて私はC言語に戻ってきてしまう。これじゃあ言語を作っても私のプログラミング環境は改善しない(プログラミングスキルは上がるし、経験も増えるけれど)。それで今年は確実に自分のプログラミング環境が改善することをやりたいと思った。だからライブラリ開発になった。・・・これで私の開発速度が上がって、来年くらいには短期間で言語が作れるようになりたい!そして作った言語の上で暮らしたい!!
- [3] そういえば先日、qsortよりも速いソート関数って簡単に作れないのかなって思って挑戦したけど、全然だめだった。結構ちゃんと作ってあるみたい。だからkqsortでも結局はqsortを呼び出すことにしている。
- 2019.04.17(水)
- [1] 自分でライブラリを作って、それをガンガン使ってプログラムを書いていくって、私からすれば自分で言語を作ってそれを使ってプログラムを書いていくのと同じくらいにかっこいい気がするのだけど、普通の人はそうは思わないのかなあ・・・。
- [2] 今回KMallocでは、freeの際にサイズとポインタの両方を引数指定しなければいけないことになったわけですが、その際に、「サイズとポインタ」なのか「ポインタとサイズ」なのか、そんなどうでもいいことをかなり悩みました(引数の順番のことです)。・・・結局、内部的にはまずサイズでKPtrPoolが決まって、次にそこへポインタを返すのだから、「サイズとポインタ」の順序にしました。そしてそれが決まると、KAutoreleasePoolでも「サイズとポインタ」の順番で引数を取ることになって、以後、この順番がkclib1では定着することになりそうです。
- 2019.04.18(木)
- [1] 普通に開発して、テスト実行もして、「よしよし」と確認している私ですが、それでも、ここに[内部実装]として掲載しているときに、「あれ?これってこう書いたらもっと短く書けるし速くなるんじゃないかな?」みたいなところに気づいて、直したことが何度かありました。・・・だから自分の書いたプログラムを何度も見直す機会があるるのは、手間はあるけど、いいことだなーと思います。
- [2] 今日はkclib1_0008とkclib1_0009を書きました。今まで私はいつも適当に大きめの固定長のバッファを確保してそれをはみ出さないように使うようなプログラムばかり書いてきたのだけど、今後はそんなこと気にしないで、たくさん使うときは相応のメモリを使うし、少ししか使わないときは少ししかメモリを使わないような、そんなプログラムが簡単に書けることになります。しかも簡単に書けます。とてもうれしいです。・・・詳しくはkreadFileAやksgetsAのサンプルを見てください。
- [3] ふむふむ。どうやらC++だって、バッファのサイズとかを気にしないでも結構便利に書けるらしいです。さすがですね。じゃあksgetsAのサンプルで行数比較をしてみようと思います。まずkclib1版のほうを少し可読性を下げて短くします。
#include "kclib1.h"
#include <stdio.h>
int main(int argc, const char **argv)
{
KAutoreleasePool *ap = KAutoreleasePool_open();
const char *s = kreadFileA(argv[1], "rb", NULL, 1 + 2);
for (int i = 1; *s != '\0'; i++)
printf("%08d: %s", i, ksgetsA(&s, 0));
KAutoreleasePool_close(ap);
return 0;
}
- これで12行。・・・ではC++で書いてみます(註:もっといい書き方がありそうなので、後日探求します)。
#include <fstream>
#include <iostream>
#include <string>
#include <boost/format.hpp>
int main(int argc, const char **argv)
{
std::ifstream ifs(argv[1]);
std::string str;
if (ifs.fail()) {
std::cerr << "file open error." << std::endl;
return 1;
}
for (int i = 1; getline(ifs, str); i++)
std::cout << boost::format("%08d: %s") % i % str << std::endl;
return 0;
}
- うーん、17行ですね。エラー処理で4行も書かされてそこで損している感じです。
- まあメモリの使い方的にはC++版の方がかなりいいのですが、今はそこにはこだわっていないのです。メモリの使い方が悪くてもいいからとにかくリークしないってだけの条件下で書いたらどこまで短くできるかという比較なのです。
- 本質的な比較ということになれば、
const char *s = kreadFileA(argv[1], "rb", NULL, 1 + 2);
for (int i = 1; *s != '\0'; i++)
printf("%08d: %s", i, ksgetsA(&s, 0));
- vs
std::ifstream ifs(argv[1]);
for (int i = 1; getline(ifs, str); i++)
std::cout << boost::format("%08d: %s") % i % str << std::endl;
- ということになって、まあどっちも3行程度だっていう感じですね。これ以上短くするのは現実的じゃない気がするので、あとはやっぱりエラー処理みたいな本質的じゃないところをライブラリで隠していくしかないです。だからkclib1の方向性は正しいと私は確信します。
- 2019.04.19(金)
- [1] このkclib1は基本的には「これを使うことでプログラムの行数が少なくなる」というのを目指しています。そのためにはどうするのかというと、この処理をするときはこの処理も一緒にやることが非常に多いのではないか、という組み合わせを探すのです。mallocしたら(いつかは)freeする、fopenしたらエラー判定する、などなど。そういう組み合わせが見つかったら、一つの関数でそれを全部やってあげるようなライブラリ関数を作るのです。
- 実はこれとよく似たことを、OSECPU-VMのときもやっていて、その時は「この命令の直後にはこの命令が来ることが多い」みたいなのを探して、それを一つの命令で書けるように工夫することで、劇的にコンパクトな命令体系を構築していました。
- ということで、ああ、私はまた似たようなことをやっているんだなーと思いました。
- [2] 今日はそもそもどうして私がC言語ばかり使うのかの話を書きたいと思います。
- 私はずっと前から、「自分でOSを作って、そのOSの中で超快適に暮らす」のが夢です。既存のソフトウェアにいろいろと不満はありますがそういうのは全部「OSから作り直せばもっとずっと良くなる」と思っています。・・・まあ思うだけなら個人の自由ですよね(笑)。
- さてそう思ってしまうと、WindowsやLinuxのような既存OS向けに何かソフトウェアを書くときには必ず、「今一生懸命に書いているこのプログラムも、自作OSに移行したら使わなくなってゴミになっちゃうんだろうなあ」という想いが脳裏を横切ります。「だったらこんなことしている場合じゃない、さっさとOSを作ってそのOS向けにプログラムを書くべきなんだ」とも思うわけです。
- でもそう思ってOSを作り始めても、そのOSの中で暮らせるようになるくらいまでに完成させるには相当に時間がかかるわけで、しかも自分でOSを作れればそれでいいわけではなく、既存の不満を解消できるような夢のようなOSを作らないと意味はないわけですから、だからそんなの全然完成しないわけです(我ながら情けない)。でもそうこうしているうちにも書かなければいけないプログラムはいっぱいあって、だから結局、既存のOS向けに不満をブツブツと言いながらプログラムを書くわけです。・・・私の日常を理解してもらえたでしょうか(笑)。
- そういう考えになってしまうと、言語はせいぜいC言語までにしておこうと思ってしまうのです。なぜなら、まあC言語相当のものは将来頑張ればそれなりの物が作れそうな気はしますが、C++相当の物を作る自信がないからです。それなのにC++を私のメイン開発環境にしてしまったら、私は理想のOSを完成させた後、自分の過去の遺産をほとんど再利用できなくなってしまいます。C++を使うたびに別のOSを起動しなければいけない、と。そんなのは嫌なわけです。だから私は自分が作れそうな言語以上のものは使いたくないのです。
- この悪循環から私はどうしても抜け出したいのです。それで私はC言語をメインにしつつも、自作ライブラリでパワーアップして、少ない行数でもいろいろ作れるようになって、それでOSや言語をぱぱっと作れるようになりたいのです。C言語でC++や他の言語以上の生産性がほしいのです。・・・このプランももしかしたらうまくいかないかもしれないけど、とにかくやれるところまでやってみたいのです。それでもだめだったら、また別の方法を考えます!とにかく今は行けるところまで行きます!!
- [3] 私は今までも自作ライブラリはそれなりには書いてきました。でも今回はそれまでのライブラリ開発とは違います。今までは私は結構大きなライブラリを作るのが好きでした。500行とか1000行とかあるようなやつです。でもこのkclib1は、とにかく小さな関数を作ることを心がけていて、作った関数を使ってまた別の小さな関数を作り、その小さな関数をさらに使ってまた別の小さな関数を作るという、「小さなものの積み重ね」をすごく意識しています。・・・凝ったことやかっこいいことなんかべつにやらなくていいんです。それよりもバグが混入しないくらいにシンプルに小さく書いて、そしてそれを少しずつ積み上げるのです。このスタイルはすごく「着々と前進している」感じがします。昨日より今日、今日より明日、明日より明後日、そうやってどんどん良くなっているわけです。ちょっとずつではあるけど、このペースなら一年後とかにはとんでもなく遠くにまで行けそうです。ワクワクが止まりません!
- 今までの私は、かっこいいライブラリを作ることにこだわりすぎて、すごく作り込んで、自分のスキルのギリギリを攻めて、まあ確かに最終的にいいものはできるのですが、完成するまでには時間がかかるし、使っていてなんか動作が微妙になった時に、自分のライブラリが疑わしくなるしで、どうもいまいちなのです。それで思ったのは、力を入れなくても書ける程度のものを確実に積み上げていく方がいいんじゃないか、とりあえずそういう書き方も試してみるべきなんじゃないか、とまあそういうことだったのです。性能は本気版と比較して10%くらいは違うかもしれませんが、でも(自分の中での)信頼性は何倍にも上がった気がします。今回それを試してみて、ああ今はこのやり方が合っているんだなーとしみじみ感じています。
- [4] 主な依存関係
kerrorExit | | | kqsort | | | kpopCount32, kpopCount64, kgetMsb32 | | | KPtrPool | kerrorExitを利用 | | KMalloc | kerrorExit, kgetMsb32, kpopCount32, KPtrPoolを利用 | 特に高速かつ効率よいKPtrPoolの寄与は大きい | KAutoreleasePool | KMalloc | KMallocが小さなメモリ片を効率よく管理できることとalloc/freeが高速なことを生かして、シンプルに設計されている | KSizPtr | KMallocを利用 | | KSizPtr_addFile, kreadFileA, ksgetsA | kerrorExit, KAutoreleasePool, KSizPtrを利用 | | KIndexS | KSizPtr, kqsortを利用 | |
- 2019.04.22(月)
- [1] 今日はKIndexSを作った。これで適当にデータを簡単に登録して簡単に検索できるようになったと思う。よしよし。
- [2] 急にふと思ったのだけど、kclib1みたいな「しょぼい」ライブラリって実はすごくいいのかもしれない。だってあまりにしょぼくて、ライブラリを使った時に何が起きるのか容易に想像できるから。複雑なことをやるとこの関数を使った時に何が起こるのか容易には想像できなくなる。そうすると予期しない副作用に悩まされるかもしれない。
- 結局、人間は自分の能力を大きく上回るようなライブラリ関数を乱用すべきじゃないんだ、きっと。そういうことをするからバグが出たりセキュリティホールになったりする。
- 2019.04.24(水)
- [1] 昨日はKIndexHC, KIndexHO, KIndexHSを作った。どれもハッシュテーブルでそれぞれ連鎖法、開番地法、二分探索融合型になっている。それでその開発というかデバッグの際に、KSizPtrの初期化を忘れて10分くらい時間を無駄にしてしまった。これはよくない。だからセーフモードを作りたい。速度は落ちるけど、安全になるやつ。
- [2] 結局セーフモードではなくリリースモードを作った。つまりデフォルトがセーフモード。切り替えが簡単なので性能比較がしやすい。数パーセントは遅くなるけど、10%にはならない感じ。とにかくエラーを探してくれるのはとても快適。・・・そして正確に初期化忘れなどを検出できるようになってみると、「だったらエラーなんか出していないで自動で初期化をして続行してくれればいいじゃないか」という気分になってきた。要するにイージーモード。でもなあこれに頼るようになったらすごく危ない気はする。でも一方で、プログラムの記述を減らす余地がさらに増えるのはいい気もする。・・・迷うなあ。・・・じゃあやってみるか。やってみないとわからないこともあるだろうし、やってみて「こりゃダメだ」になったら、イージーモードを使わなければいいだけだから。
- [3] イージーモードに頼り切った、テキストを行単位で逆順表示するプログラム。とても短い。でも確保したメモリの開放はやっていない。・・・正直ちょっといまいちかなと思っている。
#include "kclib1.h"
#include <stdio.h>
int main()
{
keasymode = 2;
KSizPtr sp;
for (char *s = kreadFileA("t0011d.c", "rb", 0, 1 + 2); *s != '\0'; )
KSizPtr_addPtr(&sp, kcutCrLfM(-1, ksgetsA(&s, 0)));
for (int i = sp.s / sizeof (char *) - 1; i >= 0; i--)
puts(((char **) sp.p)[i]);
return 0;
}
- 2019.04.25(木)
- [1] C++のコンストラクタ・デストラクタの仕組みがうらやましいけどどうしてもマネできなくて、メインの開発環境をC++に移行しようかどうかを検討中・・・。C++のフル機能を使う気は全然なくて一部の機能が欲しいだけなので、その範囲で使うにとどめておけば、将来自分の資産を引き継ぐために言語を作ることになっても大変さはあまり増えないかもしれない。・・・コンストラクタ・デストラクタがあれば、初期化忘れとか解放忘れがなくなるんだよな・・・。
- [2] まず多重継承はいらない。テンプレートもいらない(便利なのはわかるけど、まあなくても何とかなる)。C言語を超える範囲の暗黙の型変換もいらない。C++の標準ライブラリもいらない。引数の数や型で関数を区別してくれるのは欲しい。演算子のオーバーロードもそんなには欲しくないからなくてもいいかな・・・。
こめんと欄
|