20200727のGoに関する記事は7件です。

宇宙船のコンピュータの整数演算ユニット作成し、船員を助けよう!

宇宙船のコンピュータの整数演算ユニット作成し、船員を助けよう!!

illustrain09-utyuu5.png

問題

以下の入力の場合、実装して、出力の配列[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";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

あなたの知らない拡張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について

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を使って試しに3ノードたててみます。

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などで利用されているアルゴリズムなども実装してみたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

https://www.it-swarm-ja.tech/ja/github/go-get%e3%81%a7%e3%81%a9%e3%81%86%e3%82%84%e3%81%a3%e3%81%a6%e3%83%91%e3%83%83%e3%82%b1%e3%83%bc%e3%82%b8%e3%82%92%e3%82%a4%e3%83%b3%e3%82%b9%e3%83%88%e3%83%bc%e3%83%ab%e3%81%a7%e3%81%8d%e3%81%be%e3%81%99%e3%81%8b%ef%bc%9f/1053492866/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[一発ネタ] 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 してもらいます。
このクソコードを素早く読めた人は高いクソコード適性があるので、自慢がてらコメント欄にて回答をコメントしてね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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秒おきに実行してくれる)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/386darwin/arm のポート)のサポートを終了します. Go は64ビットの darwin/amd64darwin/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=armGOARCH=arm64 での OpenBSD 6.7 のサポートを追加します. 以前のバージョンの Go では、すでに GOARCH=386GOARCH=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 testgo 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 ツールは,xrune もしくは byte 以外の整数型を持つ string(x) 形式の変換について警告するようになりました. Go での経験から、この形式の変換の多くは、string(x) が整数 x の文字列表現に評価されると誤って想定していることが分かっています. 実際には、x の値の UTF-8 エンコーディングを含む文字列として評価されます. 例えば、string(9786) は文字列 "9786" に評価されるのではなく、文字列 "\xe2\x98\xba" もしくは "☺" に評価されます。

string(x) を正しく使用しているコードは、string(rune(x)) に書き換えることが可能です. あるいは、いくつかのケースでは、適切なバイトスライス bufutf8.EncodeRune(buf, x) を呼び出すことが正しい解決策かもしれません. 他のコードでは strconv.Itoafmt.Sprint を使用しなければならないでしょう.

この新しい vet チェックは、go test を使用するときにデフォルトで有効になっています.

私たちは、Go の将来のリリースでこの変換を禁止することを検討しています. つまり、x の型が runebyte である整数 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 システムコールが SIGSEGVSIGBUSSIGFPE シグナルを Go プログラムに送信するために使用され、そのシグナルが os/signal.Notify でハンドルされていない場合、Go プログラムは確実にスタックトレース有りでクラッシュするようになりました. 以前のリリースでは、挙動が予測不可能でした.

スモールオブジェクトの割り当ては、コア数が多い場合のパフォーマンスがずっと良くなり、ワーストケースのレイテンシもより低くなりました.

小さな整数値をインターフェイス値に変換してもアロケーションが発生しなくなりました.

閉じたチャンネルでのノンブロッキング受信が、開いているチャンネルでのノンブロッキング受信と同じ動作をするようになりました.

コンパイラ

パッケージ unsafe安全規則 は特定の関数を呼び出す際に unsafe.Pointeruintptr に変換することを許しています. 以前は、いくつかのケースで、コンパイラは複数の連鎖変換を許可していました(例えば、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/amd64linux/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/rsacrypto/ecdsacrypto/ed25519 パッケージの PrivateKeyPublicKey 型に鍵の等価性を比較したり、公開鍵の型安全インタフェースを作成するための Equal メソッドが追加されました. このメソッドのシグネチャは、go-cmp の等価の定義と互換性があります.

Hashfmt.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 で規定されているクライアント側のダウングレード保護チェックが実施されるようになりました. これにより、許可されていないダウングレード攻撃のような動作をするミドルボックスに遭遇したクライアントが接続エラーを起こす可能性があります。

SignatureSchemeCurveIDClientAuthTypefmt.Stringer を実装しました.

ConnectionState のフィールド OCSPResponseSignedCertificateTimestamps がクライアント側の再開された接続で再設定されるようになりました.

crypto/x509

証明書上の名前または (VerifyOptions.DNSNameVerifyHostname で) 検証されている名前のいずれかが無効な場合、それ以上の処理なしに大文字小文字を区別せずに比較されます(ワイルドカードを無視したり、末尾のドットを除去したりすることはありません). 無効な名前には、英字、数字、ハイフン、アンダースコア以外の文字、空のラベルを持つ名前、証明書の名前で末尾にドットがあるものが含まれます.

新しい 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.StringNames から非標準属性を表示するようになりました.

database/sql

新しい DB.SetConnMaxIdleTime メソッドは、コネクションが一定期間アイドル状態になった後に、コネクションの総ライフスパンに関係なくコネクションプールからコネクションを削除することができます. DBStats.MaxIdleTimeClosed フィールドは DB.SetConnMaxIdleTime によってクローズされた接続の総数を示します.

新しい Row.Err ゲッターにより、Row.Scan を呼ばずにクエリエラーをチェックできるようになりました.

database/sql/driver

新しい Validator インターフェイスは Conn によって実装され、接続が有効かどうか、または破棄すべきかどうかをドライバがシグナルすることができるようになります.

debug/pe

PE ファイル形式で使われる定数 IMAGE_FILEIMAGE_SUBSYSTEMIMAGE_DLLCHARACTERISTICS が定義されました.

encoding/asn1

Marshal は X.690 DER に従って SET OF の構成要素をソートするようになりました.

Unmarshal は、X.690 DER に従って最小エンコードされていないタグとオブジェクト識別子を拒否するようになりました.

encoding/json

JSON 配列からスライスへのデコードは、パッケージのドキュメントで既に述べられているルールに従って、既存のスライス要素を再利用しなくなりました.

デコード時のネスティングの最大の深さに内部的な制限を持つようになりました. これにより、深くネストされた入力が大量のスタックメモリを使用したり、"goroutine stack exceeds limit" パニックが発生したりする可能性が減りました.

flag

flag パッケージが -h-help を見て、それらのフラグが定義されていない場合、使用状況のメッセージを表示するようになりました. FlagSetExitOnError で作成された場合、FlagSet.Parse はステータス 2 で終了します. このリリースでは、-h-help の終了ステータスが 0 に変更されました. 特にこれはコマンドラインフラグのデフォルト処理に適用されます.

fmt

表示動詞 %#g%#G は、浮動小数点値の末尾のゼロを維持するようになりました.

go/printer

新しい ModeStdFormat は、出力の表示中に標準フォーマットの変更を適用するようにプリンタに指示します.

html/template

すべての JavaScript と JSON のコンテキストで Unicode エスケープ (\uNNNN) を使用するようになりました. これにより application/ld+jsonapplication/json のコンテキストでのエスケープエラーが修正されました.

io/ioutil

TempDirTempFile はパス区切り文字を含むパターンを拒否するようになりました. つまり、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 メソッドを呼び出すことを推奨していましたが、 期限を超えていないにもかかわらず Timeouttrue を返すエラーを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 は、特定のフラグメントの正確なエンコーディングについての詳細と制御を提供します. これらは RawPathEscapedPath に類似しています.

新しい URL のメソッド Redacted は、どのパスワードも xxxxx に置き換えた文字列形式の URL を返します.

os

I/O 操作が File.SetDeadline メソッド、File.SetReadDeadline メソッド、File.SetWriteDeadline メソッドによって設定されたデッドラインを超えると、os.ErrDeadlineExceeded をラップしたエラーを返すようになりました. これによりエラーが期限を超えたことによるものかどうかを確実に検出することができます. 以前のリリースでは、エラーが発生した際に Timeout メソッドを呼び出すことを推奨していましたが、 期限を超えていないにもかかわらず Timeouttrue を返すエラーをI/O 操作が返すことがあります.

パッケージ osnet は、EINTR で失敗したシステムコールを自動的に再試行するようになりました. 以前はこれが原因で偽のエラーが発生していましたが、Go 1.14 では非同期の先取り機能が追加されたことで、より一般的になりました. これが今や透過的に処理されるようになりました.

os.File 型が ReadFrom メソッドをサポートするようになりました. これにより、ある os.File から別の os.File にデータをコピーするために io.Copy を使用する際に、一部のシステムで copy_file_range システムコールを使用できるようになりました. 結果として、io.CopyBufferos.File にコピーするときに提供されたバッファを常に使用するとは限りません. プログラムが提供されたバッファを強制的に使用したい場合は、io.CopyBuffer(struct{ io.Writer }{dst}, src, buf) と書くことによって可能です.

plugin

macOS 上の -buildmode=plugin で DWARF の生成がサポートされました(デフォルトで有効になっています).

freebsd/amd64-buildmode=plugin でのビルドがサポートされました.

reflect

パッケージ reflect は、以前はエクスポートされていない埋め込みフィールドへのアクセスを許可していましたが、エクスポートされていないすべてのフィールドのメソッドへのアクセスを禁止するようになりました. 以前の動作に依存しているコードは、代わりに取り囲む変数に対応する昇格メソッドにアクセスするように更新する必要があります.

regexp

新しい Regexp.SubexpIndex メソッドは、正規表現内で指定された名前の最初のサブ式のインデックスを返します.

runtime

ReadMemStatsGoroutineProfile を含むいくつかの関数は、ガベージコレクションが進行中であってもブロックしなくなりました.

runtime/pprof

goroutine プロファイルはプロファイリング時に各 goroutine に関連付けられたプロファイルラベルを含むようになりました. この機能は debug=2 で報告されるプロファイルにはまだ実装されていません.

strconv

複素数を扱うための FormatComplexParseComplex が追加されました.

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.TempDirB.TempDirは、テスト終了時に自動的にクリーンアップされる一時ディレクトリを返します.

go test -v テスト名を各行に表示するのではなく、テスト名によって出力をグループ化するようになりました.

text/template

JSEscape は JSON と互換性のある Unicode エスケープ (\u00XX) を一貫して使うようになりました.

time

新しいメソッドTicker.Resetはティッカーの期間の変更をサポートしています。

エラーを返すときに、ParseDurationが元の値を引用するようになりました.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む