Not only is the Internet dead, it's starting to smell really bad.:2021年06月分

2021/06/02(Wed)

[オレオレN6] bm(3)ネタは一時中断です

前回の続きで、BM法の一致サフィックス規則(Good suffix rule)の解説を書こうとしたのだが

T かちんか|ちん|のおちんちん
P  おちん|ちん|

とか

T 志布志市|志布志|町志布志志布志市役所志布志支所
P 志布志町|志布志|

みたいな 畳語での例題作りを試行錯誤してるうちに *1、ゲシュタルト崩壊し脳神経焼き切れそうだったのでいったん中断、また気が向いたらね。

「記憶屋ジョニィ」の軍用イルカが光電管ディスプレイに表示した電子パターンっぽいよね…

志     志布志町志布志 おちんちんおちんちんおちん       老
布     布       ちんちんおちんちんおちんち       眼
志     志       んちんおちんちんおちんちん       老
町     町       ちんおちんちんおちんちんお 眼老眼老眼老眼老眼老眼老
志     志       んおちんちんおちんちんおち       老
布     布       おちんちんおちんちんおちん       眼
志布志町志布志布志町志布志 ちんちんおちんちんおちんち       老
      布     布 んちんおちんちんおちんちん       眼
      志     志 ちんおちんちんおちんちんお       老
      町     町 んおちんちんおちんちんおち       眼
      志     志 おちんちんおちんちんおちん       老
      布     布 ちんちんおちんちんおちんち       眼
志布志町志布志     志 んちんおちんちんおちんちん       老

やはりワイの脳味噌じゃうまいこと一致サフィックス規則を説明できる例題考えるのって難易度高いですわ、畳語が他の言語と比べて多い日本語なら簡単に解説できるかと思ったんだけどね… そら検索文字が短いなら使わない方が高速化できるってんでBMH(Boyer-Moore-Horsepool)法みたいな改良が生まれるわけですわ。

そこらに転がってる解説ページのように無意味な文字の羅列で解説すんならそら簡単だけど、それじゃゼロインパクトで忘れちゃうからな。 理系の人みたいに元素記号の周期表を「変な(He)ねーちゃん(Ne)アレに(Ar)狂って(Kr)セックス(Xe)乱発(Rn)」の語呂合わせに頼らずとも覚えられる頭ならいいけどさ… やはりワイのようにうんこちんちんしか考えられない肛門期から卒業できない文系が情報処理の世界に迷い込んだのが間違いだったのである、いずれ役立たずの文系は最終解決とかいってチクロンB吸って吸って吐かないされるに違いない。

これが理系で遺伝工学とか専攻してた人なら遺伝子情報配列あたり例題に使ってアカデミズムの香りを醸し出せるんだろうけど、ワイはDNAといわれてもプロ(プロではない)野球チームしか思い浮かばないのだ。 こんなんだからmRNAワクチン接種で5Gとか11axとか受信してしまうのである、国内生産ワクチンの場合やっぱり注射跡に技適マークが浮かび上がるんですかね?

*1:おちんちんで遊んでるわけではないぞ。

2021/06/03(Thu)

[オレオレN6] rbtree(3)遅くない?

@ここは私の夢日記

マーク・〇ッカーバーグとイー〇ン・マスクという巨悪が「世界」を「終了」させる時限式キルスイッチを押した事に気づいてしまった俺、フェー〇ブック内にも少なからずといる抵抗勢力と内通することに成功し手引きによってデータセンターに侵入することに成功する。 素人目には東京モノレール天王洲アイル駅近くの某倉庫屋にしか見えないが、ここはサンフランシスコだ、誰が何を言おうとサンフランシスコなんだ *1

厳重な監視システムの唯一点の死角である重い扉の向こう側の薄暗い閉鎖空間で、奴らの「システム」に 没入(ジャック・イン)すべく 端末(デッキ)氷破り(アイス・ブレーカー)をロードする待ち時間を有効活用するため、従業員専用銭湯でひと風呂浴びるべく男湯の暖簾をくぐるとそこには(ここで目が覚めた)

ということでフロイト先生、いつもの夢診断をお願いいたします。

フロイト先生「世の中暗いからなろうに憧れるのはいいとして、今時ギブスンのスプロールシリーズとか加齢臭キツ過ぎない?」

@ここからは赤黒木と白黒ショーの違いが判らない阿呆のチラシの裏のメモ書き

以前の記事でnvi-1.81用のワイド文字版regex(3)で使うために同様にワイド文字版のbm(3)風のBM法実装をクッソ雑に実装したのだが、あれをベースに簡単な性能テストを実施してみた。 やっぱりbm_comp(3)にかかるコストはそれなりに重いのだが、bm_exec(3)は不一致文字規則をソート済の配列で持ちマッチにはbsearch(3)を使うような実装であってもwcswcs(3)よりはじゅうぶんに速いので真面目に実装する価値はありそうかなこれ。

ただ以前の実装だと固定長配列で用意した不一致文字規則のテーブル長さを超える文字種がパターンに含まれるとbm_comp(3)は死ぬという制限付なので(まぁセキュリティ的にはその方がいい気もするが)そこは何とかしたいもうちょっと欲張りたいという感じ。

なのでとりあえず不一致文字規則周りを順序集合(Ordered Set)実装する時の定番である赤黒木、Nだとrbtree(3)を使って再実装してみた。

/*-
 * Copyright (c) 2021 Takehiko NOZAKI,
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <sys/cdefs.h>
#include <sys/rbtree.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#include "bmw.h"

struct bmw_bcr {
	rb_node_t rbn;
	wint_t c;	/* code point */
	size_t skip;	/* skip delta */
};

static int
bmw_bcr_compare_key(void *ctx, const void *node, const void *key)
{
	wint_t ac, bc;

	ac = ((const struct bmw_bcr *)node)->c;
	bc = *((const wint_t *)key);
	if (ac == bc)
		return 0;
	else if (ac < bc)
		return -1;
	return 1;
}

static int
bmw_bcr_compare_nodes(void *ctx, const void *parent, const void *node)
{
	return bmw_bcr_compare_key(ctx, parent,
	    (const void *)&((const struct bmw_bcr *)node)->c);
}

static const rb_tree_ops_t bmw_bcr_ops = {
	&bmw_bcr_compare_nodes,
	&bmw_bcr_compare_key,
	offsetof(struct bmw_bcr, rbn),
	NULL
};

struct bmw_pat {
	const wchar_t *pat;	/* pattern */
	size_t patlen;		/* length of pattern */
	size_t lastp;		/* last index of pattern */
	rb_tree_t bcr;		/* bad character rule */
	size_t md2;		/* mini delta */
};

bmw_pat_t
bmw_comp(const wchar_t *pat, size_t patlen)
{
	bmw_pat_t b;
	struct bmw_bcr *node;
	size_t i, skip;
	wint_t c;

	b = malloc(sizeof(*b));
	if (b == NULL)
		return NULL;
	b->pat = pat;
	b->patlen = patlen;
	b->lastp = b->patlen - 1;
	rb_tree_init(&b->bcr, &bmw_bcr_ops);
	for (i = 0; i < b->patlen; ++i) {
		c = (wint_t)b->pat[i];
		skip = b->lastp - i;
		node = rb_tree_find_node(&b->bcr, (const void *)&c);
		if (node == NULL) {
			node = malloc(sizeof(*node));
			if (node == NULL) {
				bmw_free(b);
				return NULL;
			}
			node->c = c;
			rb_tree_insert_node(&b->bcr, node);
		}
		node->skip = skip;
	}
	for (i = b->lastp; --i > 0;) {
		if (b->pat[i] == b->pat[b->lastp])
			break;
	}
	b->md2 = b->lastp - i;
	return b;
}

static inline size_t
bcr(bmw_pat_t b, wint_t c)
{
	struct bmw_bcr *node;

	node = rb_tree_find_node(&b->bcr, (const void *)&c);
	return (node != NULL) ? node->skip : b->patlen;
}

const wchar_t *
bmw_exec(bmw_pat_t b, const wchar_t *s, size_t n)
{
	const wchar_t *e;
	size_t skip;

	if (n < b->patlen)
		return NULL;
	e = s + n;
	s += b->lastp;
	for (;;) {
		if ((skip = bcr(b, (wint_t)*s)) == 0) {
			if (!wmemcmp(b->pat, s - b->lastp, b->lastp))
				return s - b->lastp;
			skip = b->md2;
		}
		if (s >= e - skip)
			break;
		s += skip;
	}
	return NULL;
}

void
bmw_free(bmw_pat_t b)
{
	struct bmw_bcr *node, *next;

	for (node = RB_TREE_MIN(&b->bcr); node != NULL; node = next) {
		next = rb_tree_iterate(&b->bcr, node, RB_DIR_RIGHT);
		rb_tree_remove_node(&b->bcr, node);
		free(node);
	}
	free(b);
}

だたまぁ予想はしてたのだが

  1. ツリーのノードをいちいちmalloc(3)するコストがかなり重い
  2. ノード追加にかかるコストがけっこう重い

というボトルネックでbm_comp(3)にかかる時間が3倍遅くなる逆シャア状態なのでもうちょっと工夫せんとアカンなこれ、1.はまぁなんかアロケーター書きゃいいか…

つーか2.についてはそもそもrbtree(3)って他の赤黒木実装と比較しても性能悪いのだよな、Oの赤黒木実装にはtree(3)ってのがあるんだけどこいつと比較すると明かに遅い。 これもうlibcの実装を置き換えちまおうかなぁという気分、ABIの後方互換に関してはshim書くのそんなに手間ではないし。ちなみにtree(3)には他にもスプレー木が実装されてたりしてより高機能なのである。

上のコードをrbtree(3)でなくtree(3)使って書き直すとこんなん。

--- bmw.c.orig	2021-06-03 18:05:36.000000000 +0900
+++ bmw.c	2021-06-03 17:54:18.000000000 +0900
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  */
 #include <sys/cdefs.h>
-#include <sys/rbtree.h>
+#include <sys/tree.h>
 #include <limits.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -36,44 +36,29 @@
 #include "bmw.h"
 
 struct bmw_bcr {
-	rb_node_t rbn;
+	RB_ENTRY(bmw_bcr) rbn;
 	wint_t c;	/* code point */
 	size_t skip;	/* skip delta */
 };
+RB_HEAD(bmw_bcr_tree, bmw_bcr); 
 
 static int
-bmw_bcr_compare_key(void *ctx, const void *node, const void *key)
+bmw_bcr_compare_key(const struct bmw_bcr *a, const struct bmw_bcr *b)
 {
-	wint_t ac, bc;
-
-	ac = ((const struct bmw_bcr *)node)->c;
-	bc = *((const wint_t *)key);
-	if (ac == bc)
+	if (a->c == b->c)
 		return 0;
-	else if (ac < bc)
+	else if (a->c < b->c)
 		return -1;
 	return 1;
 }
-
-static int
-bmw_bcr_compare_nodes(void *ctx, const void *parent, const void *node)
-{
-	return bmw_bcr_compare_key(ctx, parent,
-	    (const void *)&((const struct bmw_bcr *)node)->c);
-}
-
-static const rb_tree_ops_t bmw_bcr_ops = {
-	&bmw_bcr_compare_nodes,
-	&bmw_bcr_compare_key,
-	offsetof(struct bmw_bcr, rbn),
-	NULL
-};
+RB_PROTOTYPE_STATIC(bmw_bcr_tree, bmw_bcr, rbn, bmw_bcr_compare_key)
+RB_GENERATE_STATIC(bmw_bcr_tree, bmw_bcr, rbn, bmw_bcr_compare_key)
 
 struct bmw_pat {
 	const wchar_t *pat;	/* pattern */
 	size_t patlen;		/* length of pattern */
 	size_t lastp;		/* last index of pattern */
-	rb_tree_t bcr;		/* bad character rule */
+	struct bmw_bcr_tree bcr;/* bad character rule */
 	size_t md2;		/* mini delta */
 };
 
@@ -81,7 +66,7 @@ bmw_pat_t
 bmw_comp(const wchar_t *pat, size_t patlen)
 {
 	bmw_pat_t b;
-	struct bmw_bcr *node;
+	struct bmw_bcr *node, tmp;
 	size_t i, skip;
 	wint_t c;
 
@@ -91,11 +76,12 @@ bmw_comp(const wchar_t *pat, size_t patl
 	b->pat = pat;
 	b->patlen = patlen;
 	b->lastp = b->patlen - 1;
-	rb_tree_init(&b->bcr, &bmw_bcr_ops);
+	RB_INIT(&b->bcr);
 	for (i = 0; i < patlen; ++i) {
 		c = (wint_t)pat[i];
 		skip = b->lastp - i;
-		node = rb_tree_find_node(&b->bcr, (const void *)&c);
+		tmp.c = c;
+		node = bmw_bcr_tree_RB_FIND(&b->bcr, &tmp);
 		if (node == NULL) {
 			node = malloc(sizeof(*node));
 			if (node == NULL) {
@@ -103,7 +89,7 @@ bmw_comp(const wchar_t *pat, size_t patl
 				return NULL;
 			}
 			node->c = c;
-			rb_tree_insert_node(&b->bcr, node);
+			bmw_bcr_tree_RB_INSERT(&b->bcr, node);
 		}
 		node->skip = skip;
 	}
@@ -118,9 +104,10 @@ bmw_comp(const wchar_t *pat, size_t patl
 static inline size_t
 bcr(bmw_pat_t b, wint_t c)
 {
-	struct bmw_bcr *node;
+	struct bmw_bcr *node, tmp;
 
-	node = rb_tree_find_node(&b->bcr, (const void *)&c);
+	tmp.c = c;
+	node = bmw_bcr_tree_RB_FIND(&b->bcr, &tmp);
 	return (node != NULL) ? node->skip : b->patlen;
 }
 
@@ -152,9 +139,8 @@ bmw_free(bmw_pat_t b)
 {
 	struct bmw_bcr *node, *next;
 
-	for (node = RB_TREE_MIN(&b->bcr); node != NULL; node = next) {
-		next = rb_tree_iterate(&b->bcr, node, RB_DIR_RIGHT);
-		rb_tree_remove_node(&b->bcr, node);
+	RB_FOREACH_SAFE(node, bmw_bcr_tree, &b->bcr, next) {
+		bmw_bcr_tree_RB_REMOVE(&b->bcr, node);
 		free(node);
 	}
 	free(b);
*1:なお〇ェースブックのデータセンターは過去現在においてサンフランシスコに存在したことは無い。

2021/06/04(Fri)

[オレオレN6] gprof(1)とdlopen(3)

ワイド文字扱うプログラムの性能テストを実施する時に困るのがCitrusがdlopen(3)を使うプラグインシステムってことなんだよな。 gprof(1)で統計情報とるのにgccに-pgオプションつけてプロファイリングを有効にするともれなくdlopen(3)が無効になるので、i18n moduleの動的ロードができずsetlocale(3)に失敗してしまうのだ。

そもそもSolarisとかだと-pgつきでコンパイルした場合、dlopen(3)したライブラリの統計情報は取れない制限はあれど動的ロードに失敗するなんてことは無かったように思うのだがもう昔のこと過ぎて覚えてない。 これってNだけの制限事項だったりしたらアレね…まぁそのへん調査しはじめると脱線が止まらずいつまでたっても本線に戻れないので考えないようにする。

そもそも同じくdlopen(3)を使うのCitrus iconvの性能ボトルネック調査にふつーにgprof(1)使って特定してた記憶があるんだがどうやってたんだっけ俺。 なんかもうこういうツールの使い方を長いブランクですっかり忘れてしまった感がある、思い出さないと…

@前回のrbtree(3)が遅いって話の続き

昨日の記事書いた後に気づいたのだけど、Nにもとっくの昔にtree(3)あるじゃねーのびっくり。今は亡きsystraceが移植された18年前から存在しとった模様。

なんでrbtree(3)とtree(3)で二重に赤黒木の実装持ってんだよという話なんだが、その当時だとrbtree.h(rb.h)はlibkernにしかなくユーザランドでは使えなかったからっぽい。 じゃあrbtree(3)をユーザーランドに移す時どっちかに統一しろやという話なのだが、rbtree(3)の作者はmatt@(立浪すら頭の上がらない桑田の息子ではない方)だったのでいつものお察し案件であった。

まぁ機能重複とかチャメシインシデントではある、例えばlibc内のconstant database実装(読込専用のKVS)もすでにcitrus_dbというものが存在するのを知りつつわざわざcdb(5)という車輪の再発明を突っ込むドイツ人とかもいたので、Linusおじさんみたいな独裁者がこーゆー連中うまく捌かんとグダグダになりますわね。

話脱線するけど昔懐かしLinusおじさんのNVIDIA🖕動画、「これはNVIDIAのGPUを利用したディープフェイク動画ですか?」といってる人を某所でみて大草原。いやー彼のひととなりを知らずにコンピューターの偉い人くらいにしか思ってないと簡単に認知が歪むんすね。

@簡単なテストでプロファイルとってみた

やる気ねえのでごく簡単に。

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <wchar.h>

#include "bmw.h"

static void *
map(const char *path, size_t *size)
{
	int fd;
	struct stat st;
	void *mapped;

	if ((fd = open(path, O_RDONLY|O_CLOEXEC)) == -1)
		abort();
	if (fstat(fd, &st)  == -1)
		abort();
	if (!S_ISREG(st.st_mode))
		abort();
        mapped = mmap(NULL, (size_t)st.st_size, PROT_READ,
	    MAP_FILE|MAP_PRIVATE, fd, (off_t)0);
        if (mapped == MAP_FAILED)
		abort();
	*size = (size_t)st.st_size;
	return mapped;
}
int
main(void)
{
	const wchar_t *txt, *pat, *cp;
	size_t txtsize, patsize, txtlen, patlen, i;
	bmw_pat_t b;

	txt = map("../test/test.txt", &txtsize);
	pat = map("../test/pat.txt", &patsize);
	txtlen = wcslen(txt);
	patlen = wcslen(pat) - 1;
	for (i = 0; i < 50000; ++i) {
		b = bmw_comp(pat, patlen);
		cp = bmw_exec(b, txt, txtlen);
		bmw_free(b);
	}
	munmap(__UNCONST(txt), txtsize);
	munmap(__UNCONST(pat), patsize);
}

テストデータとパターンをファイルから読む込む、前述の通りLC_CTYPEが使えない問題があるので、ファイルをUTF-32LE(BOMなし)で保存してwchar_tかの如く扱うことで対処する。

テストデータには青空文庫の夢野久作「ドグラマグラ」を使った、パターンは同作中の最後の方から適当に「昨十九日(火曜日)正午頃~」の一文を選んだ。 これを50000回実行した結果のプロファイルを行う、うわーほんとクッソ雑。

こっちがNのrbtree(3)を使って採取したgprof(1)の結果の抜粋。

$ time ./test
   58.05s real    58.03s user     0.00s system
index % time    self  children    called     name
-----------------------------------------------
                0.90   11.11   50000/50000       main [2]
[3]     63.9    0.90   11.11   50000         bmw_comp [3]
                3.67    1.94 70200000/118100000     rb_tree_find_node [4]
                2.10    2.58 20450000/20450000     rb_tree_insert_node [6]
                0.04    0.78 20500000/20500000     malloc [12]
                0.00    0.00   50000/50000       rb_tree_init [64]
-----------------------------------------------
                2.50    1.32 47900000/118100000     bcrskip [7]
                3.67    1.94 70200000/118100000     bmw_comp [3]
[4]     50.1    6.17    3.26 118100000         rb_tree_find_node [4]
                3.26    0.00 883500000/1037250000     bmw_bcrext_compare_key [8]
-----------------------------------------------
                0.23    3.83 47900000/47900000     bmw_exec [5]
[7]     21.6    0.23    3.83 47900000         bcrskip [7]
                2.50    1.32 47900000/118100000     rb_tree_find_node [4]
-----------------------------------------------
                0.07    1.70   50000/50000       main [2]
[10]     9.4    0.07    1.70   50000         bmw_free [10]
                0.17    0.65 20450000/20450000     rb_tree_remove_node [11]
                0.01    0.66 20500000/20500000     free [15]
                0.20    0.00 20500000/20500000     rb_tree_iterate [24]

こちらがtree(3)を使っての結果。

$ time ./test
   47.83s real    47.81s user     0.01s system
index % time    self  children    called     name
-----------------------------------------------
                0.66    6.40   50000/50000       main [2]
[3]     57.3    0.66    6.40   50000         bmw_comp [3]
                2.35    1.22 70200000/118100000     bmw_bcrext_tree_RB_FIND [4]
                0.76    1.31 20450000/20450000     bmw_bcrext_tree_RB_INSERT [8]
                0.05    0.72 20500000/20500000     malloc [11]
-----------------------------------------------
                1.61    0.83 47900000/118100000     bcrskip [6]
                2.35    1.22 70200000/118100000     bmw_comp [3]
[4]     48.7    3.96    2.04 118100000         bmw_bcrext_tree_RB_FIND [4]
                2.04    0.00 883500000/1037250000     bmw_bcrext_compare_key [7]
-----------------------------------------------
                0.29    4.54 47900000/47900000     bmw_exec [6]
[8]     22.8    0.29    4.54 47900000         bcrskip [8]
                0.09    4.46 47900000/118100000     bmw_bcrext_tree_RBT_FIND [4]
-----------------------------------------------
                0.09    1.30   50000/50000       main [2]
[9]     11.3    0.09    1.30   50000         bmw_free [9]
                0.02    0.68 20500000/20500000     free [13]
                0.10    0.45 20450000/20450000     bmw_bcrext_tree_RB_REMOVE [17]
                0.05    0.00 20450000/20450000     bmw_bcrext_tree_RB_NEXT [25]
                0.00    0.00   50000/50000       bmw_bcrext_tree_RB_MINMAX [61]

検索(find)と追加が(insert)rbtree(3)の方が遅いよね。

とはいえcalledの回数みれば判ると思うけど僅かな差が積もり積もった結果なので、普通のプログラムなら性能差が問題になることは無いので普通でない人以外は安心してよろしい。

それにそもそもO由来のtree(3)の方が性能がいいとはいえど、実はOのtree(3)には

  • RB_*
  • RBT_*

という2つの実装が存在してNにも移植されてる前者が高速なのだけど、後者はrbtree(3)の性能低下なんぞ誤差にみえるくらいクソ性能悪いんだよな。

$ time ./test
  120.27s real   105.54s user     0.01s system
index % time    self  children    called     name
-----------------------------------------------
                1.46   11.43   50000/50000       main [2]
[3]     60.8    1.46   11.43   50000         bmw_comp [3]
                0.13    6.53 70200000/118100000     bmw_bcrext_tree_RBT_FIND [4]
                0.05    3.80 20450000/20450000     bmw_bcrext_tree_RBT_INSERT [9]
                0.06    0.69 20500000/20500000     malloc [17]
                0.00    0.17   50000/50000       bmw_bcrext_tree_RBT_INIT [32]
-----------------------------------------------
                0.09    4.46 47900000/118100000     bcrskip [8]
                0.13    6.53 70200000/118100000     bmw_comp [3]
[4]     52.8    0.21   10.99 118100000         bmw_bcrext_tree_RBT_FIND [4]
                5.21    5.77 118100000/118100000     rb_find [5]
-----------------------------------------------
                5.21    5.77 118100000/118100000     bmw_bcrext_tree_RBT_FIND [4]
[5]     51.8    5.21    5.77 118100000         rb_find [5]
                2.06    2.25 883500000/1037250000     bmw_bcrext_tree_RBT_COMPARE [7]
                1.46    0.00 883500000/1078150000     rb_e2n [13]
-----------------------------------------------
                0.29    4.54 47900000/47900000     bmw_exec [6]
[8]     22.8    0.29    4.54 47900000         bcrskip [8]
                0.09    4.46 47900000/118100000     bmw_bcrext_tree_RBT_FIND [4]
-----------------------------------------------
                0.09    1.88   50000/50000       main [2]
[12]     9.3    0.09    1.88   50000         bmw_free [12]
                0.04    0.79 20500000/20500000     free [15]
                0.04    0.68 20450000/20450000     bmw_bcrext_tree_RBT_REMOVE [18]
                0.02    0.22 20450000/20450000     bmw_bcrext_tree_RBT_NEXT [28]
                0.09    0.00   50000/50000       bmw_bcrext_tree_RBT_MIN [38]

さすがにこれ誤差だよと無視できない遅さなんやな、そもそもRBT_*はマニュアルにも載ってないのだけどなんで追加したんだろうな。

このRBT_*の方の作者はOのdeveloperのdlg@で最近(2016年)の仕事なのだが、特に意味なく追加されたとはOの厳格なレビューシステム上考えにくいのでなんか理由があるんだろうな。 まぁqueue(3)だって機能毎に何種類もありそれぞれ性能も違うので必要に応じての使い分けるべきなんだろう、きっとRBT_*にはRB_*には無い素敵な器官が備わってるに違いない俺にはよくわからないし調べる気力もないが。

はい最後、赤黒木でなく配列を使った最初の実装での結果。

$ time ./test
   50.05s real    49.73s user     0.00s system
-----------------------------------------------
               26.98    1.86   50000/50000       main [1]
[3]     85.3   26.98    1.86   50000         bmw_comp [3]
                0.86    1.00   50000/50000       qsort [7]
                0.00    0.00   50000/50000       malloc [30]
-----------------------------------------------
                0.35    3.49 47900000/47900000     bmw_exec [4]
[5]     11.4    0.35    3.49 47900000         bcrskip [5]
                2.22    1.27 47900000/47900000     bsearch [6]
-----------------------------------------------
                2.22    1.27 47900000/47900000     bcrskip [5]
[6]     10.3    2.22    1.27 47900000         bsearch [6]
                1.27    0.00 389700000/562750000     bmw_bcrext_cmp [8]
-----------------------------------------------
                0.01    0.00   50000/50000       bmw_free [12]
[13]     0.0    0.01    0.00   50000         free [13]
                0.00    0.00   50000/50000       idalloc [28]

テストデータを改めてパターンに含まれる文字種が大幅に増えたので、前回はrbtree(3)よりも3倍速いシャアが来るだったのに同じくらいにまで劣化してしまった。 クッソ雑な実装なので重複排除に毎回配列を頭から嘗めてるし最後にはqsort(3)とかやっとるしな、そらそうなるよ知ってた。だからこそ赤黒木で再実装したんだけど。

ただまぁbcrskip()の部分だけみるとbsearch(3)の方が速いですわな二分探索と二分探索木の計算量ってどうだったっけ、うーん性能出すためには自前でAVL木とかも実装してテストしろってアリ地獄コースなんですかこれ。 またどんどん横道逸れてって本題解決しないパターンじゃないですかやだー。

2021/06/05(Sat)

[オレオレN6] 続・rbtree(3)遅くね

前回記事で普通のプログラムならあまりtree(3)とrbtree(3)の性能差は気にしなくていいと書いたけど、最適化オプションありだと歴然とした差がでてくるので、性能が求められるケースではtree(3)使うべきなんやな。 いまんとこプロファイルに-pgつけてコンパイルしてるからinline化とか無視されるのでそれほど差は出ないのだけども。

どうしてもrbtree(3)は何度となく呼ばれるキーの比較が関数ポインタ経由なので遅くなるわね、tree(3)だとinlineで定義できるから比較が文字のコードポイントの大小比較みたいに単純なほど高速になる。

つーことで 過去記事の暇つぶしで実装したオレオレJSONパーサーもrbtree(3)でなくtree(3)使うように変更しますかね、rbtree(3)より移植性よろしいしRBT以外はヘッダファイルだけで完結するから同梱しやすい。

残る問題はmalloc(3)がボトルネックって話なのだけど、アロケーター用意のめんどくさいので手抜きして

--- bmw.c.orig	2021-06-05 10:19:47.000000000 +0900
+++ bmw.c	2021-06-05 10:46:39.000000000 +0900
@@ -58,6 +58,8 @@ struct bmw_pat {
 	size_t lastp;		/* last index of pattern */
 	struct bmw_bcrext_tree bcrext;/* bad character rule */ 
 	size_t md2;		/* mini delta */
+	size_t size;
+	struct bmw_bcrext nodes[];
 };
 
 bmw_pat_t
@@ -71,26 +73,24 @@ bmw_comp(const wchar_t *pat, size_t patl
 
 	assert(pat != NULL);
 
-	if (patlen < 1)
+	if (patlen < 1 ||
+	    patlen > (SIZE_MAX - sizeof(*b)) / sizeof(b->nodes[0]))
 		return NULL;
-	b = malloc(sizeof(*b));
+	b = malloc(sizeof(*b) + patlen * sizeof(b->nodes[0]));
 	if (b == NULL)
 		return NULL;
 	b->pat = pat;
 	b->patlen = patlen;
 	b->lastp = b->patlen - 1;
 	RB_INIT(&b->bcrext);
+	b->size = 0;
 	for (i = 0; i < patlen; ++i) {
 		c = (wint_t)pat[i];
 		skip = b->lastp - i;
 		tmp.c = c;
 		node = RB_FIND(bmw_bcrext_tree, &b->bcrext, &tmp);
 		if (node == NULL) {
-			node = malloc(sizeof(*node));
-			if (node == NULL) {
-				bmw_free(b);
-				return NULL;
-			}
+			node = &b->nodes[b->size++];
 			node->c = c;
 			RB_INSERT(bmw_bcrext_tree, &b->bcrext, node);
 		}
@@ -144,13 +144,5 @@ bmw_exec(bmw_pat_t b, const wchar_t *s, 
 void
 bmw_free(bmw_pat_t b)
 {
-	struct bmw_bcrext *node, *next;
-
-	assert(b != NULL);
-
-	RB_FOREACH_SAFE(node, bmw_bcrext_tree, &b->bcrext, next) {
-		RB_REMOVE(bmw_bcrext_tree, &b->bcrext, node);
-		free(node);
-	}
 	free(b);
 }

とパターン文字列分のnodeを事前に作っておくだけで解消し20%ほど速くなった。 まぁこれだとパターン文字列には同じ文字いっぱいあるとメモリの無駄が大きくなるのだが、もうこれでいいよって気もしてきた(飽きてる)。

こっちが都度malloc(3)版

$ time ./test
   48.21s real    47.90s user     0.04s system
index % time    self  children    called     name
-----------------------------------------------
                0.73    5.78   50000/50000       main [2]
[3]     58.3    0.73    5.78   50000         bmw_comp [3]
                2.13    0.84 70200000/118100000     bmw_bcrext_tree_RB_FIND [4]
                0.93    1.03 20450000/20450000     bmw_bcrext_tree_RB_INSERT [7]
                0.08    0.79 20500000/20500000     malloc [10]
-----------------------------------------------
                0.03    1.33   50000/50000       main [2]
[9]     12.2    0.03    1.33   50000         bmw_free [9]
                0.04    0.59 20500000/20500000     free [13]
                0.12    0.49 20450000/20450000     bmw_bcrext_tree_RB_REMOVE [14]
                0.07    0.00 20450000/20450000     bmw_bcrext_tree_RB_NEXT [27]
                0.02    0.00   50000/50000       bmw_bcrext_tree_RB_MINMAX [31]

そしてこちらが一気にmalloc(3)版

$ time ./test
   37.84s real    37.64s user     0.01s system
index % time    self  children    called     name
-----------------------------------------------
                0.79    4.67   50000/50000       main [1]
[3]     61.2    0.79    4.67   50000         bmw_comp [3]
                2.16    0.75 70200000/118100000     bmw_bcrext_tree_RB_FIND [4]
                0.84    0.90 20450000/20450000     bmw_bcrext_tree_RB_INSERT [7]
                0.00    0.01   50000/50000       malloc [21]
-----------------------------------------------
                0.00    0.01   50000/50000       main [1]
[17]     0.1    0.00    0.01   50000         bmw_free [17]
                0.00    0.01   50000/50000       free [18]

これだけで10秒高速化ってのは大きいな。

あとは検索に有利といわれるAVL木を試したいところなんだけど、自分で書く前に GNU libavl使ってみたがあまりいい結果は出なかった。

こちらがGNU libavlのAVL木での測定結果。

$ time ./test
   67.93s real    67.86s user     0.04s system
index % time    self  children    called     name
-----------------------------------------------
                1.14   11.05   50000/50000       main [2]
[3]     57.8    1.14   11.05   50000         bmw_comp [3]
                3.74    1.41 70200000/118100000     avl_find [4]
                0.18    4.64 20450000/20450000     avl_insert [5]
                0.05    1.02 20500000/41000000     malloc [12]
                0.00    0.00   50000/50000       avl_create [47]
-----------------------------------------------
                2.56    0.96 47900000/118100000     bcrskip [9]
                3.74    1.41 70200000/118100000     bmw_comp [3]
[4]     41.2    6.30    2.37 118100000         avl_find [4]
                2.37    0.00 876100000/1158500000     bmw_bcrext_cmp [10]
-----------------------------------------------
                0.05    4.02   50000/50000       main [2]
[8]     19.3    0.05    4.02   50000         bmw_free [8]
                1.57    1.13 20450000/20450000     avl_delete [11]
                0.03    0.71 20500000/40950000     free [15]
                0.58    0.00 20500000/20500000     avl_t_first [22]
                0.00    0.00   50000/50000       avl_t_init [63]
-----------------------------------------------
                0.31    3.52 47900000/47900000     bmw_exec [6]
[9]     18.2    0.31    3.52 47900000         bcrskip [9]
                2.56    0.96 47900000/118100000     avl_find [4]

そんで同じく赤黒木での測定結果。

$ time ./test
   61.31s real    61.23s user     0.05s system
index % time    self  children    called     name
-----------------------------------------------
                1.04    8.45   50000/50000       main [2]
[3]     54.0    1.04    8.45   50000         bmw_comp [3]
                3.23    1.05 70200000/118100000     rb_find [4]
                0.14    3.08 20450000/20450000     rb_insert [7]
                0.07    0.89 20500000/41000000     malloc [12]
                0.00    0.00   50000/50000       rb_create [48]
-----------------------------------------------
                2.20    0.71 47900000/118100000     bcrskip [8]
                3.23    1.05 70200000/118100000     bmw_comp [3]
[4]     40.9    5.43    1.76 118100000         rb_find [4]
                1.76    0.00 883500000/1156100000     bmw_bcrext_cmp [11]
-----------------------------------------------
                0.05    3.60   50000/50000       main [2]
[6]     20.7    0.05    3.60   50000         bmw_free [6]
                1.24    1.08 20450000/20450000     rb_delete [10]
                0.02    0.80 20500000/40950000     free [14]
                0.47    0.00 20500000/20500000     rb_t_first [22]
                0.00    0.00   50000/50000       rb_t_init [64]
-----------------------------------------------
                0.29    2.92 47900000/47900000     bmw_exec [5]
[8]     18.3    0.29    2.92 47900000         bcrskip [8]
                2.20    0.71 47900000/118100000     rb_find [4]

巷ではAVL木は赤黒木より検索速いなんていわれてるけど逆に遅いやんけ、追加が遅いのはまぁ当然なんだけど。 あと実装上のオーバーヘッドでrbtree(3)よりも遅いというね。

そういやナイーブ法での速度を計測するのを忘れてたのでmemmem(3)のワイド文字版でっちあげて測定。

$ time ./test
   76.72s real    76.08s user     0.01s system

つーことで48秒→76秒だから1.6倍くらいは速くなってますかね、もちろんパターンを使い回せるケースであればもっと効果は劇的なんだけど。

ちなみに同じテストデータをUTF-8にしてBM法すなわちbm(3)使って検索すると

$ time ./test
   10.80s real    10.76s user     0.01s system

ナイーブ法すなわちmemmem(3)で検索すると

$ time ./test
  340.02s real   338.79s user     0.00s system

と34倍近いことを考えると微妙な気分にはなる、でもわずかでも速いは正義。

まぁでもUTF-8の場合1,402,795バイト中から4,196バイトを検索してるのだけど、ワイド文字であれば472,412文字から1,404文字を検索と計算量減るので、ナイーブ法とBM法の差はそりゃ見た目小さくなるのは当然ではある。 ナイーブ法同士を比較してもワイド文字ってだけですでにUTF-8よか4.5倍近く速くなってるわけでしてな。

2021/06/15(Tue)

[プログラミング] ISBN10/13変換および自動ハイフン化ライブラリ書いたよ

@ここは私のポエム帖

ブルー・オリジンで華々しく宇宙へ飛び立つベゾス兄弟、彼らを追いかけるようにひっそりと所属不明のロケットが打ち上げられたことに気づいたものはいなかった。 切り離されたペイロードから金色のケープを纏ったキラー衛星が漆黒の闇に放たれる、素人目には不格好な望遠鏡にしかみえない高出力自由電子レーザー砲、その架台の下には数えきれないほどのサインが刻まれている。 それはここ数十年で廃業を余儀なくされた世界中の名も無き全書店と小売業の名前であり、宇宙を漂う弔いの墓標であった。

どうですかスピルバーグ監督、こんな復讐劇の映画化権買ってくだち!

スピルバーグ監督「民間人がキラー衛星打ち上げって現実味無いよね、それならブルー・オリジンに同乗する権利ならたった30億で落札なので、しれっとトイザらスあたりのアマゾンに恨み骨髄に達してそうな小売業・卸売業の創業者が身分隠して搭乗して、オリエント急行エンドのミステリ仕立ての方がいいんじゃねえかな」

@ここからはいつものどうでもいい話

ということで(どういうことで)以前ちょろっと触れた ISBNの10/13変換と自動ハイフン化ライブラリCで再実装するってやつ、放置しっぱなしだったのをあるていど形にしてBitBucketに 上げておいた、中身は書き捨てのゴミなので読む必要ないです。

いまのところ変換テーブルはCソースで抱え込んでるので定期的にアップデートしないとアレなのでJSONで外部ファイルにしてcronとかでアップデートできるようにする予定なのだが、オレオレJSON parser for CのAPI/ABIが固まってないのでこれはまたいずれ。

それと今時Cでプログラム書く莫迦もおらんので、せめてPerl XSバインディング(Perlで書く莫迦もいねえよ)くらい用意しておこうと思ったが何もかもがめんどくさい精神状態になってきたのでこちらもまたいずれ。

検索すると同様のISBN10/13変換および自動ハイフン化をしてくれるWEBサービスはあるんだけど、たいてい日本国内つまり978-4あるいは4ではじまるISBNコードしか対応してないんだよね。 そんでPythonだとisbn_hyphenateというライブラリがあるのだけど、前回も書いた通りバグってやがるので使い物にならない。

こんなライブラリで何がうれしいかというと何ひとつ嬉しいことは無いので、やっぱりBitBucketの肥やしにしかならないのである。 たいていの蔵書検索サービスやネット書店はISBN10/13ハイフン有り無しどれでも検索できるしな、唯一ブコフのオンラインがISBN13必須ってくらいか。

せめて出版社コードから出版社名称に変換できる機能とかあると使い道もありそうなんだが、あれ有料データベースしか無えんだよな。

2021/06/17(Thu)

[どうでもいい話] どうでもいい話

ほんとうに UTF-8N復権派はばかだな、ばかな理由は 前に書いたので省略でいいよね、うわぁ…と石めくったら虫いっぱい出てきた時の顔をしている。

話題のLKMLに登場した アレ、anti-vaxという単語からすると日本と違って欧米はワクチン接種で技適マークでなく

d|i|g|i|t|a|l

が表示されるんだなぁと勉強になった。 欧米人がみなVAXになってしまうとIEEE754とかThread Local Storageとか大変だなと思いました。

このポストのおかげで今年の四月莫迦で本家Nに covid(4)というクソ寒いネタがcommitされたやつ、Vaccineの略がvaxだからport-vax MLだったのかと今更気づくなど、英語の勉強になった。

そいや古いiPhone5s/6とかの機種にまさかのiOS12.5が配信されExposure Notificationに対応してもう半年以上経つのだが、未だにCOCOA対応せんのよなと思ったら 3月の時点でギフハブにwontfixタグついてたのな、これタグ打っとるの政府CIO補佐官/COCOA連携チームとやらの立場の人間なので公式に対応する気は無えってことなんだろう、でもまぁ理由くらい書いてからタグ打てよとは思う。

透けてる内部事情をエスパーすると、開発にiOS/Androidネイティブ言語でなくC#/Xamarin採用してしまったことでそっちの制限事項とは察するのだが、それも Xamarin.ExposureNotification今年の1月には対応済で解消されてそうなもんなんだが、そこから2か月経過してるにも関わらずwontfix打つってことは、もう公式にCOCOAはiOS12.5対応以前にAPIv2対応すらやる気ありませんと宣言したと同義でええんかな。

ちなみにCOCOAでいろいろインターネッツを検索してたら、いいタイミングで 興味深いポエムがポエムサイトに挙がってて笑ってしまった。 「分かりやすい犯人を見つけて、思考せず叩く、というような行為」の一文だけ読めばいいのだが、こういう文章を読みに来るポエム愛好家はたいてい最初から自分の中の犯人が決まってる手合いだからまぁ燃料になるだけだな。

前も書いたけどワイ的にはCOCOAの失敗は些細な問題で、やっぱり一国一アプリ制限を設けた上にモック以外のテスト環境用意しとらんビッグなテックこそが失敗の本質だなという感想しかないのだ。 やはりあいつらに世界政府など億万年早いわ。

いまさら伽藍とバザールとか黴臭い古文書持ち出すのもアレやけど、あれの19の法則の半分近くがこれだけで封じられるんだよな、どんな判段だ。 多様性さえあればいくらCovid19Radar/COCOAが生まれた瞬間から失敗が約束されてるシロモノだろうと、他のアプリに乗り換えることが容易にできればどうでもいい話なのだよね。

まーそもそも追跡アプリで封じ込め成功!と喧伝された国もその後ばんばん感染広がってる現状からすると追跡アプリたいした効果無いんじゃねえの感ある。 その程度のシロモノだとすると戦犯探すのすらアホらしい話で、ろくろ回しに踊らされてITに過剰な万能感抱いとる国民様がアレで、優先順位低かった厚労省の判断の方がまともだったってことになるんだよな。

2021/06/19(Sat)

[Windows][I18N] 文字列照合順序

Excelのソート順の話、こういう並びになるのは「文字列照合順序(Collation)」日本語の場合だと「 JIS X 4061 日本語文字列照合順番」に従ってるからですな。 LibreOfficeもふつーにこういう並べ替えをするので、Google Spredsheetがアレというお話。 つーか自分のアカウントでGoogle Spreadsheet試したらちゃんとCollationに従ってOfficeと同じ並びしますがな、言語設定?

以前Collationについては途中まで解説を 書いてめんどくさくなってやめてしまったな、来世の俺に期待。

そっちの記事でもサンプル書いたけど、文字列照合順序についてはC言語だとstrcoll/wcscollという関数で実装されてる。

$ cat >unko.c
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int
cmp(const void *p1, const void *p2)
{
	const char *s1, *s2;
	s1 = *((const char **)p1);
	s2 = *((const char **)p2);
	return strcoll(s1, s2);
}

int
main(int argc, char **argv)
{
	int i;

	setlocale(LC_ALL, "");
	qsort(&argv[1], argc - 1, sizeof(*argv), cmp);
	for (i = 1; i < argc; ++i)
		printf("%s\n", argv[i]);
}
^D
$ make unko
$ LANG=ja_JP.UTF-8 ./unko.exe 一 二 三 第
一
三
第
二

はいExcelと同じ並びになりましたな、ざっくりとした説明としては漢字の読みがなを50音順に並べてると思ってもらえればおk。

論より証拠で同音混ぜてみようか。

$ LANG=ja_JP.UTF-8 ./unko.exe 一 壱 二 弐 三 参 第 大
一
壱
三
参
大
第
二
弐

ちゅうこと。

読みがなについて漢字は複数あるのでJIS X 4061では「代表読み」で丸められるのだけど、Excelにふりがなデータ与えるとそっち優先してくれるみたいね。

ちなみにWindows 10のスタートメニューだとおそらくMS-IMEの辞書から逆変換した結果を元に並べてるっぽいってのはこれも ちょっと前の記事で書いた通り。

2021/06/22(Tue)

[WWW] Cookie利用同意ってもはや骨抜きもいいとこだよね

先日のGoogle Spreadsheetのソート順の話の続き、照合順序は「ファイル」→「Googleスプレッドシートの設定」→「全般」→「言語と地域」くらいしか設定項目見つからないけど、こいつをアメリカ合衆国にしたら読みがな順でなくUnicodeコードポイント順になるのは確認した。

新規作成する場合の初期値ってどっかあるのかなとGoogleアカウント設定画面に飛ぼうとしたらブラウザハングするのでこれ以上は追及ヤメ、どうもCookie拒否からのリダイレクトの無限ループと広告ブロックアドインのメモリリークの合わせ技っぽい。

そもそもCookie拒否したらアクセス認めないというトラッキングウォールはGDPR違反のはずなんだがePrivacy規則が骨抜きになってしまったのがね、おかげで開き直って同意ボタンしかなく押さないとポップアップダイアログ消せず記事読めないみたいなサイトばっかになってしまった。 まぁそういうグレーなとこはもう一度しっかり殴られろ以外に言うことは無いのだが、法改正を見据えてかCookieを受け入れないと延々と「毎日5%OFFクーポンプレゼント!」みたいなポップアップが延々と出続けて邪魔みたいな、トラッキングウォールではないが極端に利便性を下げることでCookie有効化を誘導するちゅー姑息な手口を使うサイトがすでに出現しとってイタチごっこやねもう。 ほんまあっちの業界は悪知恵働くというかタガ外れちゃってんだなと感心することしきりである、ミンナニデクノボートヨバレ ホメラレモセズトモ アアイウモノニ ワタシハナリタクナイ

ちなみにyoutubeもCookie拒否してるとコメント欄が読み込まれないのだが、この世の地獄を視界に入れたくないのでむしろ推奨設定といえる。

2021/06/27(Sun)

[宗教] ThinkPad X61とWindows 10(21H1)

@Windows 11

世間様がWindows 11の要求スペックで騒いでるが、要求スペックにかすりもしないオンボロマシンしか持ってないワイには関係ないのである、新マシンは来世にご期待下さい。

バッサリ足切りしたい理由はとどのつまり例のCPU脆弱性によるDRM回避問題であっちのギョーカイからの圧力なんだろうとは察するのだが、買い替え進まないとそのうち地デジみたいに国に補助金クレクレと言いだしそうである。

そんなどうあがいても動かんOSの話より、ワイにとってはThinkPad X61がWindows 10 21H1へのアップデート途中で死ぬ問題の方が重要なのである。 ロールバックかかって再起動してしまうんだよな。

原因究明に SetupDiag.exe実行して回収したsetup*.log読んでるんだが、どこで引っかかってるのかさっぱり判らねえのである、やはりWindows 8が安値でバラ巻かれてる時に買っておくべきであった。

Error: SetupDiag reports rollback failure found.
Last Phase = Pre SysPrep
Last Operation = Upgrade security
Error = 0xC1900101-0x30018
LogEntry: 

エラーコード曰くドライバの互換性問題のようなのだけど、どうやって特定すりゃいいんすかねこれ。

まぁ作曲用マシンでそもそもその作曲自体もう何年もやってねえしDTMソフト更新する気も無いしそもそも 音源カードが19H1の時点でトラブってるのでいっそのことWindows 7にもどしちまうかだな。

手詰まり感あるので最後のあがきとしてVHDブート化試してダメならWindows 7に戻しますかね、つーか20H2あたりからもっさりしてきて耐えられなくなってきた。

@今日のポエム

拝金主義のAIと差別主義のAIそして陰謀論のAIによる合議制で統治された21世紀のインターネッツ錬金術企業、称賛するインフルーエンサー(笑)と養分たち。これキングクリムゾンの歌詞みたいですき。

前も書いたような気がするけどAIのアルゴリズムがブラックボックスってのは非常に危険なんですわ、誰がAIが21世紀の精神異常者だと判定するのか医者は何処だ?

偉大なる詐欺師に騙されてイージーマネーを求めてディシプリンなんて捨ててゲームチェンジ(笑)、ほんと素晴らしい時代である。

@地獄からの使者

ちょっと古い記事を今更見つけてそうめん吹いたのだが、SCOからOpenServerの権利をお買い上げしたXinuosが17年ぶり2度目の IBMとRHEL相手に訴訟起こしたって、これ四月莫迦記事じゃないのかよ!

かつてのSCOの訴えが退けられた根拠となってる USL vs BSDのUNIX訴訟の和解内容で争うのを避けるために、それ以降の1995年以降に書かれたコードがパクられてるって主張のようだけど、どの部分を指してるのかほんのちょっとだけ気になる。 訴状ってどっか見れないのかなぁ。

というよりOpenServerがまだ生存してる上に10以降はkernelをSVR5からFreeBSDに置き換えてたってのが一番の衝撃なのであった。