Skip to content

Commit c6f8d48

Browse files
committed
Add single thread matrix multiplication algorithms and corresponding tests and benchmarks
Add a int set implementation and its test Add a permutation implementation making use of above int set and corresponding test
1 parent f3c7771 commit c6f8d48

File tree

12 files changed

+452
-37
lines changed

12 files changed

+452
-37
lines changed

bench/benchMatOps_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package bench
2+
3+
import (
4+
"algo/math"
5+
"testhelper"
6+
"testing"
7+
)
8+
9+
const test_size = 500
10+
11+
/*
12+
Test result when ma and mb are both 5000 x 5000 matrix
13+
goos: windows
14+
goarch: amd64
15+
BenchmarkMatMulSlow-8 1 2114916672000 ns/op
16+
BenchmarkMatMulTiling16-8 1 362979711200 ns/op
17+
BenchmarkMatMulTiling24-8 1 373598301500 ns/op
18+
BenchmarkMatMulTiling32-8 1 376010471000 ns/op
19+
*/
20+
21+
func BenchmarkMatMulSlow(b *testing.B) {
22+
ma := testhelper.GenMat(test_size, test_size, 1)
23+
mb := testhelper.GenMat(test_size, test_size, 2)
24+
b.ResetTimer()
25+
for i := 0; i < b.N; i++ {
26+
math.MatmulSlow(ma, mb)
27+
}
28+
}
29+
30+
func BenchmarkMatMulTiling16(b *testing.B) {
31+
ma := testhelper.GenMat(test_size, test_size, 1)
32+
mb := testhelper.GenMat(test_size, test_size, 2)
33+
b.ResetTimer()
34+
for i := 0; i < b.N; i++ {
35+
math.MatmulTiling(ma, mb, 16)
36+
}
37+
}
38+
39+
func BenchmarkMatMulTiling24(b *testing.B) {
40+
ma := testhelper.GenMat(test_size, test_size, 1)
41+
mb := testhelper.GenMat(test_size, test_size, 2)
42+
b.ResetTimer()
43+
for i := 0; i < b.N; i++ {
44+
math.MatmulTiling(ma, mb, 24)
45+
}
46+
}
47+
48+
func BenchmarkMatMulTiling32(b *testing.B) {
49+
ma := testhelper.GenMat(test_size, test_size, 1)
50+
mb := testhelper.GenMat(test_size, test_size, 2)
51+
b.ResetTimer()
52+
for i := 0; i < b.N; i++ {
53+
math.MatmulTiling(ma, mb, 32)
54+
}
55+
}

src/algo/back_track/permutation.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package back_track
2+
3+
import "data"
4+
5+
func Permutation(ary []int) [][]int {
6+
is := data.IntSet{}
7+
is.Init(ary)
8+
ret := make([][]int, 0)
9+
10+
var next func([]int)
11+
next = func(r []int) {
12+
if is.Size() == 1 {
13+
r = append(r, is.Get())
14+
ret = append(ret, r)
15+
return
16+
}
17+
s := is.Size()
18+
for i := 0; i < s; i++ {
19+
x := is.Get()
20+
is.Delete()
21+
entry := make([]int, len(r)+1)
22+
copy(entry, r)
23+
entry[len(r)] = x
24+
next(entry)
25+
is.Insert(x)
26+
}
27+
}
28+
next([]int{})
29+
return ret
30+
}

src/algo/math/matrix.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package math
2+
3+
// const tiling_size = 32
4+
// 32^2 = 1024, my cpu's l1 cache could store about 6400 int,
5+
// need store 3 matrix at the same time, so it should be a reasonable number
6+
7+
// will not do error handling as it is not an actual ready-to-use function
8+
func MatmulSlow(ma, mb [][]int) [][]int {
9+
if len(ma) == 0 || len(mb) == 0 || len(ma[0]) != len(mb) {
10+
return nil
11+
}
12+
13+
ret := make([][]int, len(ma))
14+
for i := 0; i < len(ma); i++ {
15+
ret[i] = make([]int, len(mb[0]))
16+
for j := 0; j < len(mb[0]); j++ {
17+
for k := 0; k < len(mb); k++ {
18+
ret[i][j] += ma[i][k] * mb[k][j]
19+
}
20+
}
21+
}
22+
return ret
23+
}
24+
25+
func MatmulTiling(ma, mb [][]int, t int) [][]int {
26+
if len(ma) == 0 || len(mb) == 0 || len(ma[0]) != len(mb) {
27+
return nil
28+
}
29+
30+
ret := make([][]int, len(ma))
31+
for i := 0; i < len(ret); i++ {
32+
ret[i] = make([]int, len(mb[0]))
33+
}
34+
for i := 0; i < len(ret); i += t {
35+
for j := 0; j < len(mb[0]); j += t {
36+
for k := 0; k < len(mb); k += t {
37+
for x := 0; x < t && (i+x) < len(ma); x++ {
38+
for y := 0; y < t && (j+y) < len(mb[0]); y++ {
39+
for z := 0; z < t && (k+z) < len(mb); z++ {
40+
ret[i+x][j+y] += ma[i+x][k+z] * mb[k+z][j+y]
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}
47+
return ret
48+
}

src/algo/parallel/basic.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,8 @@
11
package parallel
22

3-
import "sync"
4-
53
// basic parallel algorithms, scan and reduce
64
// restrict to int operation to avoid using []interface{}
75

8-
type AtomicInt struct {
9-
n int
10-
lock *sync.Mutex
11-
}
12-
13-
func (x *AtomicInt) Init(n int) {
14-
x.n = n
15-
x.lock = &sync.Mutex{}
16-
}
17-
18-
func (x *AtomicInt) Inc() {
19-
x.lock.Lock()
20-
x.n++
21-
x.lock.Unlock()
22-
}
23-
24-
func (x *AtomicInt) Set(n int) {
25-
x.lock.Lock()
26-
x.n = n
27-
x.lock.Unlock()
28-
}
29-
30-
func (x *AtomicInt) Dec() {
31-
x.lock.Lock()
32-
x.n++
33-
x.lock.Unlock()
34-
}
35-
36-
func (x *AtomicInt) Get() int {
37-
x.lock.Lock()
38-
v := x.n
39-
x.lock.Unlock()
40-
return v
41-
}
42-
436
const parallelThreshold = 2048
447

458
func Reduce(ary []int, f func(int, int) int, init int) int {

src/data/set.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package data
2+
3+
type elem struct {
4+
X int
5+
Prev, Next *elem
6+
}
7+
8+
// implemented with double linked cycle list and hash map
9+
// Constant time add, remove, check
10+
// Iterable, and able to insert and delete easily while iterating
11+
type IntSet struct {
12+
hm map[int]*elem
13+
head, curr *elem
14+
}
15+
16+
func (s *IntSet) Init(ary []int) {
17+
s.hm = make(map[int]*elem)
18+
if ary == nil {
19+
return
20+
}
21+
for _, n := range ary {
22+
s.Add(n)
23+
}
24+
}
25+
26+
func (s *IntSet) Add(n int) {
27+
if _, ok := s.hm[n]; !ok {
28+
nn := &elem{
29+
X: n,
30+
Prev: nil,
31+
Next: nil,
32+
}
33+
s.hm[n] = nn
34+
if s.head == nil {
35+
s.head = nn
36+
nn.Prev = nn
37+
nn.Next = nn
38+
} else {
39+
s.head.Prev.Next = nn
40+
nn.Prev = s.head.Prev
41+
nn.Next = s.head
42+
s.head.Prev = nn
43+
}
44+
}
45+
}
46+
47+
func (s *IntSet) Contains(n int) bool {
48+
_, ok := s.hm[n]
49+
return ok
50+
}
51+
52+
func (s *IntSet) Remove(n int) {
53+
e, ok := s.hm[n]
54+
if !ok {
55+
return
56+
}
57+
if e.Next == e {
58+
s.head = nil
59+
s.curr = nil
60+
} else {
61+
if s.head == e {
62+
s.head = e.Next
63+
}
64+
e.Next.Prev = e.Prev
65+
e.Prev.Next = e.Next
66+
}
67+
delete(s.hm, n)
68+
}
69+
70+
func (s *IntSet) Get() int {
71+
if s.curr == nil {
72+
s.curr = s.head
73+
}
74+
return s.curr.X
75+
}
76+
77+
// return whether next s.Get() call is still within the same iteration
78+
func (s *IntSet) Next() bool {
79+
if s.curr == nil {
80+
s.curr = s.head
81+
}
82+
s.curr = s.curr.Next
83+
return s.curr != s.head
84+
}
85+
86+
// insert before s.curr
87+
func (s *IntSet) Insert(n int) {
88+
if _, ok := s.hm[n]; !ok && s.curr != nil {
89+
nn := &elem{
90+
X: n,
91+
Prev: s.curr.Prev,
92+
Next: s.curr,
93+
}
94+
s.hm[n] = nn
95+
s.curr.Prev.Next = nn
96+
s.curr.Prev = nn
97+
}
98+
}
99+
100+
// delete current element under s.curr and move it to next elem
101+
func (s *IntSet) Delete() {
102+
if s.curr == nil {
103+
return
104+
}
105+
v := s.curr.X
106+
s.curr = s.curr.Next
107+
s.Remove(v)
108+
}
109+
110+
func (s *IntSet) Size() int {
111+
return len(s.hm)
112+
}

src/data/sync.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package data
2+
3+
import "sync"
4+
5+
type AtomicInt struct {
6+
n int
7+
lock *sync.Mutex
8+
}
9+
10+
func (x *AtomicInt) Init(n int) {
11+
x.n = n
12+
x.lock = &sync.Mutex{}
13+
}
14+
15+
func (x *AtomicInt) Inc() {
16+
x.lock.Lock()
17+
x.n++
18+
x.lock.Unlock()
19+
}
20+
21+
func (x *AtomicInt) Set(n int) {
22+
x.lock.Lock()
23+
x.n = n
24+
x.lock.Unlock()
25+
}
26+
27+
func (x *AtomicInt) Dec() {
28+
x.lock.Lock()
29+
x.n++
30+
x.lock.Unlock()
31+
}
32+
33+
func (x *AtomicInt) Get() int {
34+
x.lock.Lock()
35+
v := x.n
36+
x.lock.Unlock()
37+
return v
38+
}

src/testhelper/testhelper.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,23 @@ func BigInput(n int) []int {
7070
func Plus(a, b int) int {
7171
return a + b
7272
}
73+
74+
func GenMat(m, n, fill int) [][]int {
75+
ret := make([][]int, m)
76+
for i := 0; i < m; i++ {
77+
ret[i] = make([]int, n)
78+
for j := 0; j < n; j++ {
79+
ret[i][j] = fill
80+
}
81+
}
82+
return ret
83+
}
84+
85+
func GenEyeMat(m int) [][]int {
86+
ret := make([][]int, m)
87+
for i := 0; i < m; i++ {
88+
ret[i] = make([]int, m)
89+
ret[i][i] = 1
90+
}
91+
return ret
92+
}

0 commit comments

Comments
 (0)