コグノスケ


2023年9月2日

さようならdocomo回線、こんにちはSoftBank回線

目次: プロバイダ

繁華街の駅(渋谷とか新宿とか)で圏外を連発するahamoにさよならして、Y!mobileにMNPしました。ソフトバンク系を使うのは初めてです。大昔J-PHONEを使っていましたが、あの時はまだソフトバンクとは関係なかったですね。懐かしいな。

日が経っていないので使用感や良し悪しはわかりません。3Gに切り替わる頻度がahamoより高い?のが若干気になるくらいで、今のところは普通です。しばらく様子見しましょう。

さようならドコモ光、こんにちはSoftBank光

携帯と紐づいていたドコモ光も一緒にさようならしました。代わりにSoftBank光になりました。切り替えは来週だそうです。携帯と違って、光回線(ドコモ光+DTI)は特に問題なかったので、変えない方が良かった!!となる可能性もあります。

一点不安があるとすると、ドコモ光はプロバイダが好きに選べましたが、SoftBank光はプロバイダがYahoo!BB固定らしいことです。もしYahoo!BBの回線速度や提供機能に不満があっても、解約する以外に解決する手段がありません。光回線は在宅勤務の命綱なので、もしダメだったら違約金でも何でも払って速攻で乗り換えするしかないのが辛いところです……。

洗濯機を買い替え

先日壊れた(2023年8月22日の日記参照)洗濯機Panasonic NA-VR5600Lの買い替えのためヤマダ電機に行きました。最終的に買ったのは2022年モデルのPanasonic NA-LX127BL です。

Panasonicは10月に2023年モデルを発売するらしく、2022年モデルは在庫がほとんどありませんでした。ヤマダ電機の店先にも現物がほとんどなくて困っていたところ、1台だけ店頭展示品が残っていたのでこれ幸いと買いました。家電の能力的には2022年モデル、2023年モデルのどちらでも良いんですけども、乾燥機無しで10月まで耐えるのはさすがに辛いです……。

店頭展示品は色んな人がガチャガチャ触ってるのと、通電されっぱなしで基板へのダメージが若干不安ですが、値段は安いです。ドアの開く方向も選べませんが、偶然一緒でラッキーでした。

d払い残高とMNPの罠

神奈川県の近辺に住んでいることもあり、かながわPayを活用してポイントをもらおうと思っていました。支払い方法はいくつか選べますが、10万円を超える決済ができる支払い方法は少なく、数少ない高額決済に対応していたd払い残高(銀行口座から入金するタイプ)で払うつもりでした。

  • 1〜3日目: 30万円入金する(10万円/日の入金制限があるため、3日必要)
  • 4日目以降(今日): 洗濯機を買ってd払いで払う

が、しかしですね、ドコモ回線をソフトバンク回線にMNPした瞬間にd払い残高がどこかに行ってしまい、支払うことができませんでした。なんということだ。


銀行口座から3日間掛けて30万円をd払い残高に入金


d払い残高から30万円をどこへ出金したのだろう??

銀行口座にはまだ30万円は戻ってきていないようです。お金が返ってくるまでは何とかしたいですけど、その後はもう二度とd払いは使わないでしょう……。携帯電話に紐づいているサービスは私には難しすぎました。

編集者:すずき(2023/09/24 12:20)

コメント一覧

  • hdkさん(2023/09/04 09:23)
    https://service.smt.docomo.ne.jp/keitai_payment/app/help/detail/0563.html
    「解約から1〜2週間を目途に」 '`,、('∀`) '`,、
  • hdkさん(2023/09/04 09:27)
    上のは逆パターン (他からahamo) でしたね。以下の記事のパターンかも知れません。だいぶ面倒そう。
    https://note.com/game2ji/n/na13ded86c590
  • すずきさん(2023/09/04 10:49)
    この辺を見ると、MNPしても使えそうなのですが、
    https://service.smt.docomo.ne.jp/keitai_payment/app/help/detail/0080.html

    こちらに該当するようで、MNPしたらd払い残高は没収されるのが仕様みたいです。
    https://service.smt.docomo.ne.jp/keitai_payment/app/help/detail/0155.html

    ちゃんと調べずにMNPしたのが悪いのと、変にゴネてもヤマダ電機の人が困るだけなので、かながわPayは諦めてクレジットカードで払いました……。
  • hdkさん(2023/09/04 21:00)
    "dアカウント"とやらがあるかないかで変わるんですかね... 1日違いで面倒なことになったものですね。
  • すずきさん(2023/09/06 18:21)
    dアカウント自体はMNPと関係なく存在するのでさらにややこしいですね。

    d払い残高はなぜかドコモ回線と紐づいていて、ahamoなどへ切り替えたり、MNPしたりすると無効化される罠があるようです。常人には予想不能な挙動です。
open/close この記事にコメントする



2023年9月3日

Might and Magic Book One TAS US版(更新)がTASVideosにAccept

目次: Might and Magicファミコン版

先月末くらいにMight and Magic Book One TAS US版の更新版(7m 19s)のTASをTASVideosに投稿しました。今日見たらAcceptedを経てPublishedになっていました([TAS] NES Might and Magic: Secret of the Inner Sanctum "item glitch" by katsuster in 07:19.61 - YouTube)。良かった良かった。

2023年にMight and Magic Book Oneで遊ぶ人はほとんどいないと思っていましたが、ニコニコ動画で取り上げている配信者さんがいたり【マイトアンドマジック(ファミコン版)】学研の闇を子供たちに叩き込んだゲーム【VOICEROID解説】 - ニコニコ動画)して、世の中良くわからんもんです。レトロゲーとして遊んでみる人はいても、TASに挑む人はまずいないと思うので、20年くらいは記録をキープできるんじゃないですか。はっはっは。

編集者:すずき(2023/09/08 16:05)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年9月4日

d払い残高が返ってきた

目次: プロバイダ

先日ソフトバンク回線にMNPした際に没収された30万円分のd払い残高(2023年9月2日の日記参照)が、無事戻ってきました。


d払い残高が復活

面倒な手続き等もなく全額が戻って良かったですが、残念なことにかながわPayのポイント還元キャンペーンは終わってしまいました(予算を使い切ったとのこと)。d払い残高を使う唯一の理由=かながわPayだったので、今後どうしましょうね。

出金機能で銀行口座に戻そうと思いきや、1回最大2万円、手数料220円という驚きの手数料d払いアプリで出勤する - d払い)が掛かるようです。もし30万円を全額出金すると、15回出金、手数料は3,300円です。高ぇーー。

幸いにも近所のスーパーでd払いが使えるようなので、30万円分を使ったらサヨウナラですかねえ。

編集者:すずき(2023/09/24 12:23)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年9月8日

Windows 10のタスクバーの困ったところ

目次: Windows

Windows 10は標準でタスクバーボタンの入れ替え機能がありますけど、これがイマイチ使いにくいです。違うアプリならば入れ替えられますが、同じアプリ内でボタンを入れ替えられないのです。


同じアプリ内でボタンを入れ替えられない

Windows 11はさらにダメになっていて、タスクバーボタンの結合を解除する機能がなくなってしまいました。ノートPCはWindows 11にしましたが、デスクトップPCは移行する気が起きません。将来的にタスクバーとボタンという仕組みをなくしたいんでしょうか?

スマホみたいに、何か押さないとタスクの一覧が出ないタイプはすこぶる使いにくくて嫌いなんですよね……。

編集者:すずき(2023/09/24 13:26)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年9月10日

長物エアガン2号機M4A1カービン

目次: 射的

長物エアガンはスピードシューティングに全く向かないドラグノフしかありませんでしたが、もう少し撃ちやすいものも欲しいなと思って、東京マルイ M4A1カービン(メーカーの製品サイト)を購入しました。

長物はかなり高い(5万円くらいする)ことと、ハンドガンと違って家に置く場所がないので、いくつか買っていい感じのものを使う作戦が取れません。知識もありませんので、練習会でアドバイスをもらって素直におすすめされた機種を購入しました。おすすめされるだけあって撃ちやすいです。

大会で使うのはハンドガンなので、普段の練習会で使うというよりはイベントの時に活躍してもらうとしましょう。

編集者:すずき(2024/10/20 14:18)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする




2023年9月15日

日本語とアルファベットの間にスペースを入れるのをやめた

私は昔からの癖で、文章を書くときに日本語と半角アルファベットの間に半角スペースを入れています。日本語と半角アルファベットが異様にくっついて表示されてしまう環境において、少しでも自然に見えるようにする姑息な手段です。

昔ならまだしも今となってはあまり意味がなく、そろそろやめようかなあと思っています。これから書く文章は自分の気持ち次第なのでどうにでもなりますが、今まで書いた日記はどうしようかな〜と悩んでいます。

根本治療は意外と難しい

過去の日記を眺めていましたが、一括の置換で直すのは結構大変そうです。基本的には「日本語と半角文字」もしくは「半角文字と日本語」の間にあるスペースを消すのですけど、例外的にスペースを維持したいパターンがあります。

  • 削除したい: "この -a の項"のような場合の、-記号の前のスペース
  • 削除したくない: "タイトル - サイト名"のような場合の、-記号の前後のスペース

残念なことに文脈依存なので区別が付きません。根本治療は諦めて下記の置換で簡易処置しました。

vim用の全バッファ置換コマンド
:bufdo %s/\([^a-zA-Z0-9  	!-~]\) \([a-zA-Z0-9]\)/\1\2/gc
:bufdo %s/\([a-zA-Z0-9]\) \([^a-zA-Z0-9  	!-~]\)/\1\2/gc

どうしてもスペースの削除漏れが出るためか、ところどころバランスがおかしいですね……。元々あってもなくても良かったものですから、表示が大きく崩れるでもしない限りはこのままで良いでしょう。

編集者:すずき(2023/09/16 21:48)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年9月18日

一覧の一覧 - まとめリンク

一覧の一覧、まとめのまとめが欲しくなったので作りました。

OS、アーキテクチャ系。

C言語とかコンパイラ。

趣味、生活系。

編集者:すずき(2024/11/15 23:22)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年9月21日

FizzBuzzを速くする1(自作アルゴリズム)

目次: ベンチマーク

FizzBuzzをご存じでしょうか?元々は英語圏の遊びで、1を最初にして、順に1ずつ足した数を宣言します。ただし3の倍数でFizz、5の倍数でBuzz、15の倍数でFizzBuzzと言わなければなりません。ルールはこれだけで単純です。試しに16まで書いてみるとこんな感じ。

FizzBuzz 1から16まで
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16

FizzBuzzの実装は簡単ですが、可能な限り高速に出力しようとするとなかなか面白い遊びになります。自作、他作を含めて高速化の例を紹介したいと思います。

測定レギュレーション

FizzBuzzの実行範囲は1から2^32-2とします。すなわち1 ... 0xfffffffeです。出力される文字列は合計で33.3GBになります。

測定方法は簡単です。pvにパイプで繋いで出力の速度を表示します。速度が一定とは限らないので、並行してtimeで実行時間を測定します。出力が間違っていないかどうかテストするプログラムも必要ですが、本筋とは関係ないので省略します。

速度の測定方法
$ gcc -O3 something.c -o ./fizzbuzz && time taskset 0x1 ./fizzbuzz | taskset 0x4 pv > /dev/null

測定環境は、

  • Intel Pentium J4205/1.5GHz
  • DDR3L-1600 8GB x 2
  • Linux kernel 6.1.52
  • GCC 12.2.0 (Debian 12.2.0-14)
  • glibc 2.36 (Debian 2.36-9+deb12u1)

いわゆる省電力PCで、そんなに速いPCではありません。

基準値

速度の絶対値とともに、一番単純な実装と高速化後で速度が何倍になったかを見ます。高速化のありがたみがわかりやすいでしょ?

単純なFizzBuzz

// fizzbuzz_simple.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	for (unsigned int i = 1; i < 0xffffffff; i++) {
		if (i % 3 == 0 && i % 5 == 0) {
			printf("FizzBuzz\n");
		} else if (i % 3 == 0) {
			printf("Fizz\n");
		} else if (i % 5 == 0) {
			printf("Buzz\n");
		} else {
			printf("%u\n", i);
		}
	}

	return 0;
}

何も難しくないです。測定しましょう。

単純なFizzBuzzの速度
# fizzbuzz_simple.c

33.3GiB 0:07:32 [75.4MiB/s] [                                            <=>   ]

real    7m32.741s
user    7m25.558s
sys     0m51.090s

以降、この速度を基準値とします。

高速化その1 - printfを排除

プロファイリング(perf topなど)を見ると、printf()関連の処理に時間が掛かるようです。おそらく、

  • libc内部のバッファリングが遅い?
  • 毎回標準出力に書きだしていて遅い?
  • 整数から文字列への汎用的な変換処理が遅い?

などが考えられます。対策として、

  • リングバッファを実装、リングバッファ終端は少し余分に確保し、はみ出した分は先頭に折り返してコピー(ライトポインタの制御が最小限で済む)
  • printf()の代わりにwrite()を使用、write()に与える単位をページ(4KB)の整数倍にする
  • 整数から文字列への変換を10進数に限定して高速変換

以上を実装して測定します。整数から文字列への変換は、10000の剰余、10000で除算、を繰り返して4桁ずつ変換します。4桁の数字の変換は起動時に0000から9999までの1000要素のテーブルを作成しておいて、変換時はテーブルからコピーすることで高速化します。

printf排除FizzBuzzの速度
# fizzbuzz_myitoa.c

33.3GiB 0:01:20 [ 424MiB/s] [                 <=>                              ]

real    1m20.416s
user    1m1.746s
sys     0m21.756s

約6倍まで高速化しましたが、まだまだですね。

長い桁数を攻略

プロファイルを見ると整数から文字列への変換をする部分が遅いです。FizzBuzz実行範囲の1〜2^32-2にて最も多く出現し、かつ処理が遅い桁数は10桁、次いで9桁の数字です。遅い部分に集中して高速化します。

作っていて気づかれた方もいましょうが、FizzBuzzは15回で同じパターンがループします。つまり数字の桁数が同じであればFizzやBuzzが出てくるバイト位置も毎回同じのため、あらかじめ書いておくことができます。

最初に30回分のFizz, Buzz, FizzBuzzをあらかじめ書いておいた文字列をバッファにコピーします。数字が入る場所はドットで埋めてあります(後で書き換えるのでスペースでも何でも良い)。こんな感じです。

30回分のFizzBuzz(最初)

const char tmp10[] =
"FizzBuzz\n..........\n..........\n"
"Fizz\n..........\nBuzz\n"
"Fizz\n..........\n..........\n"
"Fizz\nBuzz\n..........\n"
"Fizz\n..........\n..........\n"
"FizzBuzz\n..........\n..........\n"
"Fizz\n..........\nBuzz\n"
"Fizz\n..........\n..........\n"
"Fizz\nBuzz\n..........\n"
"Fizz\n..........\n..........\n";

その後、数字を入れるべき個所を上書きします。例として4つ上書きした状態を示します。

30回分のFizzBuzz(途中)

"FizzBuzz\n1000000021\n1000000022\n"
"Fizz\n1000000024\nBuzz\n"
"Fizz\n1000000027\n..........\n"
"Fizz\nBuzz\n..........\n"
"Fizz\n..........\n..........\n"
"FizzBuzz\n..........\n..........\n"
"Fizz\n..........\nBuzz\n"
"Fizz\n..........\n..........\n"
"Fizz\nBuzz\n..........\n"
"Fizz\n..........\n..........\n";

なぜ15回分ではなく30回分かというと、10回分x 3に分解することができるからです。10回分をまとめるメリットとしては、10の桁より上の桁が全て同じ文字なので一度に書き換えられ、高速化が期待できることです。

コードは長いですが大して難しくないので、興味があればご覧ください。では速度を測ります。

9桁10桁狙い撃ちのFizzBuzzの速度
# fizzbuzz_9_10.c

33.3GiB 0:00:25 [1.31GiB/s] [                        <=>                       ]

real    0m25.372s
user    0m10.515s
sys     0m29.643s

約17倍まで高速化しました。いい感じです。

vmsplice

次はFizzBuzzの出口つまりpvコマンドへのパイプに着目します。今はwrite()を使っている状態で、write()でパイプに書き込むとカーネル内でパイプのメモリ領域へのデータコピーが発生して遅くなっています。

そう言われてもどうすれば?と思いますが、Linuxにはカーネル内のメモリコピー処理を省くためのシステムコールvmsplice()が存在します。

vmspliceの実装例

ssize_t vwrite(int fd, void *buf, size_t count)
{
	struct iovec iov;
	ssize_t n;

	iov.iov_base = buf;
	iov.iov_len = count;

	while (iov.iov_len > 0) {
		n = vmsplice(1, &iov, 1, 0);
		iov.iov_base += n;
		iov.iov_len -= n;
	}

	return count;
}

このようにvmsplice()を呼ぶvwrite関数を作成し、今までwrite()を呼んでいた個所を全てvwrite関数に置き換えます。

カーネル内の実装を調べていないため詳細な理由はわかりませんが、vmsplice()は動きにちょっと癖があってfcntl(F_SETPIPE_SZ)でパイプのサイズをある程度(64KB〜くらい?)大きくしないと、パイプの読み出し側でデータが壊れることがあります。

説明はこれくらいで測定しましょう。

vmspliceを使ったFizzBuzzの速度
# fizzbuzz_vmsplice.c

33.3GiB 0:00:10 [3.16GiB/s] [          <=>                                     ]

real    0m10.543s
user    0m8.921s
sys     0m4.067s

約42倍まで速くなりました。vmsplice()恐るべし。当初7分も掛かっていた2^32のFizzBuzzが、今やたったの10秒で終わるようになりました。

ソースコード

ソースコードはこちらからどうぞ。

編集者:すずき(2023/09/29 16:16)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年9月22日

FizzBuzzを速くする2(高速アルゴリズムの紹介)

目次: ベンチマーク

FizzBuzzの実装は簡単ですが、可能な限り高速に出力しようとするとなかなか面白い遊びになります。前回は自作のアルゴリズムを紹介したので、今回は他の方が開発した高速化手法を紹介したいと思います。名前がないようなので、オフセット0xf6アルゴリズム(仮)と呼ぶことにします。

前回の最速(9桁10桁狙い撃ち+vmsplice)も含めて、ソースコードはGitHubに置いています(GitHubへのリンク)。

整数演算と文字列変換のトレードオフ

FizzBuzzの高速化の難しい点は、数値のインクリメント(=1ずつ増やす)と数字を文字列に変換する処理の両立です。単純な方法としては、現在の数値を整数で保持する方法、文字列で保持する方法が考えられます。

  • 整数で保持: 演算は高速、文字列への変換は低速
  • 文字列で保持: 演算は低速(繰り上がり処理にループが必要で遅い)、文字列変換は不要なので最速

どちらも一長一短で困りました。

整数演算と文字列変換の両立

このトレードオフを見事に解決しているのがオフセット0xf6アルゴリズム(仮)です。最初に要点を列挙しますと、

  • 1バイト1桁(例えば64ビット変数なら8桁収まる)
  • 各桁を0xf6でオフセット(0: 0xf6, 1: 0xf7, ..., 9: 0xff)
  • 桁上がりするまでは数値のインクリメントは整数演算
  • 桁上がりすると下の桁が全部0になるので、Trailing Zeroとマスク演算で0xf6をセットし直す

桁は1つ目に書いた通り、10進数1桁を1バイトで表現します。情報量としては過剰に見えますが文字列変換との両立のためです。


桁とバイト表現

数の表現ですが0 = 0xf6, 1 = 0xf7, 2 = 0xf8, ... 9 = 0xffとします。10進数の123397ならば0xf6f6f7f8_f9f9fffdとなります。


10進数と相当するバイト表現

桁上がりするまで数値インクリメントは+1の整数演算で実現できます。


インクリメント処理は+1の加算処理と同等

ここまではオフセットが変なだけの普通の整数です。

桁の繰り上がり処理

このアルゴリズムは桁の繰り上がり処理がエレガントで、+1の整数演算で適切な桁までの繰り上げが発生します。つまり加算命令1発で良く、ループ処理は必要ありません。先の例では下位の3桁が399から400になります。


桁の繰り上がりが発生した様子

面倒な処理は不要で、+1の整数加算だけで、1の桁より上位の桁(この例だと10の桁も)が全て正しく繰り上がります。このアルゴリズムのナイスポイントその1です。

繰り上がった桁は値0x00になるので手当が必要です。先の例だと12399: 0xf6f6f7f8_f9f9ffffに+1すると、124xx: 0xf6f6f7f8_f9fa0000になって、下位の2桁(10の桁、1の桁)が無意味な値になっています。次の演算を行うには0x00の部分を10進数の0を意味する0xf6に戻す必要があります。

戻し方はまずCTZ (Count Trailing Zeros)で下位の連続している0のビット数を取得します。先の例ですと、下位24ビットが0xfa0000 = 0b1111_1010_0000_0000_0000_0000ですので、17ビットです。Nとします。


0xf6をセットすべきバイト範囲の計算

CTZビット数Nを8の倍数に切り下げ(= 16ビット)て、mask = (1 << N) - 1とし、下位16ビットに1がセットされた(= 0x00000000_0000ffff)マスクを作成します。その後(元の数値) |= 0xf6f6f6f6_f6f6f6f6 & maskを計算して、下位16ビットに0xf6をセットします。


下位の連続した0x00のバイトのみに0xf6をセット

これで桁の繰り上げ処理は完了です。ループ処理は一切不要。アルゴリズムのナイスポイントその2です。

CTZにループが必要では?と思われるかもしれませんが、世の中には素敵なアルゴリズムがあってループなしで計算可能です。また現代のCPUはCTZ専用命令を持っていることが多く、基本命令の組み合わせより高速に処理できることが多いです。

文字列への変換

桁の繰り上がり処理の素晴らしさが伝わったところで、文字列への変換を紹介します。といっても極めて単純で高速です。0xc6c6c6c6_c6c6c6c6を減算するだけです。

一見すると意味不明ですが、ASCIIコードを考えるとわかると思います。0を表すバイト表現は0xf6でした。0xc6を引くと0xf6 - 0xc6 = 0x30になります。'0'はASCIIコードで0x30です。それだけで文字の'0'に変換できてしまいます。0以外の数値はどうなるかというと、

  • 0: 0xf6 - 0xc6 = 0x30 = '0'
  • 1: 0xf7 - 0xc6 = 0x31 = '1'
  • 2: 0xf8 - 0xc6 = 0x32 = '2'
  • ...
  • 9: 0xff - 0xc6 = 0x39 = '9'

となります。他の位置のバイトも同様で、減算1回で8桁を8文字に変換できます。このアルゴリズムのナイスポイントその3です。

アルゴリズムとは関係ないですが、文字列をメモリに書くときはエンディアンに注意です。x86系CPUはリトルエンディアンなので、文字列に変換した64bit変数(0x30303132_33343938)をそのままメモリに書くと順序が逆転し、メモリには0x38 0x39 0x34 0x33 0x32 0x31 0x00 0x00、つまり"89432100"になります。

本当は"00123498"と書いてほしいので、メモリに書く前にビッグエンディアンに変換すれば良いです。この処理はバイトスワップと呼ばれたりします。これもループ不要の処理で、現代のCPUだと専用命令を持っている場合もあります。

以上がオフセット0xf6アルゴリズム(仮)のナイスポイントの紹介でした。いやあ、良く考え付いたなこれ。感心しました。

エレガントで速い

遅くなる要素は見当たりませんが、最後に測定しましょう。

オフセット0xf6のFizzBuzzの速度
# https://github.com/katsuster/fizzbuzz/blob/main/fizzbuzz2.c

33.3GiB 0:00:09 [3.39GiB/s] [         <=>                                      ]

real    0m9.824s
user    0m7.447s
sys     0m5.064s

約45倍まで速くなりました。素晴らしいです。

参考までに、前回私が作成した9桁10桁狙い撃ちの力業アルゴリズム(約42倍)はこのくらい。

9桁10桁狙い撃ちのFizzBuzzの速度
# https://github.com/katsuster/fizzbuzz/blob/main/fizzbuzz.c

33.3GiB 0:00:10 [3.16GiB/s] [          <=>                                     ]

real    0m10.543s
user    0m8.921s
sys     0m4.067s

ボロ負けというほど差は付いていませんが、コードのエレガントさは大いに差がありましたね。当たり前ですが、リングバッファやvmsplice()のような共通して使える工夫は双方で使いました。ですから純粋にFizzBuzz最適化アルゴリズムの差と言えましょう。

編集者:すずき(2024/01/23 10:51)

コメント一覧

  • hdkさん(2023/09/23 14:56)
    +1だから、繰り上がる時は必ず下のほうに0が固まるんですね。だから、CTZすらなくても、ビット演算と引き算などを組み合わせれば、分岐なしで、0になったところを0xf6で埋められるという... 賢い! Packed (8ビットで2桁分) でも同じ作戦はいけそうですね、ASCIIに変換するのが面倒にはなりますが。
  • すずきさん(2023/09/23 21:14)
    そうなんですよ。賢いなーと思って自分でも実装してみて、日記に書きました。桁の繰り上がりもさることながら、ASCIIへの変換が凄く速いのが特徴ですね。
open/close この記事にコメントする



2023年9月23日

FizzBuzzを速くする3(CPUを変えてみよう)

目次: ベンチマーク

FizzBuzzの実装は簡単ですが、可能な限り高速に出力しようとするとなかなか面白い遊びになります。前回は高速なアルゴリズムを紹介しましたが、CPUを変えたら傾向がどうなるかも見ておきます。

測定環境は、

  • AMD Ryzen 7 5700X
  • DDR4-3200 32GB x 2
  • Linux kernel 6.4.13 (Debian 6.4.13-1)
  • GCC 13.2.0 (Debian 12.2.0-14)
  • glibc 2.37 (Debian 2.37-7)

では順に測定しましょう。

単純なFizzBuzzの速度
# fizzbuzz_simple.c

33.3GiB 0:01:41 [ 335MiB/s] [ <=>                                              ]

real    1m41.715s
user    1m38.481s
sys     0m31.222s
printf排除FizzBuzzの速度
# fizzbuzz_myitoa.c

33.3GiB 0:00:22 [1.48GiB/s] [                     <=>                          ]

real    0m22.478s
user    0m14.279s
sys     0m11.151s
9桁10桁狙い撃ちのFizzBuzzの速度
# fizzbuzz_9_10.c

33.3GiB 0:00:08 [4.12GiB/s] [        <=>                                       ]

real    0m8.080s
user    0m3.138s
sys     0m12.550s
vmspliceを使ったFizzBuzzの速度
# fizzbuzz_vmsplice.c

33.3GiB 0:00:03 [10.6GiB/s] [   <=>                                            ]

real    0m3.159s
user    0m2.828s
sys     0m1.654s

基本的にPentiumで有効な高速化手法はRyzenでも有効ですが、効き目という観点で見ると違いがあります。Ryzenの場合、自作アルゴリズムの要である9桁10桁の狙い撃ちがあまり効かないようです。

オフセット0xf6アルゴリズム(仮)も測定しましょう。昨日のコードから少し変更しているのでPentiumでも測りなおします。

オフセット0xf6のFizzBuzzの速度(Pentium J4205)
# https://github.com/katsuster/fizzbuzz/blob/main/fizzbuzz2.c

33.3GiB 0:00:09 [3.40GiB/s] [         <=>                                      ]

real    0m9.789s
user    0m7.660s
sys     0m4.847s
オフセット0xf6のFizzBuzzの速度(Ryzen 7 5700X)
# https://github.com/katsuster/fizzbuzz/blob/main/fizzbuzz2.c

33.3GiB 0:00:02 [15.3GiB/s] [  <=>                                             ]

real    0m2.184s
user    0m1.827s
sys     0m1.422s

自作アルゴリズムとオフセット0xf6アルゴリズム(仮)を比べると、Pentium J4205の場合はさほど差はありませんでしたが、Ryzen 7の場合は1.5倍程度と大きく差がついています。理由は良くわかりませんが、自作アルゴリズムの方にRyzen 7が苦手とする処理があるのでしょう。

FizzBuzzの種類Pentium J4205の実行時間倍率Ryzen 7の実行時間倍率
単純 7m32.741s- 1m41.715s-
printf排除 1m20.416sx5.6 22.478s x4.5
9桁10桁狙い撃ち25.372s x17.8 8.080s x12.5
vmsplice 10.543s x42.9 3.159s x32.1
オフセット0xf6 9.789s x46.2 2.184s x46.5

まとめるとこんな感じです。最初(2023年9月21日の日記)にレギュレーションのところで説明したように、1から2^32-2まで(約42億回)FizzBuzzしているのですが、たった2秒で終わってしまいます。Ryzen速いですね……。

編集者:すずき(2024/01/23 10:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報