https://github.com/gradle/gradle-profiler gradle の処理がなんだか遅いなってときがある。 サッとググッて出てくる情報だと、--profile つけろとか、--scan 使えとか書いてある。 --profile は情報がざっくりしすぎていてよくわからないし、--scan は gradle.org に情報がアップロードされちゃうのがちょっと。。 `--profile` って結局は、「どのタスクが遅いか」はわかるんだけど、どのプラグインが遅いか、とかはよくわからなかったりするんだよね。 gradle-profiler を使うと、どのプラグインが時間食ってるか、みたいなことをサッとしれて便利。 何回か動かしたベンチマークの結果をレポートにまとめてくれるので、gradle の速度チューニングした結果の評価にも便利。 # benchmark の取り方 ``` $ gradle-profiler --benchmark --project-dir . assemble ``` とかやって動かす。 数回試行された結果が以下のような HTML で生成される。 ![](https://blog-attachments.64p.org/20230922-00105831a59d99-0fec-4423-8c03-ef8a6782aa7c.png) # profiling の仕方 jfr や async-profiler をかけながらgradleを動かせるのも便利すぎる。 なんだか最近gradle遅いな、と思ったらgradle-profilerかけると良いと思う。 ``` gradle-profiler --profile async-profiler --project-dir . assemble ``` async-profiler 以外にも jfr や YouProfiler などを使うことも可能。 以下のような見慣れた flamegraph を得ることができる。 ![](https://blog-attachments.64p.org/20230922-0012141d53c18c-5b07-4a0e-a045-13b2d11e7ed0.png) これをやった結果、spring の dependencyManagement に個別の dependency をいっぱい指定すると遅いから platform project 作ったほうが良さそうだなぁ、とかそういうことがわかったりする。
https://github.com/JetBrains/lets-plot-kotlin/issues/48 epoch millis にして `scaleXDateTime` するのが良さそう
JDK の JIT の様子を眺める方法について考える。 実際には **現代のサーバーサイドエンジニアリングでは JDK の JIT がパフォーマンスイシューの原因になっているケースは極めて少ない** が、JIT がどんなふうなタイミングで発生しているかを把握しておくことは人生にとって有益だと思う。 ログを出すと若干のオーバーヘッドがあると思うけど、JIT のログは起動直後にちょっと出るだけで量も大したことないので気にしなくていいレベルな気がする。 # JFR で見る 王道っぽい。 # JFR Event Streaming を使って収集する Java 14 以後で利用可能な [JEP 349: JFR Event Streaming](https://openjdk.org/jeps/349) を使って micrometer などに出力する。この方法だと、JFR が動く前の JIT イベントが取れない気がする。 https://matsumana.info/blog/2020/09/20/jvm-jit-compilation-metrics/ # Unified Logging に JIT log を出す https://matsumana.info/blog/2020/09/22/jvm-jit-compilation-metrics-with-mtail/ -Xlog:jit+compilation=debug というオプションを指定することで、JIT のログを取れる。Unified JVM Logging 形式なので取り回しがわりとしやすいかも。 ref. https://www.slideshare.net/slideshow/embed_code/key/BLJsjK56iDL0y # -XX:+PrintCompilation -XX:+PrintCompilation をつけると、JIT コンパイルログが stdout に出力される。軽く見るにはいいかも。Unified Logging でとれる今となってはあえてこれを使う理由はないかも。 ref. https://www.slideshare.net/nttdata-tech/jit-compiler-jjugccc-2020-fall-nttdata-sakata?slide=42 # -XX:+LogCompilation -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation つけるとファイルにログが出力される。ファイル名は current directory の hotspot_pid$$.log となる。XML で出力されるので処理しやすいかもしれない。 JitWatch で眺めると楽しい。 ref. https://yuya-hirooka.hatenablog.com/entry/2021/04/30/234227 https://github.com/AdoptOpenJDK/jitwatch/releases/tag/1.4.7 このへんからダウンロードできるので使うとよい。 ``` wget https://github.com/AdoptOpenJDK/jitwatch/releases/download/1.4.7/jitwatch-ui-1.4.7-shaded-mac-m1.jar java -jar jitwatch-ui-1.4.7-shaded-mac-m1.jar ```
Remnant 2 をクリア。難易度はサバイバー。サバイバーでも十分すぎるぐらいに難しい。特にボス。ラスボスとラビリンスがキツかった。。 ソウル系のTPSというやつ。 かなりゲームバランス良くて、楽しめた。雰囲気もダクソっぽくて楽しい。 自動生成ダンジョンなのは野心的だが、結局のところ、ソウル系ってボスが命ってことになっちゃうので、ボスまわりは毎回同じなので、刺激は薄れちゃうかもなぁ。 ゲーム内がかなり説明が不親切で、例えば特性が65が上限なのにそれがゲーム内だとさっぱりわからんとかそういうのはある。。 が、基本的には楽しめた。 攻略につまった場合は野良の協力者を召喚すればうまくいくこともあるので楽しい。
k8s を趣味でも使うか、、と思ってこのブログでも DigitalOcean の k8s を使うようにしてみたのだが、DigitalOcean への月々の支払いが $100/mo とかになってきて、さすがにたけーなと。円安だし。。 というわけで、もう少し安くすることを考えてみた。 DigitalOcean Apps というのがあったので、これを使うようにしてみる。これは AWS App Runner みたいなやつで、簡単に docker image をホスティングできるし、TLS 終端とかもいい感じにやってくれる。 - DigitalOcean Apps は、ghcr.io に対応していないのでまず docker hub に移行 - docker image の push は github actions でやっている - https://github.com/tokuhirom/blog3/blob/main/.github/workflows/docker-publish.yml - DigitalOcean Apps のインスタンスを作る - 1つのポートしか EXPOSE できないのがちょっと不便。このブログアプリ、1つのイメージの中で、外向け用のポートとadmin用のポートを開放する、みたいな構成になってるので微妙。 - しょうがないので一旦、2個インスタンスを起動する。 - Secrets 的なのを環境変数で渡せるので、適当に接続情報などを渡していく - CNAME 受け入れ可能ドメインを設定。blog.64p.org とか。 - Route53 側で、DigitalOcean Apps を CNAME として設定する - 既存の k8s クラスタを終了させる といった感じのオペレーションをしたらとりあえず完了。 これでとりあえず $40/mo ぐらいにはなった。本当は、admin サイトと web サイトを統合して一つの app に入れるようにしたらもう少しコストを圧縮可能だが、、アプリのコードをそこそこいじらないと行けないと思うので、一旦ここまで。
どちらも ggplot2 を意識したインターフェース。 というか kravis は ggplot2 の wrapper なので、ggplot2 の実装をそのまま使えて便利っちゃ便利。 一方で、ggplot2 をインストールしなきゃいけないので面倒なこともある。docker で動かすこともできるのだが、動作にちょっとコツが必要。 kravis は krangl と同じ作者なので、相性がよくて便利だったのだが、kotlinx-dataframe 時代になったのでその優位性は失われた。 letsPlot kotlin は kotlin で実装されてるのでインストールが楽。 昔は letsPlot は機能が少なすぎたが、最近は letsPlot は機能がだいぶ増えてきたので letsPlot に移行することにする。 ``` implementation("org.jetbrains.lets-plot:lets-plot-common:3.2.0") implementation("org.jetbrains.lets-plot:lets-plot-image-export:3.2.0") implementation("org.jetbrains.lets-plot:lets-plot-kotlin-jvm:4.4.1") ``` とかやる。 ```kotlin private fun renderSummary(df: DataFrame<Any?>) { val p = letsPlot(df.toMap()) + geomBar(alpha = 0.3) { x = "year"; fill = "severity" } + facetWrap(facets = arrayOf("product")) + themeLight() ggsave(p, "./reports/summary.png", path = ".") } ``` みたいに実装する。`ggsave()` とか ggplot2 感がすごい。 `LocalDate` をそのまま入れておくと描画できないとか微妙に癖はあるが。。 datalore との相性も jetbrains がメンテしているだけあって良い気がするので、今後はこっちをつかっていきたい所存。
https://qiita.com/ksh-fthr/items/34126324cc71bfc4d469 https://tech.excite.co.jp/entry/2022/12/09/101008 Spring の @Scheduled はデフォルトでは1つのスレッドで動くので、必要ならば `spring.task.scheduling.pool.size` を設定してプールサイズを大きくしておく必要があります。 queue にどのぐらい積まれているかは actuator を prometheus で出している場合、 executor_queued_tasks で確認できるので、これを監視すると良い。
``` > java.io.IOException: Cannot snapshot .git/fsmonitor--daemon.ipc: not a regular file ``` というエラーが `generateGitProperties` タスクで発生する。 `rm -rf .git/**/fsmonitor-*` をした上で、`git config --global fsmonitor.socketDir "$HOME/.git-fsmonitor"` としたほうが良いと思われる。 これでダメなら `git config --global core.fsmonitor false` か。
automatic type coercing が起きると期待しない結果になることがある。 ``` select if('-999999999999' < 0, true, false) as s, if(-999999999999 < 0, true, false) as i ``` ↑の結果が、s は false, i は true となる。 数字をうっかり文字列のまま使ってる場合、int の範囲を超えた場合に false になる。 こういう暗黙の型変換は困るケースも多いので、`spark.sql.ansi.enabled` を有効に設定することを検討する。 ``` spark.conf.set("spark.sql.ansi.enabled", "true") ``` この環境下では、上記のクエリーは `invalid input syntax for type numeric: -999999999999` と型変換エラーになる。 https://spark.apache.org/docs/3.4.1/sql-ref-ansi-compliance.html
https://amzn.to/44CzDFh 数式なども登場せず、LLM について過度な期待をもたせすぎず、それでいて未来への展望を冷静に語っている良書だと思います。 ChatGPT について学びたい人、特に文系の人にはオススメ。
https://github.com/square/okhttp/issues/3154 elasticsearch/opensearch 等では GET request に content-body をつける必要があるが、OkHttp3 では対応していない。強い気持ちがあるようだ。
ネクロマンサーでレベル50まで。 tier1でやった。 楽しかった。エクスプロージョンが使いこなせると楽しくなったかなー。
一般的なプログラミング言語に慣れていると「普通に考えたらデフォルトは machine native だろ」って考えがちだけど、Java の Byte buffer は big endian がデフォルトなので気をつけよう!! めっちゃ時間溶けた。 https://docs.oracle.com/javase/jp/6/api/java/nio/ByteBuffer.html
普段は mybatis を使うことが多いのだが、Prototyping する機会があり、たまには Exposed など使ってみることにした。 Exposed は JetBrains 製なので kotlin と相性良さそうかなーと。 で、実際使ってみると以下のような困ったことがあった。 * DB スキーマをコード上で表現可能だが、たとえば `INDEX url(255)` みたいなのを表現する方法がわからん * `MATCH AGAINST` とかを DSL で書けない * INSERT ON DUP UPDATE とかを雑に使おうとすると、ちょっとめんどい。 * 結局、複雑なクエリを使おうとすると、JDBC のレイヤまで降りていかなきゃいけない。 というわけで、今日もまた mybatis へと戻っていくのだった。 (実は Exposed でもやりようがあるのかもしれないが見つけられなかった)
Kweb がどうも日本語入力がうまくいかなくなったので諦めて普通の Ktor ウェブアプリに戻した。 機能的にはほぼ同じだがシンプルになった。Kweb で少しシンプルに書けるかなーと思ったのだが、そうでもなかったし、、 相変わらず微妙に速度が遅いのだが、、この遅さはなぜか DigitalOcean を使っている結果、シンガポールにデータベースサーバーがあるので、、というよくわからない理由によるものなので、RDSにでも移すかなぁ。
https://mcolotto.github.io/magic-research-demo/website/ Android 版をシコシコとやってた。いわゆる放置ゲー的なやつだけど、この手のやつでは一番おもしろかった。 トータルで 49日かかりました。 何が面白いかと言うとですね。。ゲームバランスが絶妙で、ボスが絶妙に倒せないのと、戦術を工夫しないと先に進めないようになっててめっちゃ楽しい。 転生するたびに見える景色が変わっていく感じがたまらないですな。 ![](https://blog-attachments.64p.org/20230602-093331f6b7b6a0-fb53-43ac-ac62-3233bfb68763.png)
なんか知らんけど、 blog を更新しようとしたら落ちた。 kweb が kotlin-logging の 4 beta に依存してて、 class not found な状態に。悲しい。 しょうがないからアプリ自体も kotlin-loggin 4 beta に依存するようにして解決。 ついでに kweb 自体のバージョンも上げたのだが、普通の input box が、すげー微妙な挙動になってつらい。。日本語の入力時に入力途中の文字が入っちゃうというよくあるやつ。 kweb 飽きたし、そのうち全面的に書き直したいかもしれない。 SPA にするのが良いと思うが、どうしようかなぁ。
https://www.playstation.com/ja-jp/games/marvels-spider-man-miles-morales/ PS5でクリア。 正当な拡張コンテンツという感じ。Spider man 2 も発売決定してるのでそれも楽しみですね。
Watchdogs LEGION をクリアしてから youtube のレビューなどを見たところ、以下のような意見が散見された。 * ロンドンは道が狭いからカーチェイスが楽しくない * ストーリーが薄味 Watchdogs LEGION も結構面白かったのに 1,2 のほうが良かったという意見が多いようなので、そんならやってみるかということでヤッてみた。 やってみるとじっさい、カーチェイス多い。カーチェイス多すぎるぐらい。あと 1 はドローンないからドンパチメインになりがち。 ストーリーもまぁ 1 とか 2 は濃い。2の世界観はなんというか、学生の正義の味方気取りのテロリストって感じでノリについていけない感じはありつつ。。 全体としては 1 も 2 も面白かった。LEGION は方向性としては面白い気がするので、AI の進化を取り入れていければ今後この方向性はもっとおもしろくなるんじゃないかなーと期待している。LEGION の良くないのは、キャラクターを収監されちゃうところな気はするのと、キャラクターごとの個性が弱いってところだと思う。RimWorld とかのほうがよほど思い入れが持ちやすいね。
寄り道しながら27時間ぐらいでクリア。 - 街中の人を採用できるというのが斬新だが、キャラに思い入れしにくいというのは本当にそう。 - 今後の AI 技術の発達によって、音声合成・会話合成がより進めばもっと自由度が生まれて楽しくなるかもしれない - この方向性には未来を感じる。 - 色んなキャラがいるが、そんなにめっちゃいいスキルがあるかというとそうでもないので、採用しようっていうモチベが薄い - もう少しレアスキルもちとかいればな〜 - 育成要素があるともうちょい楽しいと思うけど、ごちゃごちゃしちゃうかも。 - 結局、SMG/LMG 持ってるやつが便利でそういうのばっかり使っちゃう - 後半、ゴリ押しで進めがち - ハッキングしていくのは純粋に楽しい - 流石にリリースから数年たってるのでバグはほぼ感じなかった - 良くも悪くも UBI ゲームって感じで、良ゲー、ぐらいの感じ。 - ストーリーがちょっと弱いというか、先が見えちゃうかも。
kotlin の dataframe 的なライブラリである [krangl](https://github.com/holgerbrandl/krangl) が deprecate されていた。 [kotlinx.dataframe](https://github.com/Kotlin/dataframe) が後継なので、これを利用するようにするとよい。 ドキュメントが整備されていて、読みやすくなっている。dataframe 周りも kotlin で扱いやすくなって最高だ。 https://kotlin.github.io/dataframe/overview.html [kravis](https://github.com/holgerbrandl/kravis) は kotlinx.dataframe ベースに変わっていた。
神ゲーだと思う。Atomic Heart でがっかりした直後だから余計にそう思うのかも。。 STANDARD 28.2時間 で B。Window gaming PC で Steam 版をクリア。弾がなくなることはほぼないかな。 ホラーというよりアクションって感じ。虫がいきなり出てきたところ以外で驚くことはないかな。 原作プレーしてない勢というか、スマホ版とかを昔ちょっとやったけど途中で操作性悪くて辞めた勢。
freemarker の js_string がどのような変遷をたどってきたのかをまとめてみた。 ## 2.3 Date of release: 2004-June-15 https://freemarker.apache.org/docs/versions_2_3.html > New built-ins for Java and JavaScript string escaping: j_string and js_string ## 2.3.1 Date of release: 2005-01-04 https://freemarker.apache.org/docs/versions_2_3_1.html > The js_string built-in now escapes > as \> (to avoid </script>). ## 2.3.20 Date of release: 2013-06-27 https://freemarker.apache.org/docs/versions_2_3_20.html > Bug fix [390] (and other improvements): ?js_string and ?json_string didn't escape the u2028-u2029 line terminators (problem for JavaScript) and the u007F-u009F control characters (maybe a problem in JSON, depending on implementation). Furthermore, the escaping of \, <, and > become safer in that now they are escaped whenever it can't be guaranteed that they won't be part of <!, ]]> or </. Earlier they were only escaped when it was known that they are part of these patterns, thus it was possible to assemble these patterns from two adjacent interpolations. Additionally, from now on <? and --> also count as dangerous patterns, and will trigger < and > escaping. # まとめ 2.3.20 以後、2023年4月現在の最新版である 2.3.32 まで変更はなし。
commons-io で `IOUtils.toString(inputStream, StandardCharsets.UTF_8)` としているケースは、Java 9 以後なら `new String(inputStream.readAllBytes(), StandardCharsets.UTF_8)` と書けて便利。 https://www.baeldung.com/convert-input-stream-to-string
https://openapi-generator.tech/docs/templating/#all-generators-core openapi-generator で enum を制御するにはこのへんを参照。 `x-enum-varnames`: 変数名 `x-enum-descriptions`: description という2つを使って構成する。 varnames はともかくとして descriptions は OpenAPI の spec に入っておくべき要素だと思う。
https://github.com/OpenAPITools/openapi-generator/issues/6190 openapi-generator は ServiceLoader で generator を探すので、CLASSPATH に custom generator を突っ込むことができれば良いです。 CLASSPATH にカスタムジェネレータを突っ込むには `buildSrc` を使えば良いので、例えば以下のように階層構造を作れば良いですね。 ``` . ├── build.gradle.kts ├── buildSrc │ └── src │ └── main │ ├── java │ │ └── my │ │ └── own │ │ └── Generator.java │ └── resources │ └── META-INF │ └── services │ └── org.openapitools.codegen.CodegenConfig ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts ``` `buildSrc/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig` の中に `my.own.Generator` の名前を書けば読み取られます。
AbstractJavaCodeGen あたりを継承して以下のようにメソッドを上書きすれば抑制できる。 ```java @Override public boolean getUseInlineModelResolver() { return false; } ```
Mac OS においてデバッグするときには、NSLog を呼ばないととにかくやりづらい、というときがあります(InputMethodKit を使った開発をするときなどが特にそうです)。 そんなときには、以下の様にすると良いでしょう。 ```rust use cocoa::base::{id, nil}; use cocoa::foundation::NSString; #[link(name = "Foundation", kind = "framework")] extern { pub fn NSLog(fmt: id, ...); } fn main() { unsafe { NSLog(NSString::alloc(nil).init_str("Hello, %@"), NSString::alloc(nil).init_str("world")); } } ``` Dependency は以下のように Cargo.toml に記述します。 ```toml [package] name = "rust-nslog-sample" version = "0.1.0" edition = "2021" [dependencies] cocoa = "0.24.0" ``` さて、とはいえ毎回 NSString だなんだと書くのはめんどくさい、というのはあるかも。 せめて最初の引数はマクロ化したいですよね。 ↓マクロ化するならこんな感じかな? ```rust use cocoa::base::{id, nil}; use cocoa::foundation::NSString; #[link(name = "Foundation", kind = "framework")] extern { pub fn NSLog(fmt: id, ...); } macro_rules! NSLog { ( $fmt:expr ) => { unsafe { NSLog(NSString::alloc(nil).init_str($fmt)) } }; ( $fmt:expr, $( $x:expr ),* ) => { unsafe { NSLog(NSString::alloc(nil).init_str($fmt), $($x, )*) } }; } fn main() { unsafe { NSLog!("Hello, %@ %d", NSString::alloc(nil).init_str("world"), 5963); } } ```