- 投稿日:2020-07-11T23:37:17+09:00
【Goのお勉強中】ラップアラウンド(wrap-around)が要注意な件
背景
Goの独学を進める中で、ラップアラウンドなるものに出会いました。
こりゃ要注意だぞ...と思ったのでまとめます
※何かしら知見を得られたらその度に更新しますラップアラウンドって何?
ざっくり
桁あふれ(オーバーフロー)の上位互換みたいなやつ。
最大値を超えたら、また最小値からスタートする、みたいな。
反対に最小値を下回ったら、最大値になっちゃう、みたいな。
(C言語でも同じことが発生するとかなんとか)例えば
8ビットで数値を管理保持するint8の型を持つ変数aがあった場合
int8の範囲は-128~127じゃないですか。
すると、
現在 a = 127 だったとしたら、a++ をするとaの値が-128になります。
めっちゃ減っとるやんけ
逆にa = -128 の時に1を引くと127になります。嘘だと思うならやってみてください
main.gopackage main import "fmt" func main() { var a int8 = 127 a++ fmt.Printf("ふつうは127+1=128のはず。でもこの結果は... %d\n", a) a-- fmt.Printf("じゃあ-128-1は... %d\n", a) }それでよ
Goの場合、こういった現象が起きても特にエラーは発生しないらしく、ちょっと慎重に実装する必要がありそうです。
どうやって対策するの?
どうやら演算を行う前に、演算結果がラップアラウンドを起こさないか確認する必要があるみたいです。
なんてこったい。int64だとこんな感じで事前チェックを入れるのかな...全然足りないので気が向いたら充実させます。
wrapArround.gofunc wrapArround(a, b int64) bool { if math.MaxInt64-a < b { // 足し算をして問題がないか? return false } else if a < math.MinInt64+b || b < math.MinInt64+a { // 引き算をして問題がないか? return false } return true }気になっていること
もしや日付表現とかでも問題になったりする...?
参考図書
https://www.amazon.co.jp/dp/B01FH3KRTI/ref=cm_sw_r_tw_dp_x_msCcFb90E9FFA
- 投稿日:2020-07-11T22:07:36+09:00
Goでエラーだったときにlogrusでログ出力してみる
はじめに
GoでOpenWeatherMapAPI叩いてみたでやったものをちょっとだけ改良して、エラー時のログ処理を追加してみたものになります。
環境
OS:maOS Catalina 10.15.5
Go:go version go1.14.4 darwin/amd64手順
- logrusをインストールする
※ログレベルを分けたい&速度を気にする必要がない&シンプルに使えそうだったのでlogrusを選択
$ go get github.com/sirupsen/logrus
- init関数でログ設定を行う
func init() { // Info以上のレベルを出力する設定 log.SetLevel(log.InfoLevel) }
エラーだった場合にログを出力する
- APIを叩いたとき
res, resErr := http.Get(url) if resErr != nil { log.Fatalf("Failed to Get API: %v", resErr) }
- レスポンスを取得したとき
// レスポンスを取得 defer res.Body.Close() body, redErr := ioutil.ReadAll(res.Body) // レスポンスを読み込めなかった場合はログを出力 if redErr != nil { log.Warnf("Failed to read response: %v", redErr) }
- JSONをパースしたとき
// 返却されたJSONをパース if paErr := json.Unmarshal(body, &data); paErr != nil { log.Warnf("Failed to parse JSON: %v", paErr) }課題
- ちゃんとテスト書くとどうやってやるんだろう・・・?
- 全部mainに書いちゃってるので、リファクタリングが必要
- API叩いたときにエラーだったらリトライ処理あってもよい
- Goの例外処理の思想がJavaとだいぶ異なるのでそのあたり整理しておさえたい
参考にさせていただいたもの
- 投稿日:2020-07-11T17:48:54+09:00
go修行19日目 HMAC
HAMC
package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" ) // サーバサイドで正しいクライアントのものかを判定する var DB = map[string]string{ "User1Key": "User1Secret", "User2Key": "User2Secret", } func Server(apiKey, sign string, data []byte) { apiSecret := DB[apiKey] // sha256の同じアルゴリズムを使う h := hmac.New(sha256.New, []byte(apiSecret)) h.Write(data) expectedHMAC := hex.EncodeToString(h.Sum(nil)) // 同じアルゴリズムでエンコードしたhmacが一致するかを確認 fmt.Println(sign == expectedHMAC) } func main() { const apiKey = "User1Key" const apiSecret = "User1Secret" data := []byte("data") h := hmac.New(sha256.New, []byte(apiSecret)) h.Write(data) sign := hex.EncodeToString(h.Sum(nil)) // ハッシュ値 fmt.Println(sign) // サーバに投げる Server(apiKey, sign, data) }出力
076b55e7f7e12624b4569f162302f1e36c11fb3a9134889267748de14a84b996 trueSemaphore
- goroutineが走る数を限定することができる
go get golang.org/x/sync/semaphorepackage main import ( "context" "fmt" "time" "golang.org/x/sync/semaphore" ) // 並行実行できるプロセス数を制限 var s *semaphore.Weighted = semaphore.NewWeighted(1) func longProcess(ctx context.Context){ // 処理が並行している場合ロックをかけることができる isAcquire := s.TryAcquire(1) if !isAcquire{ fmt.Println("Could not get lock") return } defer s.Release(1) fmt.Println("Wait...") time.Sleep(1 * time.Second) fmt.Println("Done") } func main() { ctx := context.TODO() go longProcess(ctx) go longProcess(ctx) go longProcess(ctx) time.Sleep(5 * time.Second) }Could not get lock Could not get lock Wait... Doneini
- configファイルを読み込む
- サーバの設定値など
package main import ( "fmt" "github.com/go-ini/ini" ) type ConfigList struct{ Port int DbName string SQLDriver string } var Config ConfigList // main関数が呼ばれる前に設定を呼び込む func init(){ cfg, _ := ini.Load("config.ini") Config = ConfigList{ Port: cfg.Section("web").Key("port").MustInt(), DbName: cfg.Section("db").Key("name").MustString("example.sql"), SQLDriver: cfg.Section("db").Key("driver").String(), } } func main(){ fmt.Printf("%T %v\n", Config.Port, Config.Port) fmt.Printf("%T %v\n", Config.DbName, Config.DbName) fmt.Printf("%T %v\n", Config.SQLDriver, Config.SQLDriver) }config.ini[web] port = 8080 [db] name = stockdata.sql driver = sqlite3出力
int 8080 string stockdata.sql string sqlite3教材
https://www.udemy.com/course/go-fintech/learn/lecture/12088980
- 投稿日:2020-07-11T14:04:07+09:00
go/analysis.Analyzerをnolintできるようにする
go/analysis.Analyzer を使いこなして行こうと思った時に、
ふと思い立ったので作ってみました。https://github.com/kyoh86/nolint
func main() { multichecker.Main( nolint.WrapAll( exportloopref.Analyzer, bodyclose.Analyzer, // ... ), ) }と書くと、
// nolint
のコメントでDiagnosticsの出力を抑制できます。
- 投稿日:2020-07-11T01:13:18+09:00
言語別Stack処理速度比較
1. はじめに
Go言語にはStackが実装されていませんが、
Stackの処理自体は、どの言語でも、
配列や線形リストを使って自作で実装することができます。Go言語向けに自作で作ったついでに、
Stackが実装されている他言語との処理速度(処理時間)を比較してみました。実験で使用した言語は、
- C#(.NET Core3.0)
- go1.14.4
- java 13.0.2
- Python 3.8.3
- C言語(MinGW.org GCC-8.2.0-3)(2020/07/12追記)
の5言語です。
各言語のソースは、githubでも公開しています。
https://github.com/NobuyukiInoue/StackSpeedTest
C#
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_CSGo(配列でStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Go_Stack_by_ArrayGo(線形リストでStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Go_Stack_by_ListNodeJava
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Java_Stack
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Java_ArrayDeque
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Java_DequePython
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Python3C言語(64K個のセグメント(配列)でStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_C_Stack_by_SegmentC言語(線形リストでStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_C_Stack_by_ListNode2. 言語別のメソッド比較
Stackといっても、言語によって、各種操作がメソッドで提供されていたり、
プロパティでチェックしなければならないなど、細かな処理の実装内容が異なります。今回の実験で使用した各言語でのクラス/インタフェース/機能を一覧にすると、以下のようになります。
処理内容 C#(.NET Core3.x)
(Stackクラス)Java 8
(クラスStack)Java 8
(クラスArrayDeque)Java 8
(クラスDeque)Python3.8.3
(リスト)Stackへの書き込み Pushメソッド pushメソッド pushメソッド addFirstメソッド append関数 Stackからの取り出し Popメソッド popメソッド popメソッド removeFirstメソッド pop関数 値の検索 Containsメソッド
(戻り値はbool型)searchメソッド
(戻り値はindex番号)Containsメソッド
(戻り値はbool型)Containsメソッド
(戻り値はbool型)index関数 Stackが空かどうか調べる Countプロパティを利用 emptyメソッド isEmptyメソッド isEmptyメソッド len関数を利用 3. 検証環境と実験結果(2020/07/11追記)
項目 値 OS MS-Windows 10 CPU Intel Core-i7 8559u メモリ 16GB(LPDDR3/2133MHz) メモリ容量を追記しました。(2020/07/11)
処理の内容は、
- 1~1億までの数値のpush()
- 最も深い位置にある値1の検索
- 1億~1までのPop()
です。
1億個の数値をint64(8byte)で割り当てると、762MByteの容量が必要になります。
int32(4byte)なら、半分の381MByteですね。生成するスタックの容量が小さいうちは、それほど処理時間に差はありませんが、
1億個のスタック生成となると、選択した方法によってはメモリ消費が4GBを超えます。搭載されているメモリが8GB以下だと、言語によっては(デフォルト設定では)、
ヒープメモリが足りないといったエラーがでます。各言語での処理時間は以下の通りでした。
消費したメモリについても、Windows10のリソースモニター(コミット値)による表示の目視ではありますが、参考程度に掲載しています。(2020/07/11追記)
C# NET Core 3.0.100
Stackクラスを使用
https://docs.microsoft.com/ja-jp/dotnet/api/system.collections.generic.stack-1?view=netcore-3.1メモリ消費は0.39GBでした。
times Execution time 1st 977 [ms] 2nd 1003 [ms] 3rd 1004 [ms] メモリ消費からみて、内部ではint32で処理が行われているようです。
他言語と比べてかなりメモリ消費が少なく、
処理データの少なさが処理時間の短さに影響しているようです。今回の比較では最速の数値です。
go1.14.4 windows/amd64(配列版)
メモリ消費は2.36GBでした。
times Execution time 1st 2089 [ms] 2nd 1853 [ms] 3rd 1737 [ms] 配列の再割り当てを繰り返しても、それほど遅くはないようです。
Javaより速い結果となりました。go1.14.4 windows/amd64(線形リスト版)
メモリ消費は1.49GBでした。
times Execution time 1st 5617 [ms] 2nd 5400 [ms] 3rd 5569 [ms] 処理時間は配列版よりも遅くなりました。
線形リストでは次ノードへのポインタが必要なので、
直線的なメモリ配置の配列より不要なデータが増えてしまいますが、
配列版より消費が少ない結果となりました。Go言語では、配列の再割り当てを行っても、
プログラムの終了までメモリの解放が行われていないのかもしれません。
(あくまで推測で、だれか検証してほしい)java 13.0.2(クラスStack版)
クラスStackを使用
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Stack.htmlメモリ消費は4.21GBでした。
times Execution time 1st 6743 [ms] 2nd 6690 [ms] 3rd 6733 [ms] java 13.0.2(クラスArrayDeque版)
クラスArrayDequeを使用
https://docs.oracle.com/javase/jp/8/docs/api/java/util/ArrayDeque.htmlメモリ消費は3.96GBでした。
times Execution time 1st 4397 [ms] 2nd 4612 [ms] 3rd 4477 [ms] クラスStackよりも処理時間が短くなりました。
java 13.0.2(インタフェースDeque版)
インタフェースDequeを使用
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Deque.htmlメモリ消費は4.31GBでした。
times Execution time 1st 21416 [ms] 2nd 20187 [ms] 2nd 20341 [ms] クラスStackよりも処理時間が増えてしまいました。
Python 3.8.3
リストを使用
メモリ消費は4.42GBでした。
times Execution time 1st 16957 [ms] 2nd 16561 [ms] 3rd 17558 [ms] 他言語では、一度確保したメモリは消費したままでしたが、
Python3では、1億個のPush直後がメモリ消費の最大値で、
Pop処理が進むにしたがって消費メモリが減っていく様子が目視できます。C言語(64K個単位のセグメント(配列)でStackを実装)(2020/07/12追記)
メモリ消費は0.38GBでした。
さすがにC言語は速いですね。
といっても、C#に負けてしまいました。確保したブロックは破棄せずにそのまま保持するようにすれば、もっと速くなるかもしれません。
times Execution time 1st 1591 [ms] 2nd 1554 [ms] 3rd 1558 [ms] C言語(線形リストでStackを実装)(2020/07/12追記)
メモリ消費は1.55GBでした。
Push処理、Pop処理、それぞれ5秒程度ずつといったところでしょうか。
メモリの確保・解放の処理回数が多いので、それだけ処理時間が遅くなっています。
times Execution time 1st 10981 [ms] 2nd 11066 [ms] 3rd 10799 [ms] 関連(2020/07/11追記)
LeetCodeのProblemの中にも、Stackを自分で実装する問題があります。
いろんな言語での回答例が投稿されているので、処理時間を比較するのも面白いと思います。
- 225. Implement Stack using Queues
https://leetcode.com/problems/implement-stack-using-queues/