コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2017年12月10日 >>> 2017年11月27日
link もっと後

2017年12月10日

マウスの調子が良くない

半年くらい前(2017年5月27日の日記参照)に買ったエレコムのマウスですが、マウスを動かしてもポインタが動かない時があり、動きが悪くなってしまいました。

症状としては透明なテーブルの上でマウスを動かしたときのように、マウスを動かしてもポインタは左右に小刻みに震えるだけ、という症状です。

常にではなく、たまにこの症状が出ます。買った当時は全く出ていませんでした。

電池を替えても、レーザー出力口を掃除しても、マウスパッド代わりのコピー用紙を新しいものに交換しても、症状が改善しません。

これ以上、原因が思いつかないので諦めて一時引退させました。壊れたわけじゃ無いので、捨ててはいません。

代打

エレコムの代わりに買ったのはLogicool M705です。ジョーシンで3,000円くらいでした。

特に不満は無いのですが、エレコムのマウスに比べたら小さいせいなのか、コピー用紙の上だと滑りが良すぎるせいなのか、右手と肩に変な力が入ってしまい、使っていると疲れて肩が痛くなってきます。

会社で使っている安物オプティカルマウスもM705と同じくらいのサイズのはずなのに、会社では肩が痛くならず、家だと肩が痛くなるのはなぜでしょう……?

もしかしてマウスじゃなくて、マウスパッドを買った方が良いのかなあ??

編集者:すずき(2017/12/15 00:38)

コメント一覧

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



2017年12月7日

Raspberry Pi 3とUART

今までRaspberry Pi 3にsshが繋がらなくなった時に、HDMIケーブルを繋いで画面を映していました。しかしディスプレイのHDMI端子は大抵、背面にあって接続が面倒です。

代わりにUSBシリアル変換ケーブルを買いました。PC側はUSB端子、Raspberry Pi側はGPIOピンヘッダに挿すだけで済みます。

うまく動いたのは良かったのですが、RasPiのピンヘッダがポッキリ折れそうで怖いです。この状態で常用するのは危ない気がしますね。世の中の人はどうしてるのかしら…??

編集者:すずき(2017/12/15 00:15)

コメント一覧

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



2017年12月6日

SHA-3

SHA-3に応募されたハッシュ関数の一覧です。SHA-3 2nd roundは下記のハッシュ関数が評価されました。(NIST IR 7764)

  • BLAKE
  • Blue Midnight Wish
  • CubeHash
  • ECHO
  • Fugue
  • Grøstl
  • Hamsi
  • JH
  • Keccak
  • Luffa
  • Shabal
  • SHAvite-3
  • SIMD
  • Skein

候補が絞られて、最終のSHA-3 3rd roundでは下記5つのハッシュ関数が評価されています。(NIST IR 7896)

  • BLAKE
  • Grøstl
  • JH
  • Keccak
  • Skein

最終的にSHA-3に選ばれたのはKeccakです。

メモ: 技術系の話はFacebookから転記しておくことにした。

編集者:すずき(2017/12/15 00:06)

コメント一覧

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



2017年12月3日

ハッシュ関数とSIMD演算

CubeHashはSIMD演算が非常に有効なアルゴリズムでしたが、他のハッシュ関数をざっと見た感じSIMD演算にできる箇所があまりなく、速くならなさそうです。

今回のようにSIMDでハッシュ計算の速度を4倍にする方向に頑張るのはアルゴリズムに大きく依存するので、応用が効きません。

ハッシュ検索がお互いに独立していることを利用し、SIMDのレジスタにA, B, C, Dの4つのハッシュを入れて、4つのハッシュを同時に演算する方が応用範囲が広いです。

しかしこの方法も万能では無いです。

問題その1は、ワーク領域が16で済むアルゴリズムは無いので、明らかにx64の16レジスタではレジスタ数が足りません。L1キャッシュ頑張れ。

問題その2は、ワーク領域の内容を条件とする、条件分岐処理がほぼ不可能になることです。例えばSIMDレジスタにA, B, C, Dの4つのハッシュを入れて、4つ同時に計算しているとしましょう。こんな処理がアルゴリズムに入っていたとき、


if (w == 0)
    w++;
else
    w--;

SIMD命令をどう書くのが正解でしょうか?デクリメント?インクリメント?

わかりやすくするため、A, B, Dのワーク領域は0以外、Cのワーク領域だけが0だったとします。

もしデクリメント命令を書けばA, B, Dは正しいですが、Cの結果はおかしくなります。逆にインクリメント命令を書けばCは正しいですが、A, B, Dの結果はおかしくなります。従って「記述は不可能」が答えです。

もしSIMD演算命令に一部フィールドだけ(例えばCだけ、とか)演算するような特殊な命令があれば話は変わりますが、通常、分岐の実装は不可能です。

ちなみに、検索していたら、SIMDによる並列演算に成功されている方がいました。CubeHashはもちろんkeccak, BLAKE2, skeinはAVX2で大層速くなるそうです。しかも半年ほど前に実装までされていました。すっごいなこの人…!

モナコイン界隈で有名な人らしくてASK Monaという掲示板でccminer(CUDAを使ったマイナー)を速くしたり、sgminer(OpenCLを使ったマイナー)を速くしている方のようです。

メモ: 技術系の話はFacebookから転記しておくことにした。

編集者:すずき(2017/12/15 00:00)

コメント一覧

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



2017年12月1日

ARMでCubeHash

先日(2017年11月30日の日記参照)CPUによるモナコインというかLyra2REv2の計算で、ボトルネックとなっていたCubeHashをSSE化してみました。今回はARMでチャレンジしてみます。

Raspberry Pi 3(ARM Cortex A53/1.2GHz x 4)でCPUマイナーを実行してみるとたったの8kH/sしか出ません。4コア並列で動作させると32kH/sとなり、きっちり4倍になるのは素晴らしい(※)ですが、x86_64 CPUの1コアにも敵わないです。

NEONにもIntrinsicsがあることを知ったので、不親切なNEON命令のマニュアルと戦いながら、CubeHashをNEON化してみたところ、10kH/sほどになりました。

NEONを使ったCubeHashの素朴な実装

#if defined(__ARM_NEON__)
#  include <arm_neon.h>
#endif

//...

#define NEON_ROTL(x, n) do { \
		uint32x4_t mw0, mw1; \
		mw0 = vshlq_n_u32((x), (n)); \
		mw1 = vshrq_n_u32((x), 32 - (n)); \
		x = vorrq_u32(mw0, mw1); \
	} while (0);

#define NEON_SWP(a, b) do { \
		uint32x4_t mw; \
		mw = b; \
		b = a; \
		a = mw; \
	} while (0);

#define NEON_STEP5(x) do { \
		uint64x2_t mw; \
		mw = vreinterpretq_u64_u32((x)); \
		mw = vextq_u64(mw, mw, 1); \
		x = vreinterpretq_u32_u64(mw); \
	} while (0);

#define ROUND_ONE_NEON    do { \
		mxg = vaddq_u32(mx0, mxg); \
		mxk = vaddq_u32(mx4, mxk); \
		mxo = vaddq_u32(mx8, mxo); \
		mxs = vaddq_u32(mxc, mxs); \
		NEON_ROTL(mx0, 7); \
		NEON_ROTL(mx4, 7); \
		NEON_ROTL(mx8, 7); \
		NEON_ROTL(mxc, 7); \
		NEON_SWP(mx0, mx8); \
		NEON_SWP(mx4, mxc); \
		mx0 = veorq_u32(mx0, mxg); \
		mx4 = veorq_u32(mx4, mxk); \
		mx8 = veorq_u32(mx8, mxo); \
		mxc = veorq_u32(mxc, mxs); \
		NEON_STEP5(mxg); \
		NEON_STEP5(mxk); \
		NEON_STEP5(mxo); \
		NEON_STEP5(mxs); \
		mxg = vaddq_u32(mx0, mxg); \
		mxk = vaddq_u32(mx4, mxk); \
		mxo = vaddq_u32(mx8, mxo); \
		mxs = vaddq_u32(mxc, mxs); \
		NEON_ROTL(mx0, 11); \
		NEON_ROTL(mx4, 11); \
		NEON_ROTL(mx8, 11); \
		NEON_ROTL(mxc, 11); \
		NEON_SWP(mx0, mx4); \
		NEON_SWP(mx8, mxc); \
		mx0 = veorq_u32(mx0, mxg); \
		mx4 = veorq_u32(mx4, mxk); \
		mx8 = veorq_u32(mx8, mxo); \
		mxc = veorq_u32(mxc, mxs); \
		mxg = vrev64q_u32(mxg); \
		mxk = vrev64q_u32(mxk); \
		mxo = vrev64q_u32(mxo); \
		mxs = vrev64q_u32(mxs); \
	} while (0)

#define SIXTEEN_ROUNDS_NEON   do { \
		int j; \
		uint32x4_t mx0, mx4, mx8, mxc; \
		uint32x4_t mxg, mxk, mxo, mxs; \
		mx0 = vld1q_u32((void *)&x0); \
		mx4 = vld1q_u32((void *)&x4); \
		mx8 = vld1q_u32((void *)&x8); \
		mxc = vld1q_u32((void *)&xc); \
		mxg = vld1q_u32((void *)&xg); \
		mxk = vld1q_u32((void *)&xk); \
		mxo = vld1q_u32((void *)&xo); \
		mxs = vld1q_u32((void *)&xs); \
		for (j = 0; j < 16; j ++) { \
			ROUND_ONE_NEON; \
		} \
		vst1q_u32(&x0, mx0); \
		vst1q_u32(&x4, mx4); \
		vst1q_u32(&x8, mx8); \
		vst1q_u32(&xc, mxc); \
		vst1q_u32(&xg, mxg); \
		vst1q_u32(&xk, mxk); \
		vst1q_u32(&xo, mxo); \
		vst1q_u32(&xs, mxs); \
	} while (0)

//...

#if defined(__ARM_NEON__)
#  define ROUND_ONE    ROUND_ONE_NEON
#  define SIXTEEN_ROUNDS    SIXTEEN_ROUNDS_NEON
#else
#  define ROUND_ONE    ROUND_ONE_SLOW
#  define SIXTEEN_ROUNDS    SIXTEEN_ROUNDS_SLOW
#endif

前回と同様にcpuminer-multiのマクロに無理矢理はめ込んで実装しています。NEONを触るのは初めてで、非効率的な書き方になっているかもしれません。お気づきの点があれば教えてくださいませ。

(※)AMD A10-7600は昨日書いた通り1コア145kH/sですが、4コア並列だと145 x 4 = 580kH/sとはならず、少し効率が落ち490〜500kH/sほどになります。

コンパイラの本気はどこ行った

前回SSE化したときは1ラウンドの処理だけ書き換えれば事足りましたが、今回NEON化したときは16ラウンドのループも書き換える必要がありました。

何故かというとx64と違ってarmhfの場合、コンパイラがあまり良い結果を出力してくれないからです。gcc-7.2 x64の場合、

  • load
  • add
  • xor
  • store

このような処理をループさせても、生成されたバイナリの逆アセンブルを見ると、

  • load
  • add
  • xor
  • ※に戻る
  • store

以上のようにload/storeの無駄を検知してループ「外」に追い出してくれました。しかしgcc-4.9 armhfの場合、ループ「内」にload/storeが残ってしまい、かなり遅くなります。

原因としてgccのバージョンが古い、アーキテクチャの最適化がこなれてない、NEONのIntrinsicsを使うと最適化が制限される、などいくつか考えられますが、今のところ分かりません。gcc-7にしたらコンパイラが賢くやってくれるようになれば一番楽ですけどね……。

編集者:すずき(2021/05/14 22:57)

コメント一覧

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



2017年11月30日

モナコインとCubeHash

先日(2017年11月24日の日記参照)CPUによるモナコインのマイニングcpuminer-multiについて調べました。先日の成果としては、

  • CubeHashというハッシュ関数がとびきり時間が掛かっている
  • cpuminer-multiは既に手動で最適化されている
  • CubeHashを素朴に実装したら遅い
  • 素朴な実装でもコンパイラの最適化でcpuminer-multiの実装と同等の速度が出る

CubeHashを適当にSSE化して遊んでいたところ、基本的には非常に遅く(改変前80kH/s、改変後30〜60kH/s)なりますが、突然100kH/sに速くなるポイントがありました。なお、我が家のマシンはAMD A10-7800/3.5GHzです。

コンパイラの本気

急激に速くなった理由はおそらくコンパイラです。

途中までしかSSE化していないはずなのに、逆アセンブラで見ると1ラウンドが全てベクタ演算命令で記述されていること、また、コンパイラの最適化レベルを変えずに(Ofast)、ベクタ最適化だけ無効にすると、速度が67kH/sに落ちることから、

  • 私が中途半端にSSEを使った
  • 変数間の依存性か何かが途切れた
  • コンパイラが残りの部分を全部ベクタ化できると判断
  • 1ラウンド全てSSE or AVX化された

このようなメカニズムだろうと思っています。

平たく言えばコンパイラが本気出していなかっただけですね。1ラウンドを全てベクタ演算化すると、なんと120kH/s も速度が出ました。

元のコードの1.5倍の速度を拝めるとは思ってもいませんでした。何でもやってみるものですね!

Intel Intrinsics

SSE化にはIntel Intrinsics(マニュアル)を使いました、というより、Intrinsicが無かったらSSE化をしようと思わないです。

Intrinsicはかなり強引ですけど、一応Cの関数として定義されており、人間が考えると面倒なこと(SSEレジスタ割り当て、退避など)は全てコンパイラがやってくれるため、大変便利です。

インラインアセンブラの一種とも言えますが、gccのインラインアセンブラほど苦痛はありません。SSE/AVXを使いたいだけならIntrinsicがおススメです。

手で頑張ってみよう

最初CubeHashのSTEP5(キューブの上面と下面の入れ替え操作)をシフトとORで計算していたのですが、コンパイラが出す命令を見ていたらshuffleという素敵な命令を使っていたので、そっちで書き直してみました。

コンパイラ任せでも良いのですが、せっかく途中まで書いたので、全部SSE化しました。BeforeとAfterはこんな感じです。

SSE2を使ったCubeHashの素朴な実装

#define SSE_ROTL(x, n) do { \
		__m128i mw0, mw1; \
		mw0 = _mm_slli_epi32((x), (n)); \
		mw1 = _mm_srli_epi32((x), 32 - (n)); \
		x = _mm_or_si128(mw0, mw1); \
	} while (0);

#define SSE_SWP(a, b) do { \
		__m128i mw; \
		mw = b; \
		b = a; \
		a = mw; \
	} while (0);

#define ROUND_ONE    do { \
		__m128i mx0, mx4, mx8, mxc; \
		__m128i mxg, mxk, mxo, mxs; \
		mx0 = _mm_load_si128((void *)&x0); \
		mx4 = _mm_load_si128((void *)&x4); \
		mx8 = _mm_load_si128((void *)&x8); \
		mxc = _mm_load_si128((void *)&xc); \
		mxg = _mm_load_si128((void *)&xg); \
		mxk = _mm_load_si128((void *)&xk); \
		mxo = _mm_load_si128((void *)&xo); \
		mxs = _mm_load_si128((void *)&xs); \
		/* STEP1 */ \
		mxg = _mm_add_epi32(mx0, mxg); \
		mxk = _mm_add_epi32(mx4, mxk); \
		mxo = _mm_add_epi32(mx8, mxo); \
		mxs = _mm_add_epi32(mxc, mxs); \
		/* STEP2 */ \
		SSE_ROTL(mx0, 7); \
		SSE_ROTL(mx4, 7); \
		SSE_ROTL(mx8, 7); \
		SSE_ROTL(mxc, 7); \
		/* STEP3 */ \
		SSE_SWP(mx0, mx8); \
		SSE_SWP(mx4, mxc); \
		/* STEP4 */ \
		mx0 = _mm_xor_si128(mx0, mxg); \
		mx4 = _mm_xor_si128(mx4, mxk); \
		mx8 = _mm_xor_si128(mx8, mxo); \
		mxc = _mm_xor_si128(mxc, mxs); \
		/* STEP5 */ \
		mxg = _mm_shuffle_epi32(mxg, 0x4e); \
		mxk = _mm_shuffle_epi32(mxk, 0x4e); \
		mxo = _mm_shuffle_epi32(mxo, 0x4e); \
		mxs = _mm_shuffle_epi32(mxs, 0x4e); \
		/* STEP6 */ \
		mxg = _mm_add_epi32(mx0, mxg); \
		mxk = _mm_add_epi32(mx4, mxk); \
		mxo = _mm_add_epi32(mx8, mxo); \
		mxs = _mm_add_epi32(mxc, mxs); \
		/* STEP7 */ \
		SSE_ROTL(mx0, 11); \
		SSE_ROTL(mx4, 11); \
		SSE_ROTL(mx8, 11); \
		SSE_ROTL(mxc, 11); \
		/* STEP8 */ \
		SSE_SWP(mx0, mx4); \
		SSE_SWP(mx8, mxc); \
		/* STEP9 */ \
		mx0 = _mm_xor_si128(mx0, mxg); \
		mx4 = _mm_xor_si128(mx4, mxk); \
		mx8 = _mm_xor_si128(mx8, mxo); \
		mxc = _mm_xor_si128(mxc, mxs); \
		/* STEP10 */ \
		mxg = _mm_shuffle_epi32(mxg, 0xb1); \
		mxk = _mm_shuffle_epi32(mxk, 0xb1); \
		mxo = _mm_shuffle_epi32(mxo, 0xb1); \
		mxs = _mm_shuffle_epi32(mxs, 0xb1); \
		_mm_store_si128((void *)&x0, mx0); \
		_mm_store_si128((void *)&x4, mx4); \
		_mm_store_si128((void *)&x8, mx8); \
		_mm_store_si128((void *)&xc, mxc); \
		_mm_store_si128((void *)&xg, mxg); \
		_mm_store_si128((void *)&xk, mxk); \
		_mm_store_si128((void *)&xo, mxo); \
		_mm_store_si128((void *)&xs, mxs); \
	} while (0)

前回と同様にcpuminer-multiのマクロにはめ込めるように実装しています。

実行例
$ ./cpuminer -a lyra2rev2 -t 1 --benchmark
** cpuminer-multi 1.3.3 by tpruvot@github **
BTC donation address: 1FhDPLPpw18X4srecguG3MxJYe4a1JsZnd (tpruvot)

[2017-12-01 02:21:05] 1 miner threads started, using 'lyra2rev2' algorithm.
[2017-12-01 02:21:06] CPU #0: 140.04 kH/s
[2017-12-01 02:21:06] Total: 140.04 kH/s
[2017-12-01 02:21:10] Total: 145.47 kH/s
[2017-12-01 02:21:15] CPU #0: 145.32 kH/s
[2017-12-01 02:21:15] Total: 145.32 kH/s

CubeHashの最終160ラウンドは一番のボトルネックだった個所だけあって、改善効果はかなり大きいですね。

編集者:すずき(2017/12/01 02:24)

コメント一覧

  • AVXならこんな感じ?さん(2018/01/23 09:38)
    /* STEP1 */ \
    mxg = _mm256_add_epi32(mx0, mxg); \
    mxo = _mm256_add_epi32(mx8, mxo); \
    /* STEP2 */ \
    AVX_ROTL(mx0, 7); \
    AVX_ROTL(mx8, 7); \
    /* STEP3 */ \
    AVX_SWP(mx0, mx8); \
    /* STEP4 */ \
    mx0 = _mm256_xor_si256(mx0, mxg); \
    mx8 = _mm256_xor_si256(mx8, mxo); \
    /* STEP5 */ \
    mxg = _mm256_permute4x64_epi64(mxg, 0xb1); \
    mxo = _mm256_permute4x64_epi64(mxo, 0xb1); \
    /* STEP6 */ \
    mxg = _mm256_add_epi32(mx0, mxg); \
    mxo = _mm256_add_epi32(mx8, mxo); \
    /* STEP7 */ \
    AVX_ROTL(mx0, 11); \
    AVX_ROTL(mx8, 11); \
    /* STEP8 */ \
    mx0 = _mm256_permute4x64_epi64(mx0, 0x4e); \
    mx8 = _mm256_permute4x64_epi64(mx8, 0x4e); \
    /* STEP9 */ \
    mx0 = _mm256_xor_si256(mx0, mxg); \
    mx8 = _mm256_xor_si256(mx8, mxo); \
    /* STEP10 */ \
    mxg = _mm256_shuffle_epi32(mxg, 0xb1); \
    mxo = _mm256_shuffle_epi32(mxo, 0xb1); \
  • すずきさん(2018/01/24 14:40)
    コメントありがとうございます。そのようになると思います。
    私の実装は下記のような感じです。STEP5, 8 が多少違うくらいですね。

    mxg = _mm256_add_epi32(mx0, mxg); \
    mxo = _mm256_add_epi32(mx8, mxo); \
    AVX_ROTL(mx0, 7); \
    AVX_ROTL(mx8, 7); \
    AVX_SWP(mx0, mx8); \
    mx0 = _mm256_xor_si256(mx0, mxg); \
    mx8 = _mm256_xor_si256(mx8, mxo); \
    mxg = _mm256_shuffle_epi32(mxg, 0x4e); \
    mxo = _mm256_shuffle_epi32(mxo, 0x4e); \
    mxg = _mm256_add_epi32(mx0, mxg); \
    mxo = _mm256_add_epi32(mx8, mxo); \
    AVX_ROTL(mx0, 11); \
    AVX_ROTL(mx8, 11); \
    mx0 = _mm256_permute2x128_si256(mx0, mx0, 0x01); \
    mx8 = _mm256_permute2x128_si256(mx8, mx8, 0x01); \
    mx0 = _mm256_xor_si256(mx0, mxg); \
    mx8 = _mm256_xor_si256(mx8, mxo); \
    mxg = _mm256_shuffle_epi32(mxg, 0xb1); \
    mxo = _mm256_shuffle_epi32(mxo, 0xb1);

    残念ながら AMD A10 は AVX2 に対応していないので、SSE2 との速度が比較できませんが…。
open/close この記事にコメントする



link もっと前
2017年12月10日 >>> 2017年11月27日
link もっと後

管理用メニュー

link 記事を新規作成

<2017>
<<<12>>>
-----12
3456789
10111213141516
17181920212223
24252627282930
31------

最近のコメント5件

  • link 21年9月20日
    すずきさん (11/19 01:04)
    「It was my pleasure.」
  • link 21年9月20日
    whtさん (11/17 23:41)
    「This blog solves my ...」
  • link 24年10月1日
    すずきさん (10/06 03:41)
    「xrdpで十分動作しているので、Wayl...」
  • link 24年10月1日
    hdkさん (10/03 19:05)
    「GNOMEをお使いでしたら今はWayla...」
  • link 24年10月1日
    すずきさん (10/03 10:12)
    「私は逆にVNCサーバーに繋ぐ使い方をした...」

最近の記事3件

  • link 23年4月10日
    すずき (11/15 23:48)
    「[Linux - まとめリンク] 目次: Linux関係の深いまとめリンク。目次: RISC-V目次: ROCK64/ROCK...」
  • link 24年11月6日
    すずき (11/15 23:47)
    「[Ubuntu 24.04 LTS on ThinkPad X1 Carbon Gen 12] 目次: Linux会社ではTh...」
  • link 24年11月11日
    すずき (11/15 23:26)
    「[Pythonのテストフレームワーク] 目次: Python最近Pythonを触ることが増えたのでテストについて調べようと思い...」
link もっとみる

こんてんつ

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 サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 11/19 01:04