From 8e2f2cb83d6921829028dcc660098630d965ab25 Mon Sep 17 00:00:00 2001 From: teivah Date: Tue, 15 Dec 2020 00:18:19 +0100 Subject: [PATCH] Merge sort --- concurrency/mergesort.go | 91 ++++++++++++++++++ concurrency/mergesort_test.go | 96 +++++++++++++++++++ .../{concurrency-not-magic.go => parsing.go} | 0 ...ency-not-magic_test.go => parsing_test.go} | 0 4 files changed, 187 insertions(+) create mode 100644 concurrency/mergesort.go create mode 100644 concurrency/mergesort_test.go rename concurrency/{concurrency-not-magic.go => parsing.go} (100%) rename concurrency/{concurrency-not-magic_test.go => parsing_test.go} (100%) diff --git a/concurrency/mergesort.go b/concurrency/mergesort.go new file mode 100644 index 0000000..e05db9f --- /dev/null +++ b/concurrency/mergesort.go @@ -0,0 +1,91 @@ +package concurrency + +import "sync" + +func mergesortSequential(s []int) { + if len(s) > 1 { + middle := len(s) / 2 + mergesortSequential(s[:middle]) + mergesortSequential(s[middle:]) + merge(s, middle) + } +} + +func mergeSortConcurrentV1(s []int) { + if len(s) > 1 { + middle := len(s) / 2 + + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + mergeSortConcurrentV1(s[:middle]) + }() + + go func() { + defer wg.Done() + mergeSortConcurrentV1(s[middle:]) + }() + + wg.Wait() + merge(s, middle) + } +} + +const max = 1 << 20 + +func mergeSortConcurrentV2(s []int) { + if len(s) > 1 { + if len(s) <= max { + // Sequential + mergesortSequential(s) + } else { + // Concurrent + middle := len(s) / 2 + + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + mergeSortConcurrentV2(s[:middle]) + }() + + go func() { + defer wg.Done() + mergeSortConcurrentV2(s[middle:]) + }() + + wg.Wait() + merge(s, middle) + } + } +} + +func merge(s []int, middle int) { + helper := make([]int, len(s)) + copy(helper, s) + + helperLeft := 0 + helperRight := middle + current := 0 + high := len(s) - 1 + + for helperLeft <= middle-1 && helperRight <= high { + if helper[helperLeft] <= helper[helperRight] { + s[current] = helper[helperLeft] + helperLeft++ + } else { + s[current] = helper[helperRight] + helperRight++ + } + current++ + } + + for helperLeft <= middle-1 { + s[current] = helper[helperLeft] + current++ + helperLeft++ + } +} diff --git a/concurrency/mergesort_test.go b/concurrency/mergesort_test.go new file mode 100644 index 0000000..efbbbab --- /dev/null +++ b/concurrency/mergesort_test.go @@ -0,0 +1,96 @@ +package concurrency + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +const size = 10_000_000 + +func Test_mergeSort(t *testing.T) { + var ( + input = []int{5, 8, 9, 5, 0, 10, 1, 6} + expected = []int{0, 1, 5, 5, 6, 8, 9, 10} + ) + + type args struct { + f func(s []int) + input []int + } + tests := map[string]struct { + args args + expected []int + }{ + "sequential": { + args: args{ + f: mergesortSequential, + input: input, + }, + expected: expected, + }, + "concurrent v1": { + args: args{ + f: mergeSortConcurrentV1, + input: input, + }, + expected: expected, + }, + "concurrent v2": { + args: args{ + f: mergeSortConcurrentV2, + input: input, + }, + expected: expected, + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + tt.args.f(tt.args.input) + assert.Equal(t, tt.expected, tt.args.input) + }) + } +} + +func Benchmark_mergeSortSequential(b *testing.B) { + for i := 0; i < b.N; i++ { + s := random(size) + b.StartTimer() + mergesortSequential(s) + b.StopTimer() + } +} + +func Benchmark_mergeSortConcurrentV1(b *testing.B) { + for i := 0; i < b.N; i++ { + s := random(size) + b.StartTimer() + mergeSortConcurrentV1(s) + b.StopTimer() + } +} + +func Benchmark_mergeSortConcurrentV2(b *testing.B) { + for i := 0; i < b.N; i++ { + s := random(size) + b.StartTimer() + mergeSortConcurrentV2(s) + b.StopTimer() + } +} + +func random(n int) []int { + s := make([]int, n) + + src := rand.NewSource(time.Now().UnixNano()) + rand := rand.New(src) + + for i := 0; i < n; i++ { + s[i] = rand.Intn(n) + } + + return s +} diff --git a/concurrency/concurrency-not-magic.go b/concurrency/parsing.go similarity index 100% rename from concurrency/concurrency-not-magic.go rename to concurrency/parsing.go diff --git a/concurrency/concurrency-not-magic_test.go b/concurrency/parsing_test.go similarity index 100% rename from concurrency/concurrency-not-magic_test.go rename to concurrency/parsing_test.go