はじめに
Go言語(Golang)は、その並行処理の機能で知られています。本記事では、Go言語における並行処理の使用と非使用時の処理時間を徹底比較します。コード例を豊富に交え、その効果を実際に見ていきましょう。
処理時間の比較:基本の例
まずは、非常に単純な例から始めましょう。
例1: シンプルなループ
並行処理なし
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
for i := 0; i < 1000000; i++ {
fmt.Println(i)
}
elapsed := time.Since(start)
fmt.Println("処理時間:", elapsed)
}
並行処理あり
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
start := time.Now()
for i := 0; i < 1000000; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Println(i)
}(i)
}
wg.Wait()
elapsed := time.Since(start)
fmt.Println("処理時間:", elapsed)
}
この例では、単純なループ処理の時間を比較します。並行処理を使用すると、処理時間が大幅に短縮されることがわかります。
高度な例
より複雑な例を見てみましょう。
例2: ファイルの読み書き
ファイルの読み書きは、I/O操作が多いため、並行処理によって大きなパフォーマンス向上が期待できます。
並行処理なし
package main
import (
"bufio"
"fmt"
"os"
"time"
)
func writeFile(filename string, data []string) {
file, err := os.Create(filename)
if err != nil {
panic(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
for _, line := range data {
fmt.Fprintln(writer, line)
}
writer.Flush()
}
func main() {
start := time.Now()
data := []string{"行1", "行2", "行3", "行4"}
// ファイルに書き込む
writeFile("example.txt", data)
elapsed := time.Since(start)
fmt.Println("処理時間:", elapsed)
}
並行処理あり
package main
import (
"bufio"
"fmt"
"os"
"sync"
"time"
)
func writeFileConcurrently(wg *sync.WaitGroup, filename string, data string) {
defer wg.Done()
file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
fmt.Fprintln(writer, data)
writer.Flush()
}
func main() {
var wg sync.WaitGroup
start := time.Now()
data := []string{"行1", "行2", "行3", "行4"}
for _, line := range data {
wg.Add(1)
// 各行を並行してファイルに書き込む
go writeFileConcurrently(&wg, "example_concurrent.txt", line)
}
wg.Wait()
elapsed := time.Since(start)
fmt.Println("並行処理による処理時間:", elapsed)
}
例3: データベースへの大量データ書き込み
データベースへの大量データの書き込みは、特に並行処理を活用することで大幅なパフォーマンス向上が期待できます。ただし、データベースの種類や設定によっては、並行処理が負荷をかけすぎることもあるので注意が必要です。
並行処理なし
package main
import (
"fmt"
"time"
)
// 仮のデータベース書き込み関数
func writeToDatabase(data string) {
// ここでデータベースへの書き込みを行う想定
time.Sleep(10 * time.Millisecond) // データベースへの書き込み時間をシミュレート
}
func main() {
start := time.Now()
data := []string{"データ1", "データ2", "データ3", "データ4"}
for _, d := range data {
writeToDatabase(d)
}
elapsed := time.Since(start)
fmt.Println("処理時間:", elapsed)
}
並行処理あり
package main
import (
"fmt"
"sync"
"time"
)
func writeToDatabaseConcurrently(wg *sync.WaitGroup, data string) {
defer wg.Done()
// ここでデータベースへの書き込みを行う想定
time.Sleep(10 * time.Millisecond) // データベースへの書き込み時間をシミュレート
}
func main() {
var wg sync.WaitGroup
start := time.Now()
data := []string{"データ1", "データ2", "データ3", "データ4"}
for _, d := range data {
wg.Add(1)
go writeToDatabaseConcurrently(&wg, d)
}
wg.Wait()
elapsed := time.Since(start)
fmt.Println("並行処理による処理時間:", elapsed)
}
例4: APIリクエストのバッチ処理
例4では、APIリクエストのバッチ処理を取り上げます。ここでの目的は、多数のAPIリクエストを効率的に処理することです。並行処理を用いることで、これらのリクエストを同時に送信し、応答を待つ時間を大幅に短縮することができます。
まず、サンプルAPIとして簡単なHTTPリクエストを模擬する関数を用意しましょう。実際のAPIリクエストではないため、ここではHTTPリクエストを送信する代わりに、簡単なスリープを挿入してリクエストのレスポンスタイムをシミュレートします。
サンプルAPI関数
package main
import (
"fmt"
"time"
)
// サンプルAPIリクエスト関数
func sampleAPIRequest(endpoint string) {
// ここでは実際のHTTPリクエストの代わりにスリープを挿入
time.Sleep(100 * time.Millisecond)
fmt.Println(endpoint, "リクエスト完了")
}
func main() {
// メイン処理
}
並行処理なし
func main() {
start := time.Now()
endpoints := []string{"エンドポイント1", "エンドポイント2", "エンドポイント3", "エンドポイント4"}
for _, endpoint := range endpoints {
sampleAPIRequest(endpoint)
}
elapsed := time.Since(start)
fmt.Println("処理時間:", elapsed)
}
このコードでは、一連のエンドポイントに対して順番にリクエストを送信しています。並行処理を使わない場合、各リクエストは前のリクエストが完了するまで待つ必要があります。
並行処理あり
func main() {
var wg sync.WaitGroup
start := time.Now()
endpoints := []string{"エンドポイント1", "エンドポイント2", "エンドポイント3", "エンドポイント4"}
for _, endpoint := range endpoints {
wg.Add(1)
go func(ep string) {
defer wg.Done()
sampleAPIRequest(ep)
}(endpoint)
}
wg.Wait()
elapsed := time.Since(start)
fmt.Println("並行処理による処理時間:", elapsed)
}
結論
Go言語の並行処理機能は、多くの場面で処理速度を大幅に改善します。この記事で紹介した例を参考に、あなたのコードのパフォーマンスを最大化してみてください。