Shogo's Blog

May 4, 2017 - 3 minute read - go golang

String::RandomのGo移植を書いてみた

golangkyotoでString::RandomのGo移植についての発表があったと聞き、 これに対抗して以前途中まで書いていたString::RandomのGo移植をちょっといじって公開しました。 shogo82148/go-rerand 背景 ナイーブな実装の問題点 実はgolangkyoto以前にもGoの正規表現エンジンを使ってランダムな文字列を生成する試みはあって、 たしかにこれは面白そうだと記事を読んでいました。 「Goの正規表現エンジンを使ってファジング用ツールを書いてみる」 しかし、gocha同様、この実装では文字列の長さが幾何分布に従うため、短い文字が多めにでてしまいます。 % gocha -n 100000 'a*' | sort | uniq -c 50054 24894 a 12633 aa 6278 aaa 2994 aaaa 1517 aaaaa 809 aaaaaa 400 aaaaaaa 206 aaaaaaaa 109 aaaaaaaaa 54 aaaaaaaaaa 22 aaaaaaaaaaa 15 aaaaaaaaaaaa 7 aaaaaaaaaaaaa 4 aaaaaaaaaaaaaa 3 aaaaaaaaaaaaaaa 1 aaaaaaaaaaaaaaaa 正規表現のパターンを数え上げとその問題点 この問題を解決するために 「この先何パターンあるかを調べておけば、正規表現が表す文字列の集合からランダムに文字列を取り出せるのでは?」 と考え、golangkyoto以前からちょこちょこ実装を進め、不完全ながらも一応動作するところまでは書いていたのです。 有向グラフの経路数えあげ問題なので、メモ化再帰を使って頑張れば解けます。 少々面倒ですが、おねえさんの問題と比べれば簡単です。 パターンを数え上げる都合上、組み合わせが無限にある a* ような正規表現は扱えません。 a{1,10} のように明示的に範囲を指定する必要があります。 たとえば a{1,10} は10パターン組み合わせがあるので、20万個ランダムに生成すると、それぞれのパターンがおおよそ2万個ずつ生成されます。 (-d オプションについては後述) $ rerand -d -n 200000 'a{1,10}' | sort | uniq -c 20153 a 19863 aa 19899 aaa 19908 aaaa 19975 aaaaa 20000 aaaaaa 20081 aaaaaaa 20021 aaaaaaaa 20072 aaaaaaaaa 20028 aaaaaaaaaa [ab]{1,3}のような正規表現でも、それぞれのパターンがおおよそ同じ数だけ生成されます。

Apr 22, 2017 - 3 minute read - postgresql

Re: PostgreSQLで排他制約がめっちゃ便利!!

PostgreSQLで排他制約がめっちゃ便利!!を拝見したのですが、 とても些細なミスがあるのに気がついてしまいました。 本題とは関係ない重箱の隅をつつくような話なので、わざわざコメントするほどのことでもないかと考えていたのですが、 どうしても試してみたいクエリを思いつき、 偶然にもRedis、PostgreSQL、MySQLで近傍検索したときに セットアップしたPostgreSQL環境が残っていたのでやってみました。 試したかったこと そーだいさんの記事からSQLの実行結果を一部引用します。 demo=# SELECT * FROM schedule; schedule_id | room_name | reservation_time -------------+-------------+----------------------------------------------- 1 | soudai_room | ["2017-04-16 11:30:00","2017-04-16 12:00:00") 4 | soudai_room | ["2017-04-16 12:00:00","2017-04-16 12:30:00") 5 | soudai_room | ("2017-04-16 12:30:00","2017-04-16 12:40:00") 8 | soudai_room | ["2017-04-16 14:30:00","2017-04-16 16:00:00") (4 行) schedule_idの5をよく見て下さい。 他のスケジュールは半開区間[)(開始時刻は期間に含むが、終了時刻は期間に含まない)になっているのですが、 schedule_idの5だけ開区間()(開始時刻も終了時刻も期間に含まない)になっています。 つまり 2017-04-16 12:30:00 ジャストに空き時間があるのです。 ここに予約を入れてみたい!!! 試してみた 環境再現 以下のSQLを実行して、そーだいさんの記事と同じ内容を含んだテーブルを作成します。 CREATE TABLE schedule ( schedule_id SERIAL PRIMARY KEY NOT NULL, room_name TEXT NOT NULL, reservation_time tsrange NOT NULL ); INSERT INTO schedule (schedule_id, room_name, reservation_time) VALUES (1, 'soudai_room', '["2017-04-16 11:30:00","2017-04-16 12:00:00")'), (4, 'soudai_room', '["2017-04-16 12:00:00","2017-04-16 12:30:00")'), (5, 'soudai_room', '("2017-04-16 12:30:00","2017-04-16 12:40:00")'), (8, 'soudai_room', '["2017-04-16 14:30:00","2017-04-16 16:00:00")'); -- schedule_idが1から始まってしまい、INSERTした内容と重複してしまうので調整 SELECT setval ('schedule_schedule_id_seq', 8); SELECTを実行すると同じ内容になっていることを確認できます。

Apr 13, 2017 - 2 minute read - perl

Perl+List::Utilの64bit整数の罠にはまった話

先日 Google Code Jam Qualification Round 2017 が開催されました (って何?というひとはAboutのページを確認。本題では無いので説明略)。 僕もこれに参加して、D以外の問題A,B,Cを解いて、無事Round1へ進むことができました。 しかしPerlで解いたC-largeだけ何故か間違いの判定。 原因を探ってみたところ、PerlおよびList::Utilの64bit整数の罠にはまっていたことに気が付いたので、その備忘録として残しておきます。 問題が発生したコード 問題が発生するのは以下の計算をするコードです。 max: 250000000000000000と249999999999999999で大きい方を返す div: 249999999999999999を2で割った商を求める この計算をそれぞれ二通りの計算方法で実装してみます。 use 5.24.0; use List::Util qw(max); say "max:"; say max(250000000000000000, 249999999999999999); say max(249999999999999999, 250000000000000000); say "div:"; say int(249999999999999999/2); say 249999999999999999 >> 1; max: 順番を変えただけなので、同じ結果をになるはず div: 割り算と等価なビットシフトに置き換えたので、同じ結果になるはず 僕は「同じ結果になるはず」と期待していました。 しかし、これを実行してみると以下のようになります。 [Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ https://wandbox.org/permlink/5fUBzLmBCRKUo4xZ max: 249999999999999999 250000000000000000 div: 125000000000000000 124999999999999999 原因 250000000000000000は大体2^57.8なので、64bitの整数で十分表現できます。 しかし倍精度浮動小数点数として扱われると、精度が53bit分しかないので正確に表現できないのです。 例えば以下のコードは"true"を出力します(ここだけ何故かGo)。 package main import ( "fmt" ) func main() { fmt.

Apr 13, 2017 - 3 minute read - go golang

Go言語のヒープに確保するデータの初期化コストについて調べてみた(Go1.8.1版)

golangで p := new(Type) と p := &Type{} の使い分けってどうするべきだろう? — MURAOKA Taro (@kaoriya) 2017年4月12日 こちらのツイートに対して、以下のベンチ結果が紹介されていました。 Go言語のヒープに確保するデータの初期化コストについて調べてみた しかしhnakamur2さんも言及しているように、 これはGo1.2.2時の結果。 その後、GoのコンパイラがGo実装になったり、SSAが導入されたりと、 今のコンパイラの実装は当時とは全く違うものになっています。 というわけで、現時点での最新のバージョン(Go1.8.1)で、同様の検証をおこなってみました。 検証コード 検証に使用したコードはGo1.2.2のときと全く同じものです。 // alloc_overhead.go package main type container struct { v [64]byte } func MakeContainer() *container { c := container{} return &c } func MakeContainerOneLine() *container { return &container{} } func MakeContainerNew() *container { return new(container) } func main() { _ = MakeContainer() _ = MakeContainerOneLine() _ = MakeContainerNew() } // alloc_overhead_test.

Mar 28, 2017 - 4 minute read - mysql redis postgresql

Redis、PostgreSQL、MySQLで近傍検索

「サーバーで付近の情報を通知するサービスのつくり方」 という、Geohashを使って近傍検索を実現する記事をみつけました。 最近Redisに関する記事を書いた関係で、 この記事をみて「GeohashはRedisと一緒に使うともっと便利だよ!」と思わず宣伝したくなったのですが、 MySQL5.7でInnoDBに空間インデックス(Spatial Index)のサポートが入ったので 「MySQLでももっと簡単にできるのでは?」と思い、 RedisやMySQLを含めたいろんなDBで近傍検索を実現する方法を調べてみました。 以前、スマートフォンのセンサを活用して花火の打ち上げ場所を推定するアプリを作った関係で、 地球上での距離計算の実装も気になったので、それについても調査してみました。 関連知識 GeoHash Geohash(ジオハッシュ) は緯度・経度を短い文字列に変換する方法です。 距離が近い2地点のGeohashは似たような文字列になるという特徴があります(一部例外あり)。 この特徴を利用すると、文字列検索だけで近傍検索が実現できます。 地球上の二点間の距離 地球は完全な球体ではなく、回転楕円体であることが知られています。 地球の形がわからないと緯度・経度などを決められないので、 地球楕円体が定義されています。 近似方法によっていくつか種類があるのですが、GPSなどで使われているWGS84がよく使われているようです。 国土地理院が提供している測量計算サイトでは 距離と方位角の計算を使って緯度・経度から距離を計算できます。 回転楕円体上の距離の厳密解は求められない(要出典)ので、 数値計算によって求めることになります。 計算式を見て分かる通り非常に複雑なので、なんらかの近似をしている実装がほとんどです。 各種DBでの実現方法 Redis Redisでは3.2からGEO関連の機能をサポートしています。 ソート済みセットにGeohashを組み合わせて実現しています。 簡単に試してみました。データは以下の記事から拝借したものを使用します。 MySQLで指定した緯度経度から半径nメートル内検索っぽいのを実現するSQL PostgreSQLとOracleで緯度経度から半径nメートル内検索を実行してみる。 GEOADDでデータ挿入です。 ちなみにデータを削除するGEODELは用意されていないとのこと。 中身はソート済みセットなので、ZREMでいいんですね。 $ cat command.txt GEOADD geotable 139.777254 35.713768 上野駅 139.774029 35.711846 西郷隆盛像 GEOADD geotable 139.774744 35.712737 上野の森美術館 139.770872 35.712351 不忍池弁財天 GEOADD geotable 139.775696 35.716293 野口英世博士像 139.775803 35.715420 国立西洋美術館 GEOADD geotable 139.776544 35.716319 国立科学博物館 139.772776 35.717186 東京都美術館 GEOADD geotable 139.

Mar 17, 2017 - 2 minute read - go golang

Go言語のchanはいったいいくつ付けられるのか試してみた

pecoに入った修正をみて、果たしてchanはいくつまで付けられるのか気になったので、 雑に試してみました。 先に断っておきますが、全く有用ではないですよ。 背景 pecoに入った修正はこちら(一部抜粋)。 Make Resume a blocking operation #411 diff --git a/interface.go b/interface.go index 3d4472f..fff446c 100644 --- a/interface.go +++ b/interface.go @@ -162,8 +162,8 @@ type Screen interface { // Termbox just hands out the processing to the termbox library type Termbox struct { mutex sync.Mutex - resumeCh chan (struct{}) - suspendCh chan (struct{}) + resumeCh chan chan struct{} + suspendCh chan struct{} } // View handles the drawing/updating the screen diff --git a/screen.

Mar 11, 2017 - 1 minute read - go golang time

HTTP/WebSocketで時刻同期するWebNTPを書いた

Go1.8からhttp/httpgtraceが追加され、 HTTP通信にフックを差し込めるようになりました。 以前触った時はパフォーマンス測定に利用しましたが、 他に面白い活用法はないかとWebNTPというのを作ってみました。 webntp HTTP/HTTPS/Websocket上でNTP(Network Time Protocol)っぽいことをするプログラムです。 HTTP/HTTPSで時刻同期 日本標準時はNICTの管理する原子時計が基準になっており、 NICTでは原子時計に直結したNTPサーバー(ntp.nict.jp)の提供を行っています。 それに加えて、https/httpサービスも提供しており、 ブラウザを使って現在時刻を取得できます。 APIは簡単でミリ秒単位のtimestampを返してくれるだけです。 その情報からサーバーとクライアント間の時間のズレを算出するわけですが、 HTTP通信では、DNSの名前解決、TCPハンドシェイク、TLSハンドシェイク等々の時間が入ってしまうため、 正確なズレを求めることは困難です。 そこでhttp/httpgtraceを使って、ハンドシェイクを除いたリクエストの送信時刻、レスポンスを最初に受信した時刻から、 より正確なズレを知ることができるのではと作ったのがWebNTPです。 NICTのJSON形式のAPIに対応しており、 以下のように時刻を取得できます。 $ go get github.com/shogo82148/go-webntp/cmd/webntp $ webntp https://ntp-a1.nict.go.jp/cgi-bin/json server https://ntp-a1.nict.go.jp/cgi-bin/json, offset -0.006376, delay 0.024411 2017-03-11 16:08:06.150393313 +0900 JST, server https://ntp-a1.nict.go.jp/cgi-bin/json, offset -0.006376 WebNTPはNICTのAPIと同様の内容を返すサーバーにもなれます。 本家のフォーマットにしたがい、しっかりとうるう秒の情報も返すようになっているので、 ntpdのSLEWモードを切った状態でお試しください。 $ webntp -serve :8080 $ curl -s http://localhost:8080/ | jq . { "id": "localhost:8080", "it": 0, "st": 1489217288.328757, "time": 1489217288.328757, "leap": 36, "next": 1483228800, // 今年の1/1にあったうるう秒の情報 "step": 1 } ところで、URLにcgi-binが入っているのは、過去との互換性を保つためにそうなっているのか、 今もCGIで動いているのか、気になる・・・ (少なくとも初期実装はPerlのCGIだったみたいですね)。

Mar 5, 2017 - 3 minute read - go golang

go-JSONStoreの高速化と機能追加

以前mattnさんが紹介していたschollz/jsonstore。 時間が経ってしまいましたが「ここは高速化できそうだなー」といじってみたので、 やってみたことをメモ。 本来は上流にフィードバックしたほうがよいのですが、 本家のほうも修正が入ってコンフリクトして面倒になったので、 フォーク版をそのまま置いておきます。 shogo82148/jsonstore 高速化 まだまだ高速化できそうなところがあったので、いじってみた部分です。 ロックの範囲を最小にする ロックの範囲を小さくすることで、並列処理時の性能が上がります。 例えば、jsonstoreに値を入れるSetメソッドは、 以下のようにSet全体がロックの対象になっていました。 func (s *JSONStore) Set(key string, value interface{}) error { // Set の中全体がロックの対象になっている s.Lock() defer s.Unlock() b, err := json.Marshal(value) if err != nil { return err } if s.data == nil { s.data = make(map[string]*json.RawMessage) } s.data[key] = (*json.RawMessage)(&b) return nil } jsonのエンコード処理はjsonstoreの中身を触らないので並列実行可能です。 次のように s.data だけをロックの対象にすれば十分です。 func (s *JSONStore) Set(key string, value interface{}) error { // json.Marshal は並列実行可能 b, err := json.

Feb 26, 2017 - 2 minute read - go golang redis

Redisを使ってユニークなIDを配布する

スケーラブルにIDを生成する方法として Twitterのsnowflakeが有名です。 1024台までスケールすることが出来ますが、各snowflakeのサーバにユニークなWoker IDを割り振る必要があります。 IDを振るためのサーバにIDを振るのが問題になるとは難しいですね。 各snowflakeサーバにIDを振る親玉Worker ID配布サーバを作るというアイデアはあったのですが、 Worker IDサーバの可用性を考えるのが大変で手を付けていませんでした。 最近になってWorker IDサーバとしてRedisを使い、ソート済みセット型で管理すれば楽できるのでは? と思いついたので、やってみたというお話です。 概要 レポジトリはこちらです。 shogo82148/yaraus 他のsnowflake-likeなID発番サーバの実装として katsubushiや sonyflakeなんていうのもあります。 これらのID発番サーバにRedisを使ってWorker IDを割り振るコマンドです。 Redis3.2以上推奨です。 使い方 Go製なのでgo getでインストールできます。 go get github.com/shogo82148/yaraus/cmd/yaraus # 1から1023までのIDが使えるようにRedisを初期化 $ yaraus init -min 1 -max 1023 # ユニークなIDが必要な処理を実行する $ yaraus run -- echo {} 2017/02/25 17:19:16 getting new id... 2017/02/25 17:19:16 client id: YourHostName-1488010756.738-1, id: 1 2017/02/25 17:19:16 sleep 2s for making sure that other generates which has same id expire.

Feb 25, 2017 - 2 minute read - polyglot rust rustlang go golang

Rust vs Go の終戦へ向けてPolyglotを作ってみた

「Golang Rust」とググると、関連項目は「Rust vs Go」のように GolangとRustが対立しているような項目ばかりです。 まあまあ、もっと仲良くやろうじゃないですか、ということで、 どうしたら仲良くなれるかを考えました。 Polyglotにして同じソースコードの中に閉じ込めてやれば、 そのうち仲良くなるのではないかと考え、 RustとGoのPloyglotを作ってみました。 結果 /*/*/ package main import "fmt" func main() { fmt.Print("Hello Go!!") _ = `*/*/ fn main() { println!("Hello Rust!!"); //` } /*/*/ package main import "fmt" func main() { fmt.Print("Hello Go!!") _ = `*/*/ fn main() { println!("Hello Rust!!"); //` } 仕組み 一番のポイントは最初の行の /*/*/ です。 RustもGoも/* */形式の複数行コメントに対応していますが、 Rustはネストに対応しており、Goはネストはできないという違いがあります。 この違いにより、Rustは/*/*/を/* /* /のように「二重にネストしたコメントの開始部分」として扱いますが、 Goは/* / */のように「/をコメントアウトしたもの」と見なします。 これにより2行目package main以降はGoには普通のコードに見えますが、 Rustからは単なるコメントとして認識されます。 次はGoからRustへの切り替えです。 Goではバッククオートで複数行文字列を定義できるので、その中にRustのコードを書きます。 この中ではバッククオートさえ使わなければ自由にRustのコードを書くことが出来るので、 あとはGoのコードだけ上手くコメントアウトされるよう調整すれば完成です。