あおかびのおすなば

日々のコトとかなんか見た感想とか

Goで循環参照なオブジェクトはGCはされるのか

要約

Goでオブジェクトの循環参照を作ったときGCはされるのか.
マークアンドスイープなのでされる.
実験で確かめた.

こんにちは

この記事はKMCアドベントカレンダーの14日目の記事です.
adventar.org

昨日はdefinedさんの,素人が1から音ゲー曲を作る話【KMCアドベントカレンダー2021】 - でぃふぁいんどの空間でした.
ローパスフィルタっぽいところとか音ゲーっぽくてよかった.
defined.hatenadiary.com


この記事では,Goでオブジェクトの循環参照を作ったときGCで回収されるのかを書きます.
GCの細かいアルゴリズムの話はしません(できない).
実験してGCの動作を確かめたよ,という話です.

循環参照しているコード

Goではパッケージの循環参照はできません.
ですがオブジェクト同士の循環参照はできます.

package main

type S1 struct {
    s2 *S2
}

type S2 struct {
    s1 *S1
}

func main(){
  s1 := S1{}
  s2 := S2{}
  s1.s2 = &s2
  s2.s1 = &s1
}

s1,s2のオブジェクトがお互いを参照しています.

GoのGC

上記のコードですが,仮にs1,s2がヒープにいて,参照カウントでGCしてたら回収されなさそうです.*1

実際にはGoのGCの実装方式はmark-and-sweep方式で,上記のようなコードでも問題なく回収されます.
goのレポジトリのコメントにmark-and-sweep方式であることが書かれています.
mark-and-sweep方式の解説は調べてください.
簡単な理解だと「ルートから探索して到達できなくなったオブジェクトはGCで回収される」という方式で,
循環参照していてもルートから到達不可能であれば回収されます.

実験

もうわかったようなものですが,実際に動かして確認してみます.
実験に使ったコードです.

package main

import (
	"fmt"
	"runtime"
)

type S1 struct {
	s2 *S2
}

type S2 struct {
	s1 *S1
}

func main() {
	m1 := runtime.MemStats{}
	runtime.ReadMemStats(&m1)
	
	for i := 0; i < 10000; i++ {
		Func()
	}
	// runtime.GC()
        m2 := runtime.MemStats{}
	runtime.ReadMemStats(&m2)
	fmt.Println("before GC: ", m1.HeapObjects)
	fmt.Println("after  GC: ", m2.HeapObjects)
	return
}

func Func() {
	s1 := S1{}
	s2 := S2{}
	s1.s2 = &s2
	s2.s1 = &s1
	fmt.Println(s1.s2)
}

GCに回収されたかどうかはGC前後のヒープサイズを比較して確かめることにしました.
メモリに関する情報はruntime.ReadMemStatsで取得することができます.
MemStats.HeapObjectsはヒープに割り当てられたオブジェクトの数です.
今回はこの数値を比較します.

さてまず循環参照しているオブジェクトをヒープに置く必要があります.
go build -gcflags '-m' main.goとするとヒープとスタックどちらに置かれるか確かめることができます.

./main.go:32:2: moved to heap: s1
./main.go:33:2: moved to heap: s2

ちゃんとヒープに配置されています.
ちなみにfmt.Printlnにs1を渡さないとスタックに配置されます.

あとはGC前後のヒープサイズを見れば,GCで回収されたのか分かりそうです.

ループでFuncを繰り返し実行しています.
GC前のヒープサイズ-GC後のヒープサイズが実行するたびに若干ブレるため,変化が分かりやすいようにそうしています.

GCを実行しなかった場合の結果

...
before GC:  151
after  GC:  20156

GCを実行した場合の結果

before GC:  146
after  GC:  148

GCを実行したときはヒープサイズが小さくなっています.
これで無事循環参照もGCによって回収されることが確認できました.
めでたし.

まとめ

Goでは循環参照なオブジェクトもGCされることを実験して確かめました.
runtimeパッケージが便利ですね.
プログラムを計測する際に色々使えそうです.
これ書いてる途中で気になることも出たのでもうちょっと色々調べたかったけど,時間が来たので終わりです.

明日以降のKMCアドベントカレンダーもお楽しみに.

*1:GC=参照カウントという認識しかなかった

2020KMCアドベントカレンダー(自分で作成したVSCode拡張の宣伝)

こんにちは

こんにちは.
kmcid: aokabiです.

この記事はKMC Advent Calendar 2020 - Adventarの12/7の記事になります.

昨日(12/6)の記事はkmcid: damaさんによる
AtCoder青になったので競技プログラミングの話 - DNEK's blog
blog.dnek.net

でした.

競プロ,ちゃんと続けてるのすごいです.
私も前ちょっとやってたんですが,最近はめっきりやらなくなってしまいました.

本題

さて,この記事では自分で作成したVSCode拡張, snippet-add-easyの宣伝をします.

marketplace.visualstudio.com

snippet-add-easyはVSCodeスニペットを追加する拡張です.

まずVSCodeスニペットについて少し説明すると,コードをテンプレートとして保存して,後で呼び出して使える機能です.
よく使うコードを登録しておくと,タイプ数が減って快適にコーディングすることが出来ます.

非常に便利な機能なのですが,一つ問題があります.
VSCodeスニペットjson形式で保存されていて,コードをjson形式に直すのがとても面倒です.
c++のコードをスニペットしたものを例としてあげます.

"init": {
	"prefix": "main",
	"body": [
		"#include <iostream>",
		"#include <vector>",
		"#include <cmath>",
		"#include <algorithm>",
		"#include <utility>",
		"#include <string>",
		"#include <stack>",
		"#include <regex>",
		"#include <numeric>",
		"#include <queue>",
		"#include <set>",
		" ",
		"#define REP(i, n) for (int i = 0; i < n; i++)",
		"#define ALL(v) (v).begin(), (v).end()",
		" ",
		"using namespace std;",
		" ",
		"typedef long long ll;",
		"",
		"int main(int argc, char const *argv[])",
		"{",
		"  return 0;",
		"}"
	],
	"description": "競プロ用"
},

コードはbodyフィールドに書きます.
見て分かる通り,コードを一行ずつダブルクォーテーションでくくる必要があります.
面倒くさいですね.

snippet-add-easyでは,VSCode内でコードを選択して,右クリックから追加することができます.
gyazo.com

Visual Studio MarketplaceでVSCode拡張として公開しているので,他の拡張と同様にVSCodeの拡張タブからインストールすることが出来ます.
一つだけ注意すると現状Windowsでしか動かなくて,MacLinux(WSL含む)ではおそらく使えないです.
本当は記事公開までに調整するつもりだったんです.
プルリク待ってます.

あと,昨日のdamaさんの記事を読んでいる方は気づいたかも知れませんが,
snippet generatorと機能は同じです.

個人的には検索して出来てきたコードをスニペット化したかったらsnippet-generator,
VSCode上で書いていたものをスニペット化したかったらsnippet-add-easy,
という感じで使い分けするといいんじゃないかなと思います.

一応コードを置いておきます.雑ですね.
github.com

締め

もしよかったら使ってみてください.
フィードバックをしていただけると私のモチベーションが上がります.

KMCのアドベントカレンダーをこれからもよろしくおねがいします.

adventar.org

明日はkmcid: opesanによる「5年間総振り返り+聖地巡礼記2020」です.
opesan.hatenablog.com

ありがとうございました.

ISUCON10予選を突破したので感想とやったことを書く(百万円ドリブン:21位)

目次

あいさつ

チーム百万円ドリブンは今年もISUCON本戦出場です!!!

こんにちは.

今年もISUCONの予選に出場したので,参加記を書きます*1

チームはISUCON7の初参加から同じ 百万円ドリブン です.

ISUCON参加も今回で4回目になります. 今まではどこかに集まって参加していたのですが,今年は初めて slack call を使って参加しました.

チームメンバーはmurataさんとgnuさんです. 今回のISUCON予選でも,Bot対策を爆速でやったり,使ったことがなかったspatial indexを使えるところまで持っていったりしていてパワフルです.

チームメンバーの参加ブログを読んでください!!!

nakario.hateblo.jp

chy72.hatenablog.com

レポジトリは以下のものです.

GitHub - nakario/isucon10q: 定額給付金x10

さて,ではやったことと感想を書いていきます.

やったこと

ISUCON10予選概要 問題は,物件検索と物件に合う椅子を検索するアプリケーションでした. 元ネタはSUUMO.

まずチームがやったこととしては,

  • 正規表現Botを弾く
  • インデックスを張った
  • N+1を排除した
  • Geometryに置き換えた
  • Jsonのインデント処理をなくした
  • 1サーバー1テーブルにした

だいたいこんなことをやってました.

メモリに余裕がありそうだったので,カウント系のクエリは一部メモリに乗っけてしまって良さそう, というのもやりたかったのですが,マージまではたどり着けませんでした.

自分がメインでやったのはインデックスを貼るのと,1サーバー1テーブルにするところ(のDB側の設定)です. あとはMySQLやNginxのログの設定や,複数台にしたときに使えるようにMySQLのユーザーを作ったりしました.

自分がメインじゃないところはチームメイトのブログにおまかせするとして,自分がやったことについて書いていきます.

【やったこと1】 インデックスを貼る

インデックスを貼るのは,

  • スローログを見て重そうなクエリを見つける
  • 見つけたクエリを EXPLAIN してインデックスが効いていないことを確かめる
  • 効きそうなインデックスを貼ってもう一回 EXPLAIN して確認する

という流れを繰り返しながらやっていました.

ぶっちゃけ時間がかかるし,スロークエリを見て効きそうなインデックスを一気に貼ってベンチにぶん投げる,というやり方のほうが早いしISUCONならそれでもいい気がします. 私は勇気がなかったので一個ずつやってました. (やっぱり非効率な気がする...)

インデックスを張っている途中でorder by popularity desc, id asc をどうするかという問題にあたりました. popularityを全部正負反転すれば良さそうという案もありましたが,結果的にはMySQL8.0にして降順インデックスを使いました. MySQL8.0にする判断にしたのは,前にISUCONの練習でやったことがあったからです.

スコアへの影響はじわじわ効いた感じです.

「劇的な改善にはならないがやらないと予選突破できないやつだな......」と思いながらやってました.

【やったこと2】1サーバー1テーブルにする

上記のインデックスを貼ったり,チームメンバーがしてくれた改善を含めても,MySQLのCPU使用率が高くボトルネックのままでした. どうにかしてDBを複数台で使おうということになり,

をしようとしました.

10分くらいで諦めました. なぜなら一度もやったことがないからです.

残り1時間くらいの状況で,一度もやったことがないレプリケーションの設定をするのはちょっと厳しかったです. 悔しいので本戦までに練習したいと思います.

  • 1サーバー1テーブルにすればいいのでは

という提案をしてくれたので,そっちに方針を変えました.

といっても自分がやったのはDBの設定を移したりMySQLのユーザーを作ったりするくらいでした.

ここで初めて2000点超えのスコアが出て,盛り上がりました.

【やったこと3】その他

MySQLやNginxのログの設定をしたり,restart.sh書いてログのクリアと再起動処理を一度にやれるようにしたりしました. ここらへんは流石に慣れてきて素早くできるようになってて良かったです.

良かったこと

一つは事前準備です.

今回は踏み台サーバー経由ということで,そのままではFrameGraphやNetDataなどのモニタリングツールが見えませんでした. 競技前にポートフォワーディングの設定を用意していたので,競技開始後にバタバタすることなくモニタリングツールを使うことができました.*2

MySQLを8.0にアップグレードするのも比較的スムーズにできたと思います. これはいつかの予習でやったことがあって,参考にしたサイトをScrapboxにメモしていたのが良かったです. (実行したコマンドをメモしておけばもっとスムーズだったはずなのでそこは反省)

こういう事前準備,スコアに直接関係はしないですがISUCONでは結構重要だと思います.

あとはインデックスをちゃんと貼ったり,データベースを複数台に配置したりするのをスムーズにできたのは,過去の経験が活きててよかったと思います.

良くなかったこと

静的ファイルの配信に手を付けようとしたことです. ドキュメントにベンチマーカーはAPIだけ見に行く,と書いてあるところは読んだのですが,頭から消し飛んでました. 結果的には手を付ける前にgnuさんに指摘されたので,「確かに!」となって時間をロスせずに済んだので良かったです.

強いて言うならやろうとしていることをちゃんと宣言したのはえらい!

あとは,後半気持ち的に焦ってしまったことです. 今回は幸い焦りによる凡ミスは(多分)無かったですが,焦ってもいいことはないので, ちゃんと最後まで落ち着いていられるようになりたいです.

まとめ

過去の経験を活かせているところもあればまだまだだなあと感じるところもあるISUCONでした.

競技終了後の感想とか見ると,結構知らないこともありました. MySQLが8.0でデフォルトでバイナリログを吐き出すようになったとか,generated columnとかspatial indexとか.

運営の皆さんもひとまず予選お疲れさまでした! ポータルのclerとかスコアページが死んでてもベンチマーカーへのキューイングはちゃんとできたりとか良かったです. Webプッシュ通知機能があるのは気づきませんでした!ごめんなさい!

本戦も頑張りたいと思います. 今度こそ100万円取りたい!!!

*1:過去の参加記は何故か下書きに埋もれています

*2:運営がssh_configのサンプルを用意してくれてて気づいた.運営の皆さんありがとうございます.

2020年の頑張ること

2020年やっていきたいことを書きます。 こういうのは簡単にでも明文化するのがいいですよね。

最近自炊を再開したので、この調子で一年間継続したいです。 以前の反省を踏まえて、「食料をいろいろ買って頑張って保存する」をやめたところ、今のところうまくいっています。

家をきれいにしたいですね。 とにかくモノが多くて収集ついていないので捨てたいです。 順調に行けばあと一年で引っ越すのでそのときに大変なことにならないようにしたい。 あと自分は家が荒れてると心も荒れるらしいのでちゃんとしたい。

2020年はこれをベースにとにかく楽しい感じでやっていきたい。 心を健康に保っていられたら研究もなんとかなるんじゃないでしょうか(ホンマかな…) おわり

FortranのドキュメントをFORDで自動生成した話

諸事情により書いたものをここに乗せることができないので私の書いたものはここにありません.

Fortranを書く機会があり,そのドキュメントを作成する必要があったのでドキュメント自動生成ツールを調べた.

Fortranのドキュメントツールをいくつか上げるとこんな感じ.

  • f90doc
  • f90tohtml
  • FortranDOC
  • FORD

詳しくは fortranwiki.org

にいくつか乗っていて,

www.gfd-dennou.org

で比較されている.

今回は,FORDがfortran2003の文法にも対応しているので採用. 本家はここ.

github.com

使い方

トップページ用にHoge.mdを書いて,ford Hoge.mdとすればドキュメントが生成される. ソースコードのあるディレクトリはコマンドライン引数で--src_dir fuga/srcと指定するか Hoge.mdの中に書く.
Hoge.mdはこんな感じで書く.

summary: poyo
src_dir: fuga/src
            piyo/src

Main sentences in markdown.

markdown metadeta を使ってドキュメント生成の設定ができる. 公式のWikiもあるので詳細はそっちで.

Home · Fortran-FOSS-Programmers/ford Wiki · GitHub

使ってみて感想

いいところ

  • pipで入る・コマンド一発でビルドという簡単さ
  • MathJax対応
  • プリプロセッサを使ってくれるのでマクロでいろいろできる
  • ドキュメントをmarkdownでかける
  • htmlで吐き出されるので見るのが楽

ソースコードから自動生成されるドキュメントの見た目はまあ普通.

あとドキュメントに反映させるためのコメントは定義分の下に書かないといけないというのがもともとの実装.
こんな感じで.

function hoge
!! comment

調べてみて, github.com

を読む限り変更されてないようなオチに読み取れてしまったのだけど

github.com

でちゃんとマージされてて

!> comment
function hoge

と書けば可能.

これなら今までどおり定義文の上にコメントをかけるのでめでたい.

不満

  • トップページのSource Codes がディレクトリごとになってほしい
  • Source Code上で関数を呼び出してるところをクリックしたら定義に飛んでほしい
  • もうちょっとサンプルコードほしい

まとめ

割と簡単にドキュメントが作成できたし,開発が止まっていないので,使う価値はあるのではないでしょうか.

CyberRebeatCTF-WriteUp

CyberRebeatCTFに参加したのでWriteUpを書きます.

解いた問題全部書く気合は無いので以下の2つにしぼります.

  • Calculation
  • ChangeHistory

Calculation

まずはCalculation(Type: Programming)について.
59.106.212.75:8080にアクセスすると四則演算程度の数式が出てくるのでそれを何問か正解するとフラグが出てくる,という問題です.
私はpython3に計算させて解きました.
手間取ったところが2つあります.
一つは,数式の前に制御文字が入っていたこと.
最初これに気づかずにeval(数式)がうまく動かなくて困ってました.
これは制御文字をstripして解決.
2つ目は,pythonの出力を吐き出す場所.

nc hogehoge | python3 ans.py

でprint(fuga)すれば出来ると思ってたのですがだめでした.
画面には表示されるんですがncの標準入力に流し込めて無いんですよね...
結局,pythonでファイル(ans.txt)に出力して

tail -f ans.txt | nc hogehoge | python3 ans.py

として解決しました.
もっといいやり方無いんですか...(あったら教えてください)

ChangeHistory

次はChangeHistory(Type: Recon)です.
GitHubリポジトリからフラグを探す問題です.
「CyberRebeat github」でググって出てきたennachさんのgithubレポジトリからフラグを探します.
ChangeHistoryレポジトリを見ると,”I committed the FLAG by mistake! ”と書かれたClosedなissueがあってめちゃくちゃ怪しいです.
コメントを読むと,あるハッシュのコミットをremoveしたということですが,https://github.com/{user}/{repository}/commit/{hash}で出てきました.
せっかくコミット消してもそのハッシュを残してたらだめだよということですかね?

まとめ

実はncでつないでプログラムに解かせるやつやったことなかったので,それができてよかった.
GitHubみたいな普段使ってるツールからフラグを探すのは面白いですね.
おわり

VSCodeのユーザースニペットにフォーマットするやつ

VSCodeのユーザースニペット(json)にフォーマットするやつを作ったのでブログ書きます.
こちらになります.
VSCodeのSnippetにするやつ
非常に簡単な仕様になっておりますがブラウザから利用できるのでよかったら使ってみてください.
ちなみに全部フロントエンドで動いています.

感想