UseCaseの再利用性
Clean ArchitectureにはUseCase層が定義されていますが、このUseCaseが一体どういうものなのか度々わからなくなるので、自分の考えをまとめてみるエントリです。
Clean Architectureについてはこちら
日本語訳:クリーンアーキテクチャ(The Clean Architecture翻訳)
以降、概念を”ユースケース”、実装されるモノを”UseCase”と表記することにします。 (同じっちゃ同じなんですが、指してるものがところどころ変わるので表記分けをしています。) また、Webアプリケーションを想定しています。
ユースケースとは何なのか
続きを読むドメインイベントを設計する
第3回Reactive System Meetup in 西新宿のLTで発表をしてきました。
reactive-shinjuku.connpass.com
LTという都合上、含めたかったけれど泣く泣く削ったボツネタも併せて補足するエントリです。 (例によって長いです。)
続きを読むマイクロサービスとDDDをGo言語とScala+Akkaで比較したらEventSourcingの話にもなって面白かったまとめ
JSでもオブジェクト指向ライクに書きたくなるので覚書
JSでオブジェクト指向ライクに書こうと思ったときにES6もBabelも使えない環境向けの覚書です。
ここではクラスのようにデータと振る舞いをまとめられる程度のところを到達点としています。(オブジェクト指向とは云々などは本旨から外れるので"ライク"という表現に留めておきます。)
参考書籍は「オブジェクト指向JavaScriptの原則」です。この本はC#やJavaなどの経験はあってJavaScriptへの取っ掛かりが欲しい人にオススメです。
なお、私はJavaScriptに関しては初心者同然です...
TypeScriptとかおいしそうですね...
基本のカタ
function Foo(name) { // コンストラクタ this.name = name; // データ定義(Public) } Foo.prototype = { constructor: Foo, bar: function() { // メソッド定義 console.log('foo.bar'); }, toString: function () { // オーバーライド console.log(this.name); } };
まず、 function Foo
のようにコンストラクタを定義します。
先頭1文字が大文字だとコンストラクタであることが期待されるようです。このとき、 var foo = new Foo('foo');
のようにnewを使ってオブジェクトを生成することができます。
このコンストラクタの中に this.name = ...
のようにPublicなデータを定義することができます。
メソッドもここに定義することができますが、そうすると全てのインスタンスで関数オブジェクト(?)を持つことになり非効率です。
そこで、 Foo.prototype
に対してメソッド(関数オブジェクト)の集まりを設定することで、同じ振る舞いを持ったオブジェクトを生成できるようにします。
JavaScriptでは、すべての関数はオブジェクトなので、どこかでprototypeに対してメソッドを追加したり、変更したりできてしまいます。
オーバーライドについても、同名であるだけで上書きがされます。
この辺りはプロトタイプチェーンであることの特性なので、気をつけて運用しましょう。
constructor: Foo
のようにわざわざコンストラクタはこれだと明示していますが、これを行わない場合constructorがObjectになってしまいます。
これはprototypeに対して {...}
の形で丸ごと設定しているため、本来のconstructorが失われてしまうことが原因です。
Foo.prototype.bar = function(){...}
のように一つ一つ設定するか、constructorを忘れずに明示するようにする必要があります。
その他、書籍では Object.defineProperty
を使った方法や即時関数のなかでオブジェクトを生成する方法なども紹介されていますが、ここでは割愛します。
プライベートメンバ
プライベートな変数を持ちたいような場合、上記のコンストラクタパターンでは、以下の様な方法で定義することができます。
function Person() { var age = 25; this.getAge = function() { return age; }; this.growOlder = function() { age++; }; }
ただし、この方法では、そのプライベートメンバへ参照するために内部にメソッドなどを定義する必要があり、前述のようにprototypeではなくインスタンスに配置されるため非効率ではあります。 しかし、プライベートなメンバを定義するには、今のところこの方法しか無いようです。 (全てのインスタンスで共有されても良い場合は、即時関数のなかでprototypeにメソッドを定義するなどすることは可能。)
継承
オブジェクト継承という方法もありますが、ここではコンストラクタ継承を取り上げたいと思います。 コンストラクタ継承では、継承元のprototypeとObject.createを使ってプロトタイプチェーンのつながりを実現します。
function Rectangle(length, width) { this.length = length; this.width = width; } Rectangle.prototype.getArea = function() { return this.length * this.width; }; function Square(size) { this.length = size; this.width = size; } Square.prototype = Object.create(Rectangle.prototype, { constructor: { configurable: true, enumerable: true, value: Square, writable: true } });
mixin
書籍では継承の方法から派生してmixinに言及されています。 mixinは強力ではありますが、そこに継承のように参照関係がつくられないため、比較的規模の小さい機能での使用に適していると書籍では述べられています。 多くの機能を追加したい場合や、機能の提供源を把握しておきたい場合は継承を使うことが推奨されるようです。
function mixin(receiver, supplier) { if (Object.getOwnPropertyDescriptor) { // ES5以降 Object.keys(supplier).forEach(function (property) { var descriptor = Object.getOwnPropertyDescriptor(supplier, property); Object.defineProperty(receiver, property, descriptor); }); } else { for (var property in supplier) { if (supplier.hasOwnProperty(property)) { receiver[property] = supplier[property]; } } } return receiver; }
これは以下のように使うことができます。
function Foo() { // ... } function Bar() { // ... } // プロトタイプへのmixin mixin(Foo.prototype, Bar.prototype); // インスタンスへのmixin var foo = new Foo(); mixin(foo, Bar.prototype);
終わりに
コンパイル、静的型付けチェックが欲しいですね。
せめてJSDocを書きましょう。IDEなどを使っていればコード補完も享受できるようになります。
参考
DDD座談会に際して素振りをしていた
DDD座談会のパネラーとして登壇させていただきました。
素振り
予め参加者の方々からいただいた質問(テーマ)に対して、予行練習というか、ちょっと自分の考えを先にまとめていたのですが、予想通りテーマひとつひとつが盛り上がったので全部を話しきることができませんでした。 折角なのでここに公開しておきます。 ところどころ、座談会終わっての後書きも追記しています。
なお、あくまで私見ということでよろしくお願いします。座談会のまとめ記事というわけではありませんので、悪しからず。
各人のコンテキストで、こんな解釈あるよ等あると思いますので、Twitterでもコメントでもフィードバックいただければ幸いです。
続きを読む
false sharingの整理
マルチスレッド時代における意外なハマりどころfalse sharingについてまとめておきます。 参考にあげている書籍「Javaパフォーマンス」からのまとめです。
false sharingとは
例えば、以下の様なコードがあるとします。
public class DataHolder { public volatile long l1; public volatile long l2; public volatile long l3; public volatile long l4; }
このとき、longは64bit = 8バイトのサイズですが、これらはメモリ上で近接して配置されます。
プログラムがl2を操作しようとすると、ある程度まとまったサイズのメモリが読み込まれます。(※2)
多くの場合、近接するインスタンス変数も操作するため、アクセスはとても高速にでき、パフォーマンスに貢献します。
しかし、複数のスレッド(コア)によって頻繁にアクセスする場合、異なる変数への操作であっても、キャッシュにまとまって載っているため、あるコアでの操作結果を他のコアに通知し、そのコアでは再読み込みを行う必要がでてきます。(volatile変数に書込を行うと、他のすべてのスレッドでキャッシュ上の値が無効化され、メモリから再読み込みが必要)
このように意図せず 競合 干渉(※1)が発生し、パフォーマンスが大幅に劣化することをfalse sharingと呼びます。
※厳密には、false sharingは同期やvolatile変数とは無関係に発生しますが、Javaのメモリモデルではメインメモリへの書き出しは同期プリミティブ(CAS命令やvolatile変数も含む)の末尾でのみ行うと定められているため、最も頻繁に発生するのは同期に関連するケースとなります。
※1(2016.7.7追記)
"競合"というとプログラムが意図しない間違った動作を引き起こし性能劣化以外の影響があるように捉えられる、とご指摘いただいたので"干渉"という表現に変更いたしました。
性能劣化以外の影響をご存知の方がいらっしゃいましたら、教えていただけると幸いです。
@fjnli 競合というと、プログラムが意図しない間違った動作を引き起こすように思えたので性能劣化以外のそういった影響があるのではないかと考えてしまいましたが、OpenMPやIntelのドキュメントを読んでもそのような言及はなかったので、競合という言葉は気にしないことにします
— Akira Takahashi (@cpp_akira) July 7, 2016
(※1追記終)
※2(2016.7.8追記)
キャッシュ ライン の説明があった方が干渉の説明に繋がるとご指摘いただいたので補足します。
下記参考1で解説されている内容を抜粋します。
キャッシュでは,メイン・メモリを数バイトから数十バイトの「ライン(あるいはブロック)」という単位で管理します.具体的には,キャッシュとメイン・メモリの間のデータ転送,キャッシュ上にデータがあるかどうか(ヒット/ミス・ヒット)を,すべてラインの単位で管理します
(※2)の個所は、このキャッシュラインの単位でデータがメモリからキャッシュへ読み込まれることを指しています。
イメージとしては参考2の図がわかりやすいと思います。false sharingは各スレッドが同じ変数への アクセス を実際に共有していないのに、キャッシュラインの単位で共有している状態になっており、お互いのアクセスによってメモリからの再ロードを頻発させます。インテルガイドの日本語訳がありましたので末尾の参考に追記しました。
また、JEP 142の @Contended
アノテーションも指摘されていたのですが、書籍にはこれにも言及があります。ただ、これは「JEPを経てはいますが、このアノテーションはJVM内部での利用を主に想定しています。このアノテーションが今後のリリースで使われ続ける保証も、機能が変わらない保証もありません。」とあるためこの記事では省略していました。
参考1:今さら聞けないマルチプロセッサの基礎教えます ――キャッシュの共有,割り込みの共有,OSによる制御|Tech Village (テックビレッジ) / CQ出版株式会社(キャッシュライン)
参考2:今さら聞けないマルチプロセッサの基礎教えます ――キャッシュの共有,割り込みの共有,OSによる制御|Tech Village (テックビレッジ) / CQ出版株式会社(false sharingの図)
参考3:コンピュータアーキテクチャの話 (6) キャッシュの構造(基礎編) - どういう単位でキャッシュに入れるのか? | マイナビニュース
false sharingの整理 - yoskhdia’s diary https://t.co/L0HnGyoT4B 間違ったこと言ってないけどキャッシュ"ライン"を導入しないことには干渉の説明に繋がりにくい気はする あとJavaでこの手のチューニング事実上できるのか?
— yoh (@yohhoy) 2016年7月7日
(ご指摘いただいてキャッシュラインについて改めて調べたのですが、自分の理解が曖昧だったことが分かりました...勉強になります)
(※2追記終)
対策
明確な対策はありません。
検出のためには、プロセッサのアーキテクチャに関する詳細な知識が必要で、また検出するツールもベンダーによって対応が異なるようです。(IntelだとVTuneというツールを公開している。)
ネイティブなプロファイラでは、指定されたコードについて命令ごとに必要なクロックサイクル数(CPI)を知ることができるため、多くのCPIが費やされているかがfalse sharingが発生している可能性を検知する材料になります。
通常false sharingが発生した場合、コードの変更が必要です。
大まかな方針は以下のとおり。
パディングは後述のとおり、期待する効果が得られるか微妙な面があるため、前者のローカル変数で処理をし、最後に同期処理でまとめてしまう方法をまずは検討した方が良いでしょう。
- データをローカル変数へ移動して、書込は最後に行うようにする。
- パディングを行って複数の変数が同じキャッシュに読み込まれないようにする。
パディング
CPUのキャッシュが128バイトだとしたら、以下のようなコードにします。
public class DataHolder { public volatile long l1; public long[] dummy1 = new long[128 / 8]; public volatile long l2; public long[] dummy2 = new long[128 / 8]; public volatile long l3; public long[] dummy3 = new long[128 / 8]; public volatile long l4; }
ただし、このようにしてしまうと、キャッシュのサイズはCPUごとに異なるため、異なるアーキテクチャへの移行難易度が上がることと、また、パディングした分、インスタンスサイズが増加するため、インスタンスの数によってはGCに悪影響を及ぼします。 さらに、JVMがこれらの変数を配列ごとやlongごとに並べ替えてしまい、期待通りに機能しない可能性もあります。 (その場合、プリミティブ型の変数を使ってパディングすることもできるが、大量の変数が必要になって現実的ではなくなってしまう。)
Actorモデルとfalse sharing
Actorモデル(Akkaを想定)を使う場合、false sharingはほとんど気にする必要はありません。 それは、対策の1点目のようにActorの持つローカル変数は1つのスレッドのみからアクセスすることになるため、意地悪なコードを無理やり書かない限りは競合しなくなるためです。(Actorの内部データは参照されることがなく、Actor間はメッセージによってやり取りされる。)
ただし、Actorそれ自体が同じキャッシュに載ってしまうという可能性は否定することができません。 (たとえ自分がローカル変数を持つようにプログラミングしなくても、Akkaを使っている場合Actorトレイトでローカル変数を持っているため。) しかし、この場合にfalse sharingが何故発生するのかまでは追えていません。 通常ActorRefを通してActorにはアクセスしますが、Actor System内でどのようにActorを管理しているかによって、false sharingが発生し得るのでしょうか。 ご存知の方がいらっしゃいましたら、教えていただけると幸いです。
(※追記) @TanUkkii007 さんが「Akkaはマルチスレッドプログラミングにおける可視性の問題をどう解決しているか」について書いてくれました。
github.com (※追記終)
参考
XLsoft エクセルソフト : インテル ガイド : 3-4 スレッド間のフォールス・シェアリングの回避と特定(2016.7.8追記)
Mechanical Sympathy: False Sharing