Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

文化的まちがいさがし - スレッドセーフ

ある処理がスレッドセーフであるというとき……

  • (A) 複数のスレッドから同時に処理を実行したときと、複数のスレッドが別々で処理を実行したときとで、処理結果が変わらないように設計されていること
  • (B) あるスレッドで処理されている値が、別のスレッドから見えないようになっていること

(A)と(B)は重なる部分もあるんだけど、「スレッドセーフである」というライブラリで(B)にあたる処理を書く(書いてしまう)こともできるから、話をするときには注意したほうがいいですね。

報道機関のドキュメンタリーと社史の関係

なんでこんなことが起こるんだろう?というのを報道機関でなくエンジニアの側から考えたのだが、もしかして「知的財産やそれにまつわる情報の蓄積や引継ぎをちゃんとやってる事業会社」と「ドキュメンタリー」って非常に相性が悪いのでは?

スパコンの開発に伴う各種設計情報などの知的財産の引き継ぎがちゃんと行われている組織なら、事業会社内では知的財産としての管掌部署が現在も設定されているはず。ドキュメンタリーを作りたければまず報道機関から事業会社の広報部署に対して「あのプロジェクトのことを教えてください」というお願いが行くはずで、そうしたら広報部署は「あのプロジェクトには誰が関わっていたのか」よりも「あのプロジェクトの引取り先はどこか」(あのプロジェクトに対して責任を持って回答ができる管掌部署はどこか)という情報を出さないとマズいですよね。そういう聞かれ方をしたときに、管掌部署を差し置いて、単に昔そのプロジェクトに関わっていた人を「この人に聞いて」と紹介してしまったら、何のための管掌部署なの?という話になる。

また事業会社内の業務に対する評価がどうなっているか?もポイントだと思っていて、広報部署としては「外部からの問い合わせに対して、知的財産の管掌部署に正しく繋げることができた」ということが評価につながるしんだろうし、知的財産の管掌部署は「保持している知財に対して正しく対応が行えた、保持している知財を使って自社のPRに正しく寄与できた」ということは担当者の評価に繋げられる。一方で、その管理元部署から離れてしまった人は、稼動を割いて対応してもそのような評価には繋がらないんじゃないかな?

じゃぁ、報道機関側がそれを分かっていてどう情報を取りに行ったらいいだろう?と考える。営業情報の社外公開に関する規則はどういうわけだかクリアできたとして、まず確実に情報が残ってるのはプロジェクトに関わった人間のリストのはず。ISMSがあるから関係者の氏名リストはプロジェクトが終わっても保管されてるんじゃないかな。ただ、ISMSのリストだけでは座組みは分からない。座組みが知りたければプロジェクトの体制図を見る必要があるが、これはプロジェクト終了後に破棄されている可能性があるし、また素人が体制図を見ただけでは誰がキーマンなのかは分からない。というわけで、この方向からもプロジェクトのキーになっていた人はだれか?は外側から見ると非常に掴みにくいのでは。「開発責任者」というのがプロジェクトの体制のどこにいるのかは分からないけど、全体のとりまとめを行ったプロジェクトマネージャではないような言い方に見える(新規技術を大量に採用する大規模なプロジェクトで、技術的な取り纏めを行うエンジニアがプロジェクトマネージャを兼任していたら、とてもじゃないけどプロジェクトが回らないのでは)。

ところで会社によっては「社史」を作るという文化があって、実はこのようなケースがまさに「社史が役にたつケース」なのでは?と思い至った。たとえ後である人が会社を離れたとしても、社史を作った時点で社内にある程度以上の役職で残っていたなら、社史には名前が残るのでは?

JavaVMのスタックのガードページ幅を変更する必要性

luozengbin.github.io

pangin.pro

-XX:StackYellowPagesに指定できる最小値は3(4kbページとして12kb)。

$ java -XX:StackYellowPages=1 Test1
intx StackYellowPages=1 is outside the allowed range [ 3 ... 8 ]
Improperly specified VM option 'StackYellowPages=1'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

ただ、Yellow Pagesを増やす必要のあるケースって何だろう? メソッド呼び出しでスタックの上に積めるのはメソッドの引数とローカル変数だけ(定数プールへの参照は微々たるもの)みたい。

docs.oracle.com

なので、long型ローカル変数を3,000個用意したうえでスタックサイズを調節すれば12kbのページをぶっちぎってRed Pageに到達できるかと思って以下のようなプログラムを作ってみたんだけど、いずれも普通にjava.lang.StackOverflowErrorが発生するんだよなぁ……

以下をPowerShellから138..150 | ForEach-Object { java -Xss"$_"k Test1 }のようにして実行。

// javac -J-Xss4m Test1.java
public class Test1 {
    public static void main(String args[]) {
        stackoverflow(1);
    }

    public static void stackoverflow(int i) {
        System.out.println(i);
        // & { $list = (1..5500); $list | ForEach-Object { "long i${_} = ${_};" }; "System.out.println(0"; $list | ForEach-Object { "+ i$_" }; ");" } | sv hoge; $hoge -join " " | Set-Clipboard
        long i1 = 1; long i2 = 2; long i3 = 3; long i4 = 4; long i5 = 5; long i6 = 6; long i7 = 7; ...
        stackoverflow(i+1);
    }
}
PS C:\home\src> 138..150 | ForEach-Object { "■Xss:$_"; java -Xss"$_"k Test1 }
■Xss:138
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:139
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:140
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:141
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:142
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:143
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:144
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:145
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:146
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:147
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:148
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:149
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)
■Xss:150
1
15127750
Exception in thread "main" java.lang.StackOverflowError
        at Test1.stackoverflow(Test1.java:11)
        at Test1.main(Test1.java:4)

JavaVMの起動オプションについてのメモ

起動オプションの初期値

起動オプションの初期値は何か、最終的にどのような値が指定されたか、そのうちコマンドラインで渡した値は何か、は以下のオプションで表示できる。

  • -XX:+PrintFlagsFinal: 最終的な設定値
  • -XX:+PrintFlagsInitial: 初期値
  • -XX:+PrintCommandLineFlags: コマンドライン上で与えた設定値

java -XX:+PrintFlagsFinal -versionのようにして実行する。Windows TerminalのPowerShell上でシンタックスハイライトの表示がおかしくなるのが気になるならjava -XX:"+PrintFlagsFinal" -versionでもよい。versionオプションを先に指定してjava -version -XX:+PrintFlagsFinalのようにすると表示されないので注意。

コンパイラに渡すJavaVM起動オプション

javac -Jのあとにオプションを指定する。たとえば2コンパイラでStackOverflowErrorが出たらjavac -J-Xss4m Test1.javaなど。

環境ごとの設定オプション

開発時・性能問題調査時・本番環境で指定すべきオプションをわかりやすく指定している記事は以下。検討時のスタート地点として良さそう。

qiita.com

  • 日本語だとあまり説明のない-XX:StackRedPages-XX:StackYellowPages-XX:StackShadowPagesについては以下が分かりやすい。

luozengbin.github.io

pangin.pro

Java 11 or later equlvalent for Java -XX:+PrintGC

Java8の-XX:+PrintGCはJava11以降ではどう変わったのか?は以下にまとまっている。

https://docs.oracle.com/javacomponents/jp/enterprise-performance-pack/epp-user-guide/printing-jvm-information.html

COBOLの小数点演算

qiita.com

そういやCOBOLはどうなんだっけ?ということで確認。 やっぱり除算は鬼門で、できるだけ乗算で処理できるように仕様を決めるのがベターということでしょうね。

IDENTIFICATION DIVISION.
PROGRAM-ID. DECUMALTEST.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 RESULT PIC 9(1)V9(37) VALUE 0.0.

PROCEDURE DIVISION.
  INITIALIZE RESULT.
  COMPUTE RESULT = 0.1 + 0.1 + 0.1.
  DISPLAY "0.1 + 0.1 + 0.1 = "RESULT.
  IF RESULT = 0.3 THEN
    DISPLAY "0.1 + 0.1 + 0.1 IS EQUAL TO 0.3."
  ELSE
    DISPLAY "0.1 + 0.1 + 0.1 IS NOT EQUAL TO 0.3."
  END-IF.

  INITIALIZE RESULT.
  COMPUTE RESULT = 4.8 - 4.7 - 0.1.
  DISPLAY "4.8 - 4.7 - 0.1 = "RESULT.
  IF RESULT = 0.0 THEN
    DISPLAY "4.8 - 4.7 - 0.1 IS EQUAL TO 0.0."
  ELSE
    DISPLAY "4.8 - 4.7 - 0.1 IS NOT EQUAL TO 0.0."
  END-IF.

  INITIALIZE RESULT.
  COMPUTE RESULT = (1.0 / 3.0) * 3.0.
  DISPLAY "(1.0 / 3.0) * 3.0 = "RESULT.
  IF RESULT = 1.0 THEN
    DISPLAY "(1.0 / 3.0) * 3.0 IS EQUAL TO 1.0."
  ELSE
    DISPLAY "(1.0 / 3.0) * 3.0 IS NOT EQUAL TO 1.0."
  END-IF.
STOP RUN.
0.1 + 0.1 + 0.1 = 0.3000000000000000000000000000000000000
0.1 + 0.1 + 0.1 IS EQUAL TO 0.3.
4.8 - 4.7 - 0.1 = 0.0000000000000000000000000000000000000
4.8 - 4.7 - 0.1 IS EQUAL TO 0.0.
(1.0 / 3.0) * 3.0 = 0.9999999999999999999999999999999999999
(1.0 / 3.0) * 3.0 IS NOT EQUAL TO 1.0.