SharingJavafalse-sharingテックタッチアドベントカレンダー14日目を担当する@ihirokyです。何気なく眺めているプロダクトがなぜこんなデザインになっているのかが垣間見れて「あーなるほど」と納得してしまいました。自分は何となくに頼りがちな部分がありますが、デザインするにあたりしっかり考えた裏付けをもちプロダクト開発に当たっていきたいです。テックタッチエンジニア勢の記事はTypeScript/Goが大勢を占めていますが、ネタが無いので心機一転Javaを使ったハードウェアを考えるお話です。6日目の話は忘れました。氏の名言「漏れのある抽象化の法則」というものがあります1。いろいろな物事は抽象化され、細かいことを気にしなくても扱い安いレベルで機能が提供されています。その抽象化の内側にいるなにかが特殊な振る舞いをしたとき、一見理解しがたい現象が発生します。これに対処するには結局抽象化がどのように機能し、何を抽象化しているのかを学ぶ必要があります。抽象化は作業時間を短縮するが学ぶ時間までは短縮してくれません。つまるところ、問題を解決できるエンジニアはすべてを勉強する必要がある、というお話です。通して高級なプログラミング言語の上にのっても結局ハードウェアのことを理解していなければその性能はいかせない例を見てみたいと思います。した場合の二通り呼び出しています。AtomicLongArrayの長さと、FalseSharingArrayクラスがAtomicLongArrayのどこを参照するかを指定するindexに影響しています。}つまり、FalseSharingArrayがAtomicLongArrayを隙間無く(普通に)使うか、8個おきに使うかの指定です。7299ms.となり、widthを8にした場合、widthを1にした場合と比べて3倍以上速くなっています。widthが1か8かに関係なくプログラム上配列要素の共有による競合はありません。間をあけただけなのこの差は不思議ですよね。私は不思議でなりませんでした。こんなイメージ:何が漏れている?この現象は、ハードウェアレベルでみつめ直すとメモリの競合が起きていることが分かります。以下はCPUがメモリ上のデータを使う様子を模式的に表しています:CPUはデータを扱う際に、メモリより高速なキャッシュ上にデータを一時的にロードします。メモリ上のデータをキャッシュに載せる際に、キャッシュラインを単位としてデータをキャッシュ読み込んだりメモリに書き込んだりしています。キャッシュラインのサイズはCPUによりいろいろですが、普段触るx64系は大抵64バイトです。キャッシュラインサイズ分まとめてメモリに対してデータの読み書きが行われます。このため、プログラム上では複数のスレッドによる要素の共有はなくとも、その要素がキャッシュライン上に共存していれば複数のCPUからみると変数の共有が行われている状態になります。ここで、同一キャッシュライン上に存在する二つの数値を2つのCPUがそれぞれキャッシュに読み込んでいる状態で片方がその数値を更新した場合、CPU間で整合性をとる取るためにもう片方はキャッシュの読み込みなおしが必要になる、更に更新が起きた場合は...となると、競合が発生します。これが頻発すると、上記プログラムのように顕著な差となって現れます。Sharing」と言います。学びはエンジニアの基礎体力このようにハードウェアがどうなっているかを理解していないとプログラムの動きを予測することは難しいです。遠回りに見えても基本的な計算機の仕組みを含めた基本的な事項、流行りの技術の裏側もしっかり学んでおきたいです。それゆえ工学系出身のエンジニアを非常に羨ましく思う今日この頃。今回のネタは通常の配列を用いると(通常の方法では)発生しません。キャッシュの一貫性を保つ処理を挟み込まないからです。配列の各要素に対してvolatile相当の処理もできるようになっているところがミソです。