- 投稿日:2020-07-27T23:51:36+09:00
宇宙船のコンピュータの整数演算ユニット作成し、船員を助けよう!
宇宙船のコンピュータの整数演算ユニット作成し、船員を助けよう!!
問題
以下の入力の場合、実装して、出力の配列[0]の値を示せ。
入力
[1,1,28,3,1,1,2,3,1,3,4,3,1,5,0,3,2,9,1,19,1,9,19,23,1,23,5,27,2,27,10,31,1,6,31,35,1,6,35,39,2,9,39,43,1,6,43,47,1,47,5,51,1,51,13,55,1,55,13,59,1,59,5,63,2,63,6,67,1,5,67,71,1,71,13,75,1,10,75,79,2,79,6,83,2,9,83,87,1,5,87,91,1,91,5,95,2,9,95,99,1,6,99,103,1,9,103,107,2,9,107,111,1,111,6,115,2,9,115,119,1,119,6,123,1,123,9,127,2,127,13,131,1,131,9,135,1,10,135,139,2,139,10,143,1,143,5,147,2,147,6,151,1,151,5,155,1,2,155,159,1,6,159,0,99,2,0,14,0];出力
入力配列が同じサイズの配列
例
入力:[1,0,0,0,99] 。出力: [2,0,0,0,99] 原因 (1 + 1 = 2). 入力:[2,3,0,3,99]。出力:[2,3,0,6,99] 原因(3 * 2 = 6). 入力:[2,4,4,5,99,0]。出力:[2,4,4,5,99,9801] 原因(99 * 99 = 9801). 入力:[1,1,1,4,99,5,6,0,99] 。出力:[30,1,1,4,2,5,6,0,99]制約
なし。言語は限らない。
ボーナス
出力配列[0]が19690720となる場合の入力配列[1]と[2]の値は何番でしょう。
取りうる範囲は0~99とする。回答
回答例1(JavaScript)
const intCodeComputer = (intCode=[99]) => { for(let i=0; i<intCode.length; i+=4){ if(intCode[i] === 99) return intCode; switch(intCode[i]) { case 1: intCode[intCode[i+3]] = intCode[intCode[i+1]] + intCode[intCode[i+2]]; break; case 2: intCode[intCode[i+3]] = intCode[intCode[i+1]] * intCode[intCode[i+2]]; break; default: break; } } }; const answer = intCodeComputer([...PUZZLE_INPUT]); console.log(answer[0]);回答例2(C#)
static void Main(string[] args) { Console.WriteLine(string.Join(',', OperationRecursive(args, 0))); } private static int[] OperationRecursive(int[] sources, int step) { int calc(Func<int, int, int> f, int x, int y) => f(x, y); var targets = sources.Skip(step * 4).Take(4).ToArray(); if (targets[0] == 99) return sources; if (targets[0] == 1) sources.SetValue(calc((x, y) => x + y, sources[targets[1]], sources[targets[2]]), targets[3]); if (targets[0] == 2) sources.SetValue(calc((x, y) => x * y, sources[targets[1]], sources[targets[2]]), targets[3]); return OperationRecursive(sources, step + 1); }回答例3(GO)
func main() { numlist := []int{} for i := 0; i < len(numlist); i += 4 { var a = numlist[i+1] var b = numlist[i+2] var c = numlist[i+3] if numlist[i] == 1 { var d = numlist[a] + numlist[b] numlist[c] = d } if numlist[i] == 2 { var d = numlist[a] * numlist[b] numlist[c] = d } if numlist[i] == 99 { break } } fmt.Println(numlist[0]) }回答例4(Java)
public class Main { public static void main(String[] args) { int [] arrayCode = {}; for(int i=0;i<arrayCode.length; i+=4) { if(arrayCode[i]==99){ break; } if(arrayCode[i]==1) { arrayCode[arrayCode[i+3]] = arrayCode[arrayCode[i+1]] + arrayCode[arrayCode[i+2]]; } else if(arrayCode[i]==2) { arrayCode[arrayCode[i+3]] = arrayCode[arrayCode[i+1]] * arrayCode[arrayCode[i+2]]; } } System.out.print(arrayCode[0]); } }回答例5(Perl)
my @array = (1,0,0,0,99); sub calc { my @array = @_; my $length = @array; for (my $i = 0; $i < $length; $i+=4) { ($array[$i] == 99) && (last); if ($array[$i] == 1) { $array[$array[$i+3]] = $array[$array[$i+1]] + $array[$array[$i+2]] } elsif ($array[$i] == 2) { $array[$array[$i+3]] = $array[$array[$i+1]] * $array[$array[$i+2]] } } return $array[0]; } $result = &calc(@array); print "$result";
- 投稿日:2020-07-27T21:50:19+09:00
あなたの知らない拡張Consistent Hashの世界 ~Chord Protocol~
この記事はEnjoy Architectingからの転載です。
概要
分散システムを学術的に学びたくて、
Chord Protocolというアルゴリズムが面白かったので実際に論文を読んで実装してみました。
この記事では、分散システムにおける名前付けの概念と、Chord Protocolの紹介、簡単な検証について言及していこうと思います。
実際に作ったサーバ 分散システムにおける名前付けとは?
分散システムの分野には「名前」、「名前付け」、「アドレス」と呼ばれる概念があります。
それぞれどのような意味を持っているのでしょうか?名前付けと名前
分散システムはネットワークを通じてそれぞれのサーバ、プロセスが協調して動作しています。
この中で、各サーバ、プロセスはやり取りをする相手の「名前」を知らなければやり取りを行うことができません。
この名前の解決を行うことを「名前付け」と呼んでいます。
そして、あるリソース(特定のプロセス、サーバなど)を一意に特定するための文字列を「名前」呼んでいます。アドレス
名前が分かっていても実際にはそれだけではやり取りできず、実際には「アドレス」と呼ばれるリソースの住所に対してアクセスすることでやり取りが成立します。
つまり、リソースに対する実質的なアクセスポイントをアドレスと呼んでいます。
名前との違いは、名前は永続的にリソースを一意に特定するもの、
アドレスはアクセスするために必要な実際のリソースの住所、つまり位置情報を持っているということです。
名前は永続的に同じリソースを指し続けるのに対し、アドレスはそのときリソースが存在する位置によって変化します。具体例
ドメイン名(example.com)が「名前」、example.comに紐づくAレコードのIPが「アドレス」に当たります。
ドメイン名はずっと変わりませんが、IPアドレスはサーバを移管したりすることで変化することがありえます。
永久に同じリソースへのポインタになるのでドメインは「名前」として成立するわけです。Naming Serverとは?
名前からアドレスを解決したり、特定のリソースに名前をつけてくれるサービスのことをNaming Serviceと呼びます。
一番わかり易い例としては、DNSが挙げられます。
DNSは、ドメインという名前からIPというアドレスを解決し、またIPに対してドメイン名をつける名前付けを行うことで成立しています。代表的なアルゴリズム ~Consistent Hash~
名前付け、名前解決のためのアルゴリズムとしてメジャーなものにConsistent Hashがあります。
Consistent Hashは、
サーバの集合(以降各要素をノードと呼ぶ)に対してハッシュ関数で一意のID(たいていはホスト名などのハッシュ)を振り、
各ノードをID順に円状に並べ、
あるIDがどのノードに配置されるべきかを決定するアルゴリズムです。
与えられたID以上であり、値が一番近いノードに解決され、
円状になっているので、
仮にそのノードがいなくなったとしても次のノードに回されるといったように、ノード障害に強いアルゴリズムになっています。詳しくは、こちらがわかりやすいです。
コンシステントハッシュ法ハッシュによって名前付けを行い、
ハッシュによるIDから対応するサーバを解決して名前解決を行います。
(引用: https://vitalflux.com/wtf-consistent-hashing-databases/)Consistent Hashの拡張、Chord Protocol
前述のConsistent Hashは完璧ではなく、欠点があります。
それは、ノードの離脱、ノードの参画がいつ起こったとしても、
ただしい結果を返し続けるためには一つのノードは、参加している他のすべてのノードの状態を把握している必要があることです。
(そうでないとノードが増えたり減ったりした時点で正しく結果を返すことができなくなる)
これは大規模な分散システムになればなるほど、大きな課題になりえます。
大規模なノードの集合では各ノードは他のノードの情報を保持するために大規模なリソースを要求し、また、ノードの参加、離脱時の情報更新などの計算コストも高くつくことになります。
これを解決するべく生まれたのがChord Protocolになります。基本的なアルゴリズムは下記の資料がとてもわかり易くまとめられています。
Chord Protocolは何が優れているのか?
基本的なことは上の資料がとてもわかり易いので、ここではかいつまんで何が優れているポイントなのかを説明しておきます。
乱暴にいってしまえば、
- 経路表による高速な検索
- ノード参画、離脱時に各ノードが独立して経路表を更新する仕組み
- 経路表が最新化されていない場合のためのFallback手段としてのSuccessorList
が主なポイントになります。
これらのポイントにより、耐障害性を担保しつつスケーラビリティを向上させることに成功しています。
順に追っていきましょう。経路表による高速な検索
各ノードは、全てのノードの状態を維持する代わりに、一定サイズの経路表をメンテナンスします。
例えば、32ビット長のハッシュをIDとする場合、32個の行数を持つ経路表を持ち、
この表を元にして、他ノードにルーティングして検索します。
計算量はO(logN)になり(Nはノード数)、これによって大幅にスケーラビリティを担保できるようになります。ノード参画、離脱時に各ノードが独立して経路表を更新する仕組み
Chord Protocolでは、ノードの参画、離脱時に各ノードがそれぞれ安定化させるためのルールを規定しています。
このルールに従った非同期プロセス、ないしスレッドが一定間隔で動作していて、経路表のメンテナンスを行います。
このメンテナンスは、ノードの離脱、参画に対して、メッセージ数がO(log^2N)程度で、そこそこ高速に実行されます。経路表が最新化されていない場合のためのFallback手段としてのSuccessorList
ノードが参加した直後などは当然経路表は最新化されていません。
よって一時的に正しい結果を返せない可能性もあります。
そこで、この資料のスライド131ページあたりに記載されているように、
Successor Listというリストも保持しておき、経路表による検索に失敗した場合はこのリストを使ってFallbackすることができます。実際に実装してみた
ここまでがNaming Service及び、Chord Protocolの説明でした。
今回は、これをもとに実際にNaming ServiceをGoで実装してみました。
Goで書いたChordの参照実装なので名前はgordです。(安直)https://github.com/taisho6339/gord
gordの概要
これはどういう実装かというと、
GordがChord Protocolのノード郡を管理するためのプロセスとして常駐し、
別途ストレージを管理するソフトウェアがgRPCでGordに対してアクセスし、
特定のキーがどのノードに存在するかを検索したり、
自分たちが管理するべきキーを判別したりする、といった機能を提供します。
k8sであればサイドカーとして各Podにくっついているイメージになります。
この構成は実際に論文でもサンプルとして提案されている構成になります。
論文で提案されている構成
実際に落とし込んだ構成 実装に対する検証
実際に実装に落とし込んだところで、ちゃんと実装できているのかを検証したいと思います。
機能的な観点は、単体テストにて実装しているので今回はパフォーマンス、耐障害性について検証してみます。
ただし、ローカルマシンでの検証につき、プロセッサ数に限度があるため、
お遊び程度の検証しかできないのはあしからず...パフォーマンスに関する検証
探索のパフォーマンスを測定してみます。
ノード数を1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024と増やしていったときに特定キーの検索にかかる時間を計算してみましょう。
すでにStabilizationを完了したノードを指定数用意し、検索のパフォーマンスの違いを見ていきます。
実験にした私のマシンは下記のスペックになります。
OS: MacOS Catalina 10.15.5 プロセッサ: 2.4 GHz 8コア/16スレッド Intel Core i9 メモリ: 64 GB 2667 MHz DDR4結果はこんな感じになりました。
ノード数 検索にかかった時間 1 20.1 ns/op 2 4440 ns/op 4 4450 ns/op 8 4644 ns/op 16 4644 ns/op 32 6473 ns/op 64 19778 ns/op 128 25808 ns/op 256 35940 ns/op 512 35825 ns/op 1024 36940 ns/op ローカルマシンでの検証なので32ノード以上になってくると、
ノードが実行している大量のゴルーチンを限られたプロセッサで共有せざるを得なくなってくるため、アルゴリズム以外の要因で遅くなっていそうです。
少なくともノード数によって線形に増加している傾向は見られませんでした。耐障害性に関する検証
docker-compose build && docker-compose up gord1_1 | time="2020-07-22T06:59:08Z" level=info msg="Running Gord server..." gord1_1 | time="2020-07-22T06:59:08Z" level=info msg="Gord is listening on gord1:26041" gord1_1 | time="2020-07-22T06:59:08Z" level=info msg="Running Chord server..." gord1_1 | time="2020-07-22T06:59:08Z" level=info msg="Chord listening on gord1:26040" gord1_1 | time="2020-07-22T06:59:09Z" level=info msg="Host[gord1] updated its successor." gord2_1 | time="2020-07-22T06:59:09Z" level=info msg="Running Gord server..." gord2_1 | time="2020-07-22T06:59:09Z" level=info msg="Gord is listening on gord2:26041" gord3_1 | time="2020-07-22T06:59:09Z" level=info msg="Running Gord server..." gord3_1 | time="2020-07-22T06:59:09Z" level=info msg="Gord is listening on gord3:26041" gord2_1 | time="2020-07-22T06:59:09Z" level=info msg="Running Chord server..." gord2_1 | time="2020-07-22T06:59:09Z" level=info msg="Chord listening on gord2:26040" gord3_1 | time="2020-07-22T06:59:09Z" level=info msg="Running Chord server..." gord3_1 | time="2020-07-22T06:59:09Z" level=info msg="Chord listening on gord3:26040" gord2_1 | time="2020-07-22T06:59:09Z" level=info msg="Host[gord2] updated its successor."すぐにStabilizerがノード間連携をし、経路表を更新することでどのノードも一貫した結果を返しています。
grpcurl -plaintext -d '{"key": "gord1"}' localhost:26041 server.ExternalService/FindHostForKey \ && grpcurl -plaintext -d '{"key": "gord1"}' localhost:36041 server.ExternalService/FindHostForKey \ && grpcurl -plaintext -d '{"key": "gord1"}' localhost:46041 server.ExternalService/FindHostForKey { "host": "gord1" } { "host": "gord1" } { "host": "gord1" }つぎに試しに1ノード退場させてみます。
docker stop 1a3bf85b693b gord3_1 | time="2020-07-22T07:04:43Z" level=error msg="successor stabilizer failed. err = &errors.errorString{s:\"NodeUnavailable\"}" gord2_1 | time="2020-07-22T07:04:43Z" level=warning msg="Host:[gord1] is dead." gord3_1 | time="2020-07-22T07:04:43Z" level=warning msg="Host:[gord1] is dead." gord_gord1_1 exited with code 0すぐにノードのダウンを検知し、更新に入っています。
grpcurl -plaintext -d '{"key": "gord1"}' localhost:36041 server.ExternalService/FindHostForKey \ ✘ 1 && grpcurl -plaintext -d '{"key": "gord1"}' localhost:46041 server.ExternalService/FindHostForKey { "host": "gord2" } { "host": "gord2" }ノード1がダウンしてもすぐに経路表が更新され、さらにノード1に本来属していた"gord1"というキーが別のノードに移っていることがわかります。
まとめ
実際にChord Protocolを実装することで、
実装としては、未熟な点やバグなど残っていると思いますが、何が優れていて、既存の手法とは何が違うのかを実際に理解することができました。
また付加価値として256ビットにもなる大きな数値をGolangでどう計算するのか、
Golangでの並行処理についても勉強することができました。
こういったことが直接お仕事の役に立つケースは多くはないかもしれませんが、実装力のレベルを一段引き上げてくれる良い機会になり、楽しむことができました。
今度はもっと身近なOSSなどで利用されているアルゴリズムなども実装してみたいと思います。
- 投稿日:2020-07-27T21:26:39+09:00
vim-lspでgoの補完が効かない場合のメモ
近頃、vscodeが楽で便利すぎてvimを触る頻度が減ってしまっていたのですが、vimによるgoの開発環境を設定しようと思い立って設定してみました。
基本的にはmattnさんの以下の記事に従いました。
(1) Big Sky :: Vim で Go 言語を書くために行った引越し作業 2020年度版
(2) Big Sky :: Vim をモダンな IDE に変える LSP の設定(1)の内容だと、コード補完が効いたり効かなかったりしたので、(2)に記述のある以下の設定があると良いようです。vim-lspのdocumentにも記述があります。
function! s:on_lsp_buffer_enabled() abort setlocal omnifunc=lsp#complete setlocal signcolumn=yes nmap <buffer> gd <plug>(lsp-definition) nmap <buffer> <f2> <plug>(lsp-rename) endfunction augroup lsp_install au! autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled() augroup END手元にあるMac(8.2.1250)だと上記設定を加えることで補完が動きましたが、ubuntu(8.1.1401)だと動きませんでした。
そこで、ubuntuでvimの最新版(8.2.1303)をbuildしたら補完されるようになったので、困ったら上記を追記&最新版をinstallするといいのかもしれません。
参考までに自分の今のvimrcをおいておきます。
https://gist.github.com/sodefrin/7e89e77904898fa5872ab8b06ddc2e84
- 投稿日:2020-07-27T19:34:52+09:00
go get オプション集
go getのオプション
go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]-uオプション
新しいマイナーリリースまたはパッチリリースが利用可能な場合に、パッケージとその依存パッケージをネットワークから更新する。
使用しているパッケージを更新する際も使うことができる。-dオプション
パッケージのダウンロードのみ、インストールなし
-fオプション
-uオプションがある時のみ使用可能。
上手く説明が出来ないので、参考URLから引用
get -uに、各パッケージがソース管理リポジトリからチェックアウトされていることをインポートパスで暗黙的に確認しないようにします。これは、ソースがオリジナルのローカルフォークの場合に便利です。
-tオプション
指定されたパッケージのテストに必要なパッケージも一緒にダウンロードしてくれる。
-vオプション
実行しているコマンドを表示する。
-fixオプション
依存関係を修正。ダウンロードしたパッケージを修正。
修正してからgetをしてくれる。-insecureオプション
HTTP等の安全性が確保されていない方式を使いリポジトリから取得するためのコマンド。
危険が伴うため、使用はお勧めしません。参考
GO ドキュメント
https://golang.org/cmd/go/#hdr-Download_and_install_packages_and_dependencies
- 投稿日:2020-07-27T14:56:57+09:00
[一発ネタ] Golang で GoTo キャンペーン [クソコード]
みなさんこんにちは! 最近旅してますか?
私は天気が悪いわコロナで騒がしいわで旅できてません!なんかイライラしてきたので今回はプログラムの中だけでも旅をしましょう。
というわけで、モダンな言語なのになんでか知らんがgoto文のあるGo言語でGotoキャンペーンをキメたいと思います!次のプログラムが最終的に出力する文字列を、実行せずに処理を追って当ててみましょう!
プログラム本文
package main import "fmt" func main() { fmt.Print("Go to ") var money int = 10000 var toWhere string var a int goto TROUBLE goto TRAVEL NIGATA: if money == 31000 { toWhere = "fukuoka" goto FUKUSHIMA } else { toWhere += "tou" goto YOKOHAMA } return TRAVEL: fmt.Println("travel") return YOKOHAMA: fmt.Println(toWhere) return TROUBLE: a = 3 if a % 2 == 1{ goto KYOTO } else { goto TOKYO } return FUKUSHIMA: fmt.Println(toWhere) return TOCHIGI: toWhere = "ret" SAITAMA: money += 3000 HOKKAIDO: if money > 30000{ goto NIGATA } else if money > 20000{ goto CHIBA } else if money < 20000 { goto SAITAMA } else if money <= 12000{ goto GUNMA } fmt.Println("hokkaido") return GUNMA: fmt.Println("gunma") TOKYO: fmt.Println("tokyo") return CHIBA: money = 29000 goto TOCHIGI return FUKUOKA: fmt.Println("fukuoka") return KYOTO: toWhere = "kyoto" for _, char := range toWhere { if char == 'y' { money += 5000 goto HOKKAIDO } if char == 't' { money += 2000 goto FUKUOKA } } fmt.Println(toWhere) return }おわりに
いかがでしたか? クソコードすぎて Go to hell するところでしたね。
実際の現場でこんなコードを書いたら Go to hell してもらいます。
このクソコードを素早く読めた人は高いクソコード適性があるので、自慢がてらコメント欄にて回答をコメントしてね!
- 投稿日:2020-07-27T08:12:52+09:00
GoでCLIアプリケーション作成時にホットリロードする
背景と解決したい問題
Goの学習を進めるにあたり、簡易的なサンプルコードを書いて実行する、というサイクルを頻繁に回していました。
その際にいちいちエディタと実行するウィンドウを往復することになり、手数が増えて効率が落ちてしまっている実感がありました。
そこで、ファイルの更新を自動検知して自動でビルド&実行(ホットリロード)してくれる環境が欲しくなりました。解決策
Go のツール、
gohr
を使うことをおすすめします。
https://github.com/longtime1116/gohr導入と利用
README.md に書いてある通り、以下のコマンドを実行します。
$ go get github.com/longtime1116/gohr
あとは、自分の開発しているワーキングディレクトリで
gohr
コマンドを実行すればOK。第一引数に出力ファイル名を指定します。$ gohr main
おまけ
このようなツールを使わなくても watch コマンドを使うと擬似的に同じような機能を実現できます。
状況に関係なく使える技なので、覚えておくと便利かもしれません。# 1秒ごとにコマンドを繰り返す。 $ watch -n1 <コマンド>(例えば↑で
watch -n1 go run main.go
とかやっておけば1秒おきに実行してくれる)
- 投稿日:2020-07-27T06:53:40+09:00
Go 1.15 リリースノート 日本語訳
この記事は https://tip.golang.org/doc/go1.15 を日本語訳したものです. 前のバージョンはこちら: Go 1.14 リリースノート 日本語訳
ドラフトリリースノート - Go 1.15 の紹介
Go 1.15 はまだリリースされていません. これらは書いている途中のリリースノートになります. Go 1.15 は2020年8月にリリースされる予定です.
言語への変更
言語の変更はありません.
ポート
Darwin
Go 1.14 のリリースノートでアナウンスしたように、Go 1.15 は macOS 10.12 Sierra 以降を必要とし、以前のバージョンのサポートは終了となります.
Go 1.14 のリリースノートでアナウンスしたように、Go 1.15 は macOS、iOS、iPadOS, watchOS、tvOS 上の32ビットバイナリ(
darwin/386
とdarwin/arm
のポート)のサポートを終了します. Go は64ビットのdarwin/amd64
とdarwin/arm64
のサポートは継続します.Windows
Go は
-buildmode=pie
cmd/link フラグが指定されたときに Windows ASLR 実行ファイルを生成するようになりました, Windows では、Go コマンドはデフォルトで-buildmode=pie
を使用します.
-race
と-msan
フラグは、unsafe.Pointer
の使用をチェックする-d=checkptr
を常に有効とするようになりました. これは以前から Windows を除く全ての OS でそうでした.Go で作られた DLL は、シグナル(ターミナルでの Ctrl-C など)を受け取ったときにプロセスを終了させないようになりました.
Android
Android 用のバイナリをリンクする際に、Go 1.15 は最近のバージョンの NDK で利用可能な
lld
リンカを明示的に選択します.lld
リンカは一部のデバイスでのクラッシュを回避し、将来の NDK バージョンではデフォルトのNDK リンカになる予定です.OpenBSD
Go 1.15 は
GOARCH=arm
とGOARCH=arm64
での OpenBSD 6.7 のサポートを追加します. 以前のバージョンの Go では、すでにGOARCH=386
とGOARCH=amd64
での OpenBSD 6.7 をサポートしています.RISC-V
Linux 上の 64 ビット RISC-V ポート(
GOOS=linux
,GOARCH=riscv64
)の安定性と性能の向上に進展がありました. また、非同期のプリエンプションもサポートするようになりました.386
Go 1.15 は x87 浮動小数点数のみを持つハードウェア(
GO386=387
)をサポートする最後のリリースとなります. 今後のリリースでは 386 では少なくとも SSE2 をサポートすることが要求されるようになり、Go のGOARCH=386
の最小要件は Intel Pentium 4 (2000年リリース) または AMD Opteron/Athlon 64 (2003年リリース) に引き上げられます.ツール
Go コマンド
GOPROXY
環境変数はエラーを返すプロキシのスキップをサポートしました. プロキシ URL はカンマ (,
) かパイプ文字 (|
) で区切ることができるようになりました. もしプロキシ URL がカンマに続いている場合、go
コマンドは 404 か 410 HTTP レスポンスの場合にのみリストの次のプロキシを試します. もしプロキシ URL がパイプ文字に続いている場合、go
コマンドはどんなエラーでもリストの次のプロキシを試します.GOPROXY
のデフォルト値は、エラーのときにdirect
にフォールバックしないhttps://proxy.golang.org,direct
のままであることに注意してください.
go test
-timeout
フラグの変更がキャッシュされたテスト結果を無効化するようになりました. 長いタイムアウトで実行したテストの結果のキャッシュが、短いタイムアウトでgo test
を再実行した時にパスとしてカウントされることはもう有りません.フラグ解析
go test
とgo vet
の様々なフラグ解析の問題が修正されました. 特にGOFLAGS
の中で指定されたフラグはより一貫性があるように扱われるようになり、-outputdir
フラグは (個々のテストの作業ディレクトリではなく)go
コマンドの作業ディレクトリと解釈されるようになりました.モジュールキャッシュ
モジュールキャッシュの場所は
GOMODCACHE
環境変数で設定できるようになりました.GOMODCACHE
のデフォルト値は、この変更が入る前からモジュールがキャッシュされていた場所であるGOPATH[0]/pkg/mod
です.外部プログラムがファイルシステムを同時にスキャンすることで発生する、モジュールキャッシュにアクセスする
go
コマンドで "Access is denied" エラーが発生する Windows 向けのワークアラウンドが利用可能になりました(issue #36568を見てください). 1.14.2 及び 1.13.10 未満の Go バージョンが同じモジュールキャッシュで同時に実行される時に使用すると安全ではないため、このワークアラウンドはデフォルトでは有効となっていません. 環境変数にGODEBUG=modcacheunzipinplace=1
を明示的に設定することにより、有効にできます.Vet
string(x) への新しい警告
vet ツールは,
x
がrune
もしくはbyte
以外の整数型を持つstring(x)
形式の変換について警告するようになりました. Go での経験から、この形式の変換の多くは、string(x)
が整数x
の文字列表現に評価されると誤って想定していることが分かっています. 実際には、x
の値の UTF-8 エンコーディングを含む文字列として評価されます. 例えば、string(9786)
は文字列"9786"
に評価されるのではなく、文字列"\xe2\x98\xba"
もしくは"☺"
に評価されます。
string(x)
を正しく使用しているコードは、string(rune(x))
に書き換えることが可能です. あるいは、いくつかのケースでは、適切なバイトスライスbuf
でutf8.EncodeRune(buf, x)
を呼び出すことが正しい解決策かもしれません. 他のコードではstrconv.Itoa
やfmt.Sprint
を使用しなければならないでしょう.この新しい vet チェックは、
go test
を使用するときにデフォルトで有効になっています.私たちは、Go の将来のリリースでこの変換を禁止することを検討しています. つまり、
x
の型がrune
かbyte
である整数x
に対してのみstring(x)
が許されるように言語が変更されます. そのような言語の変更は後方互換性がありません. 私達は言語を変更するための最初の試験的なステップとして、この vet チェックを使用しています.不可能なインターフェース変換への新しい警告
あるインターフェイス型から別のインターフェイス型への常に失敗する型アサーションについて、vet ツールは警告するようになりました. これは両方のインターフェース型が違う型シグネチャの同じ名前のメソッドを実装している場合に発生します.
常に失敗する型アサーションを書く理由はないので、この vet チェックを引き起こすコードはすべて書き換えるべきです.
この新しい vet チェックは、
go test
を使用するときにデフォルトで有効になっています.Go の将来のリリースでは、不可能なインターフェイスの型アサーションを禁止することを検討しています. そのような言語の変更は後方互換性がありません. 私達は言語を変更するための最初の試験的なステップとして、この vet チェックを使用しています.
ランタイム
bool
,complex64
,complex128
,float32
,float64
,int
,int8
,int16
,int32
,int64
,string
,uint
,uint8
,uint16
,uint32
,uint64
,uintptr
の派生型の値でpanic
を発生させた場合、そのアドレスの代わりに値が表示されるようになりました. 以前は正確にこれらの型の値の場合にのみ値が表示されていました.Unix システム上で、
kill
コマンドまたはkill
システムコールがSIGSEGV
、SIGBUS
、SIGFPE
シグナルを Go プログラムに送信するために使用され、そのシグナルがos/signal.Notify
でハンドルされていない場合、Go プログラムは確実にスタックトレース有りでクラッシュするようになりました. 以前のリリースでは、挙動が予測不可能でした.スモールオブジェクトの割り当ては、コア数が多い場合のパフォーマンスがずっと良くなり、ワーストケースのレイテンシもより低くなりました.
小さな整数値をインターフェイス値に変換してもアロケーションが発生しなくなりました.
閉じたチャンネルでのノンブロッキング受信が、開いているチャンネルでのノンブロッキング受信と同じ動作をするようになりました.
コンパイラ
パッケージ
unsafe
の 安全規則 は特定の関数を呼び出す際にunsafe.Pointer
をuintptr
に変換することを許しています. 以前は、いくつかのケースで、コンパイラは複数の連鎖変換を許可していました(例えば、syscall.Syscall(…, uintptr(uintptr(ptr)), …)
). コンパイラは今や正確に1つの変換であることを求めます. 複数の変換を使用していたコードは、安全規則を満たすように更新する必要があります.Go 1.15 では、特定のタイプの GC メタデータを削除し、未使用の型メタデータをより積極的に削除することで、典型的なバイナリサイズを Go 1.14 と比較して約5%削減しました.
ツールチェーンは、関数を32バイト境界に揃え、ジャンプ命令をパディングすることによって、
GOARCH=amd64
上で Intel CPU erratum SKX102 を緩和するようになりました. このパディングによりバイナリサイズが増加しますが、上述のバイナリサイズの改善により埋め合わせています.Go 1.15 では、コンパイラとアセンブラの両方に
-spectre
フラグが追加され、Spectre の緩和が有効にできるようになりました. これらはほとんど必要ないはずですが、主に "defense in depth" メカニズムとして提供されています. 詳細については Spectre wiki page を参照してください。コンパイラは、それらが適用される宣言にとって意味のない
//go:
コンパイラディレクティブを、"misplaced compiler directive" エラーとして拒否するようになりました. そのような誤って適用されたディレクティブは以前から壊れていましたが、コンパイラによって無言で無視されていました.コンパイラの
-json
最適化ロギングは、大きな (128バイト以上) コピーを報告するようになり、エスケープ解析の判断の説明を含むようになりました.リンカ
このリリースには、リンカのリソース使用量(時間とメモリの両方)を削減し、コードの堅牢性/保守性を向上させる、Go リンカへの大幅な改良が含まれています.
大規模な Go プログラムの代表的なセットでは、
amd64
アーキテクチャ上で動作するELF
ベースの OS (Linux, FreeBSD, NetBSD, OpenBSD, Dragonfly, および Solaris) の場合、リンクは 20% 高速化され、必要とするメモリは平均で 30% 少なくなり、他のアーキテクチャ/OSの組み合わせではもう少し控えめな改善となります.リンカの性能向上の主要因は、新しく再設計されたオブジェクトファイル形式や、同時実行性を高めるための内部フェーズの見直しです(例えば、シンボルへの再配置を並行して適用する). Go 1.15 のオブジェクトファイルは、1.14 のものよりも若干大きくなっています.
これらの変更は Go リンカの近代化 のためのマルチリリースプロジェクトの一部であり、将来のリリースではさらなるリンカの改良が期待できます.
リンカは
linux/amd64
とlinux/arm64
で内部リンクモードとして-buildmode=pie
をデフォルトとしたので、これらの構成では C リンカを必要としなくなりました.Objdump
objdump ツールは
-gnu
フラグで、GNU アセンブラ構文での逆アセンブルをサポートするようになりました.コアライブラリ
新しい tzdata 埋め込みパッケージ
Go 1.15 には、タイムゾーンデータベースをプログラムに埋め込むことができる新しいパッケージ、
time/tzdata
が含まれています. (import _ "time/tzdata"
のように)このパッケージをインポートすると、ローカルシステム上にタイムゾーンデータベースがない場合でも、プログラムがタイムゾーン情報を見つけることができます.-tags timetzdata
を付けてビルドすることによってでもタイムゾーンデータベースを埋め込むことができます. どちらのやり方でも、プログラムのサイズは約 800KB 増加します.Cgo
Go 1.15 では、C言語型の
EGLConfig
が Go 言語型のuintptr
に変換されます. この変更は Go 1.12 以降のEGLDisplay
や Darwin の CoreFoundation や Java の JNI 型の扱い方に似ています. 詳細は cgo のドキュメント を参照してください.X.509 CommonName の廃止
Subject Alternative Names が存在しない場合、X.509 証明書の
CommonName
フィールドをホスト名として扱うという非推奨でレガシーな挙動がデフォルトで無効になりました.GODEBUG
環境変数にx509ignoreCN=0
の値を追加することで、一時的に再度有効にすることが可能です.
GODEBUG
の設定に関わらずCommonName
が無効なホスト名の場合、常に無視されることに注意してください. 無効な名前には英字、数字、ハイフン、アンダースコア以外の文字や空のラベルや末尾にドットがあるものが含まれます.ライブラリへの小さな変更
いつものように、Go 1の 互換性の約束を念頭に置いて行われた、ライブラリに対するさまざまな小さな変更と更新があります.
bufio
Scanner
が無効なio.Reader
と一緒に使用され、Read
から誤って負の数を返す場合、Scanner
は panic にならず、代わりに新しいエラーErrBadReadCount
を返すようになります.crypto
crypto/rsa
、crypto/ecdsa
、crypto/ed25519
パッケージのPrivateKey
、PublicKey
型に鍵の等価性を比較したり、公開鍵の型安全インタフェースを作成するためのEqual
メソッドが追加されました. このメソッドのシグネチャは、go-cmp
の等価の定義と互換性があります.
Hash
はfmt.Stringer
を実装しました.crypto/ecdsa
新しい
SignASN1
関数とVerifyASN1
関数は、標準 ASN.1 DER エンコーディングでECDSA 署名を生成し、検証することを可能にします.crypto/elliptic
新しい
MarshalCompressed
関数とUnmarshalCompressed
関数は圧縮形式での NIST 楕円曲線点のエンコードとデコードを可能にします.crypto/rsa
VerifyPKCS1v15
は RFC 8017 に従って、先頭のゼロが欠落している無効な短い署名を拒否するようになりました.crypto/tls
新しい
Dialer
型とそのDialContext
メソッドにより、コンテキストを使用して TLS サーバとの接続とハンドシェイクの両方を行うことができます.
Config
型の新しいVerifyConnection
コールバックにより、すべての接続に対してカスタム検証ロジックを適用することができます. このコールバックでは、ピア証明書、SCT、ステープルされた OCSP レスポンスを含むConnectionState
にアクセスすることができます.自動生成されたセッションチケットの鍵は、forward secrecy への影響を制限するために、24時間ごとに自動的にローテーションされ、有効期限は7日間となりました.
接続の再開で再利用されるセッションキーである、TLS 1.2 およびそれ以前のバージョンのセッションチケットの寿命は forward secrecy への影響を制限するため、同様に7日に制限されました.
RFC 8446 で規定されているクライアント側のダウングレード保護チェックが実施されるようになりました. これにより、許可されていないダウングレード攻撃のような動作をするミドルボックスに遭遇したクライアントが接続エラーを起こす可能性があります。
SignatureScheme
、CurveID
、ClientAuthType
がfmt.Stringer
を実装しました.
ConnectionState
のフィールドOCSPResponse
とSignedCertificateTimestamps
がクライアント側の再開された接続で再設定されるようになりました.crypto/x509
証明書上の名前または (
VerifyOptions.DNSName
やVerifyHostname
で) 検証されている名前のいずれかが無効な場合、それ以上の処理なしに大文字小文字を区別せずに比較されます(ワイルドカードを無視したり、末尾のドットを除去したりすることはありません). 無効な名前には、英字、数字、ハイフン、アンダースコア以外の文字、空のラベルを持つ名前、証明書の名前で末尾にドットがあるものが含まれます.新しい
CreateRevocationList
関数とRevocationList
型により、RFC 5280 準拠の X.509 v2 証明書失効リストを作成できるようになりました.
CreateCertificate
はテンプレートが CA でSubjectKeyId
が明示的に指定されていない場合、自動で生成するようになりました.
CreateCertificate
は、テンプレートがMaxPathLen
を指定しているが CA ではない場合に、エラーを返すようになりました.macOS 以外の Unix システムで、
SSL_CERT_DIR
環境変数をコロン区切りリストにできるようになりました.macOS では、cgo が利用可能かどうかに関わらず、システムのトラストルートを取り出すために、バイナリは常に
Security.framework
に対してリンクされるようになりました. 結果として OS のベリファイアとより一貫した動作をするようになりました.crypto/x509/pkix
ExtraNames
が nil の場合、Name.String
はNames
から非標準属性を表示するようになりました.database/sql
新しい
DB.SetConnMaxIdleTime
メソッドは、コネクションが一定期間アイドル状態になった後に、コネクションの総ライフスパンに関係なくコネクションプールからコネクションを削除することができます.DBStats.MaxIdleTimeClosed
フィールドはDB.SetConnMaxIdleTime
によってクローズされた接続の総数を示します.新しい
Row.Err
ゲッターにより、Row.Scan
を呼ばずにクエリエラーをチェックできるようになりました.database/sql/driver
新しい
Validator
インターフェイスはConn
によって実装され、接続が有効かどうか、または破棄すべきかどうかをドライバがシグナルすることができるようになります.debug/pe
PE ファイル形式で使われる定数
IMAGE_FILE
、IMAGE_SUBSYSTEM
、IMAGE_DLLCHARACTERISTICS
が定義されました.encoding/asn1
Marshal
は X.690 DER に従って SET OF の構成要素をソートするようになりました.
Unmarshal
は、X.690 DER に従って最小エンコードされていないタグとオブジェクト識別子を拒否するようになりました.encoding/json
JSON 配列からスライスへのデコードは、パッケージのドキュメントで既に述べられているルールに従って、既存のスライス要素を再利用しなくなりました.
デコード時のネスティングの最大の深さに内部的な制限を持つようになりました. これにより、深くネストされた入力が大量のスタックメモリを使用したり、"goroutine stack exceeds limit" パニックが発生したりする可能性が減りました.
flag
flag パッケージが
-h
や-help
を見て、それらのフラグが定義されていない場合、使用状況のメッセージを表示するようになりました.FlagSet
がExitOnError
で作成された場合、FlagSet.Parse
はステータス 2 で終了します. このリリースでは、-h
や-help
の終了ステータスが 0 に変更されました. 特にこれはコマンドラインフラグのデフォルト処理に適用されます.fmt
表示動詞
%#g
と%#G
は、浮動小数点値の末尾のゼロを維持するようになりました.go/printer
新しい
Mode
値StdFormat
は、出力の表示中に標準フォーマットの変更を適用するようにプリンタに指示します.html/template
すべての JavaScript と JSON のコンテキストで Unicode エスケープ (
\uNNNN
) を使用するようになりました. これによりapplication/ld+json
とapplication/json
のコンテキストでのエスケープエラーが修正されました.io/ioutil
TempDir
とTempFile
はパス区切り文字を含むパターンを拒否するようになりました. つまり、ioutil.TempFile("/tmp",
"../base*")
のような呼び出しは成功しなくなりました. これにより、意図しないディレクトリトラバーサルを防ぐことができます.math/big
新しい
Int.FillBytes
メソッドにより固定サイズのあらかじめ割り当てられたバイトスライスにシリアライズすることができるようになりました.math/cmplx
本パッケージの関数は、無限大、NaN、符号付きゼロなどの特殊な引数の取り扱いに関して、C99 標準(Annex G IEC 60559 互換複素数演算)に準拠するように更新されました.
net
I/O 操作が
Conn.SetDeadline
メソッド、Conn.SetReadDeadline
メソッド、Conn.SetWriteDeadline
メソッドによって設定されたデッドラインを超えると、os.ErrDeadlineExceeded
をラップしたエラーを返すようになりました. これによりエラーが期限を超えたことによるものかどうかを確実に検出することができます. 以前のリリースでは、エラーが発生した際にTimeout
メソッドを呼び出すことを推奨していましたが、 期限を超えていないにもかかわらずTimeout
がtrue
を返すエラーをI/O 操作が返すことがあります.新しい
Resolver.LookupIP
メソッドは、ネットワーク固有の IP ルックアップとコンテキストを受け入れる IP ルックアップの両方をサポートしています.net/http
HTTP Request Smuggling 攻撃が難しくなるようにパースがより厳格になりました. SP や HTAB のように非 ASCII ホワイトスペースはトリムされなくなり、"
identity
"Transfer-Encoding
のサポートが削除されました.net/http/httputil
ReverseProxy
は、入ってくるRequest.Header
マップエントリのX-Forwarded-For
フィールドをnil
とすることによって、X-Forwarded-For
を変更しないことをサポートするようになりました.
ReverseProxy
によってハンドルされた Switching Protocol (WebSocket のような) リクエストがキャンセルされた場合、バックエンド接続が正しく閉じられるようになりました.net/http/pprof
すべてのプロファイルエンドポイントが "
seconds
" パラメータをサポートするようになりました. 与えられた場合、エンドポイントは指定された秒数分のプロファイルを行い、その差分をレポートします.cpu
プロファイルとトレースエンドポイントの "seconds
" パラメータの意味は変更されていません.net/url
新しい
URL
フィールドRawFragment
とメソッドEscapedFragment
は、特定のフラグメントの正確なエンコーディングについての詳細と制御を提供します. これらはRawPath
とEscapedPath
に類似しています.新しい
URL
のメソッドRedacted
は、どのパスワードもxxxxx
に置き換えた文字列形式の URL を返します.os
I/O 操作が
File.SetDeadline
メソッド、File.SetReadDeadline
メソッド、File.SetWriteDeadline
メソッドによって設定されたデッドラインを超えると、os.ErrDeadlineExceeded
をラップしたエラーを返すようになりました. これによりエラーが期限を超えたことによるものかどうかを確実に検出することができます. 以前のリリースでは、エラーが発生した際にTimeout
メソッドを呼び出すことを推奨していましたが、 期限を超えていないにもかかわらずTimeout
がtrue
を返すエラーをI/O 操作が返すことがあります.パッケージ
os
とnet
は、EINTR
で失敗したシステムコールを自動的に再試行するようになりました. 以前はこれが原因で偽のエラーが発生していましたが、Go 1.14 では非同期の先取り機能が追加されたことで、より一般的になりました. これが今や透過的に処理されるようになりました.
os.File
型がReadFrom
メソッドをサポートするようになりました. これにより、あるos.File
から別のos.File
にデータをコピーするためにio.Copy
を使用する際に、一部のシステムでcopy_file_range
システムコールを使用できるようになりました. 結果として、io.CopyBuffer
はos.File
にコピーするときに提供されたバッファを常に使用するとは限りません. プログラムが提供されたバッファを強制的に使用したい場合は、io.CopyBuffer(struct{ io.Writer }{dst}, src, buf)
と書くことによって可能です.plugin
macOS 上の
-buildmode=plugin
で DWARF の生成がサポートされました(デフォルトで有効になっています).
freebsd/amd64
で-buildmode=plugin
でのビルドがサポートされました.reflect
パッケージ
reflect
は、以前はエクスポートされていない埋め込みフィールドへのアクセスを許可していましたが、エクスポートされていないすべてのフィールドのメソッドへのアクセスを禁止するようになりました. 以前の動作に依存しているコードは、代わりに取り囲む変数に対応する昇格メソッドにアクセスするように更新する必要があります.regexp
新しい
Regexp.SubexpIndex
メソッドは、正規表現内で指定された名前の最初のサブ式のインデックスを返します.runtime
ReadMemStats
とGoroutineProfile
を含むいくつかの関数は、ガベージコレクションが進行中であってもブロックしなくなりました.runtime/pprof
goroutine プロファイルはプロファイリング時に各 goroutine に関連付けられたプロファイルラベルを含むようになりました. この機能は
debug=2
で報告されるプロファイルにはまだ実装されていません.strconv
複素数を扱うための
FormatComplex
とParseComplex
が追加されました.
FormatComplex
は、複素数を a と b を実数と虚数のパートとする (a+bi) 形式の文字列に変換します.
ParseComplex
は文字列を指定した精度の複素数に変換します.ParseComplex
は、N+Ni
の形式の複素数を受け付けます.sync
新しいメソッド
Map.LoadAndDelete
は、アトミックにキーを削除し、存在する場合は前の値を返します。
Map.Delete
メソッドはより効率的になりました.syscall
Unix システムでは、
SysProcAttr
を使用する関数は、どちらもCtty
フィールドを使用していますが、互換性のない方法で使用しているので、Setctty
フィールドとForeground
フィールドの両方を設定しようとする試みを拒否するようになりました. 既存のプログラムで両方のフィールドを設定することはほとんどないと想定しています.
Setctty
フィールドを設定するには、ProcAttr.Files
フィールドによって決定されるように、Ctty
フィールドを子プロセスのファイル記述子番号に設定する必要があります. 子ディスクリプタを使用すると常に動作しますが、親ファイルディスクリプタを使用するとたまたま動作する場合がありました.Setctty
を設定するプログラムの中には、子ディスクリプタ番号を使用するためにCtty
の値を変更する必要があるものがあります.
windows/amd64
で浮動小数点数の値を返すシステムコールを呼び出すことができるようになりました.testing
testing.T
型にテストバイナリがタイムアウトを超過した時間を報告するためのDeadline
メソッドが追加されました.
TestMain
関数はos.Exit
を呼び出す必要がなくなりました.TestMain
関数から戻ったとき、テストバイナリはm.Run
によって返された値を使ってos.Exit
を呼び出します.新しいメソッド
T.TempDir
とB.TempDir
は、テスト終了時に自動的にクリーンアップされる一時ディレクトリを返します.
go test -v
テスト名を各行に表示するのではなく、テスト名によって出力をグループ化するようになりました.text/template
JSEscape
は JSON と互換性のある Unicode エスケープ (\u00XX
) を一貫して使うようになりました.time
新しいメソッド
Ticker.Reset
はティッカーの期間の変更をサポートしています。エラーを返すときに、
ParseDuration
が元の値を引用するようになりました.