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.
「サーバーで付近の情報を通知するサービスのつくり方」 という、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.
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.
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だったみたいですね)。
以前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.
スケーラブルに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.
「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のコードだけ上手くコメントアウトされるよう調整すれば完成です。
昨年末にSongmuさんからお話を頂き、 WEB+DB PRESS Vol.97内の連載「第43回Perl Hackers Hub」に 「PerlでのRedis活用法」というタイトルで寄稿しました。 発売日は2月24日です。
内容 簡単に内容を紹介しておきます。 Perl使いではじめてRedisを使う人向けに書いたつもりです。
Redisの簡単な説明 Redisのインストール方と、Perlからの接続方法、そしてRedisの型の説明です。 記事の中でも紹介していますが、Redisはその豊富な型が特長です。 読者はきっとPerl使いだろうということで、Perlの型(Perlにも型はあるんだよ!!)と 比較しながら簡単に紹介しています。
Redisの応用例とCPANモジュールの紹介 Redisを使うとこんなことができるよ、という紹介です。 CPANで公開されているRedis関連のモジュールも合わせて紹介しています。
Redis自体の注意点 以前Redisを使ったサービスの運用に携わっていたのですが、 そのなかで実際に起きたことを元に、Redisの注意点について書きました。 さいわいサービスが停止するような事故にはありませんでしたが、 メトリックスを眺めながらエンジニア勢でヤバイヤバイ騒いでましたね・・・。 みなさんも気をつけて下さい。
執筆してみての感想 昔から文章を書くのにはだいぶ苦手意識があり、 今回の執筆も非常に苦労しました。 一文の前半を書いた時点で 「今から書こうとしている情報は本当に必要なのか」 「自分の記憶違いで間違った情報なのでは」と不安になり、 色々考えているうちに、何書こうとしてたのかわからなくなるんですよね。 まずは適当に書き上げて、後からちゃんと推敲しよう、 とは思いつつもなかなか進められず・・・。 スループットを上げたい。
細かい表現とかも気になってなかなか進まないので、 こういうの入れて頑張ろうと思います!
VS Codeでtextlintを使って文章をチェックする gitbookで技術書を書く環境の構築手順 (執筆が進まないと、こういう環境構築に時間をかけてしまうのもよくないと思うんだ・・・)
余談 ところで、Vol.97と第43回ってどっちも素数ですね! 雑なプログラムを書いて調べてみたところ、 両方素数になるのはVol.83, 第29回以来、7回目(これも素数だ!)。 次はVol.101, 第47回です。 そのときのPerl Hackerは誰になるのでしょうか。楽しみですね!
use warnings; use strict; sub is_prime { my $n = shift; return 0 if $n < 2; my $i = 2; while($i*$i<=$n) { return 0 if $n % $i == 0; $i++; } return 1; } my $i = 1; for my $n(1.
整数の公式でフィボナッチ数列を求めるという記事を読んで、 「これコンパイル時ならGoでも簡単に計算できるのでは?」と思いやってみたメモ。
背景 みんな大好きフィボナッチ数列(要出典)。 漸化式で定義されているため、再帰やループを使って書くことが多いと思いますが、 閉じた式で書くことが知られています。 ただし、この一般式には無理数の演算が入るので、コンピュータで厳密に扱うことはできません。 ところが、さきほど紹介した記事で紹介された方法を使うと、整数の演算のみで実現できるそうです。
原理などはネタ元の記事を参照してもらうとして、 Python3では以下のように書けるらしいです。
def fib(n): return (4 << n*(3+n)) // ((4 << 2*n) - (2 << n) - 1) & ((2 << n) - 1) ある程度大きなフィボナッチ数を求める場合、 計算途中の値が非常に大きくなるため、多倍長整数が必要となります。 Python3は多倍長整数に組み込みで対応していますが、 Goではmath/bigパッケージを利用する必要があります。
なんか面倒だなGolangと思っていたのですが、 Better C - Go言語と整数 #golangを読んで、 「Goの定数には型がない(場合がある)」「任意の精度で計算してくれる」ということを知り、 「つまりコンパイル時に定数として計算すれば楽にいけるのでは!!」と考えたわけです。
結果 ちょっと複雑な式ですが、個々の演算自体はPython3もGoも変わらないので、 翻訳は簡単ですね。
package main import "fmt" const Fib0 = 1 // 0だけはうまくいかない const ( _ = (4 << (iota * (3 + iota))) / ((4 << (2 * iota)) - (2 << iota) - 1) & ((2 << iota) - 1) Fib1 Fib2 Fib3 Fib4 Fib5 Fib6 Fib7 Fib8 Fib9 Fib10 Fib11 Fib12 Fib13 Fib14 Fib15 Fib16 Fib17 Fib18 Fib19 Fib20 Fib21 ) func main() { fibs := []int{ Fib0, Fib1, Fib2, Fib3, Fib4, Fib5, Fib6, Fib7, Fib8, Fib9, Fib10, Fib11, Fib12, Fib13, Fib14, Fib15, Fib16, Fib17, Fib18, Fib19, Fib20, Fib21, } for i, fib := range fibs { fmt.
Go1.8ではdatabase/sqlのcontextサポートが入ります。 (きっと今日のGo 1.8 Release Partyで詳しく説明があるはず、たぶん) それにともないGo言語でSQLのトレースをするで紹介した shogo82148/go-sql-proxyでもcontextを扱えるようにしました。
Go1.8新機能のサポート Golang 1.8 でやってくる database/sql の変更点で mattnさんが紹介しているように、Go1.8ではdatabase/sqlにいくつか新機能が追加されます。 (mattnさんの対応が早すぎて、メソッド名とか微妙に変更が入っているので注意)
特に大きなのがcontextのサポートでしょう。以下のようなコードでクエリのキャンセルが可能になります。
ctx, cancel := context.WithCancel(context.Background()) go func() { // 1秒待ってからキャンセル time.Sleep(1 * time.Second) cancel() }() rows, err := db.QueryContext(ctx, "SELECT name FROM test where id = ?", id) if err != nil { log.Fatal(err) } go-sql-proxyでもcontext対応を行ったので、 proxyを経由した場合でも、キャンセルが可能になります。 (もちろん、originとなるドライバの対応も必要です)
Go1.8ではcontextサポート以外にもいくつか新機能が追加される予定です。 これらについても、originとなるドライバが対応していれば、go-sql-proxy経由でも全く同じように扱えます。
contextとHookの関連付け contextにHookを関連付けて、一部のクエリにだけHookを付けることができるようになりました。 例えば以下のようなコードでctxに関連したクエリだけログを出力できます。
package main import ( "context" "database/sql" "github.com/shogo82148/go-sql-proxy" ) var tracer = proxy.NewTraceHooks(proxy.TracerOptions{}) func main() { // 何もしないproxyをインストール proxy.