I've recently started learning Go, and I have to say I'm very amazed by the whole concept of Goroutines, specially coming from a NodeJS / C++ background.

But sometimes it's nice to have a variable with a dynamic type, which Go does support (aka interface{}) , then I started wondering about the performance of using it vs just using one specific type.

I wrote a simple benchmark, at first I was rather disappointed at how slow it was compared to using the direct type, then I tried using goroutines to speed it up, with both buffered and unbuffered channels, surprisingly it didn't help at all, it even slowed it down farther.

Since the benchmark is simply summing some numbers, I decided to try atomic.AddUint64(), and while it was a tiny bit slower for defined types, it was much faster using interface{}

interface_vs_type_test.go package main import ( "testing" "sync/atomic" ) func sumI(nums... interface{}) interface{} { var s uint64 for _, n := range nums { s += uint64(n.(int)) } return s } func sumT(nums... int) uint64 { var s uint64 for _, n := range nums { s += uint64(n) } return s } func BenchmarkInterface(b * testing.B) { var s uint64 for i := 0; i < b.N; i++ { s += sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64) s += sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64) s += sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64) s += sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64) } } func BenchmarkType(b * testing.B) { var s uint64 for i := 0; i < b.N; i++ { s += sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) s += sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) s += sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) s += sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) } } func BenchmarkInterfaceGoroutines(b * testing.B) { var s uint64 ch, done := make(chan uint64), make(chan bool, 4) f := func() { for i := 0; i < b.N; i++ { ch <- sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64) } } l := func() { for i := 0; i < b.N; i++ { s += <- ch } done <- true } for i := 0; i < 4; i++ { go f(); go l(); } for i := 0; i < 4; i++ { <- done } } func BenchmarkTypeGoroutines(b * testing.B) { var s uint64 ch, done := make(chan uint64), make(chan bool, 4) f := func() { for i := 0; i < b.N; i++ { ch <- sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) } } l := func() { for i := 0; i < b.N; i++ { s += <- ch } done <- true } for i := 0; i < 4; i++ { go f(); go l(); } for i := 0; i < 4; i++ { <- done } } func BenchmarkInterfaceGoroutinesBuffered(b * testing.B) { var s uint64 ch, done := make(chan uint64, b.N / 4), make(chan bool, 4) f := func() { for i := 0; i < b.N; i++ { ch <- sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64) } } l := func() { for i := 0; i < b.N; i++ { s += <- ch } done <- true } for i := 0; i < 4; i++ { go f(); go l(); } for i := 0; i < 4; i++ { <- done } } func BenchmarkTypeGoroutinesBuffered(b * testing.B) { var s uint64 ch, done := make(chan uint64, b.N / 4), make(chan bool, 4) f := func() { for i := 0; i < b.N; i++ { ch <- sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) } } l := func() { for i := 0; i < b.N; i++ { s += <- ch } done <- true } for i := 0; i < 4; i++ { go f(); go l(); } for i := 0; i < 4; i++ { <- done } } func BenchmarkInterfaceGoroutinesAtomic(b * testing.B) { var s uint64 done := make(chan bool, 4) f := func() { for i := 0; i < b.N; i++ { atomic.AddUint64(&s, sumI(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).(uint64)) } done <- true } for i := 0; i < 4; i++ { go f(); } for i := 0; i < 4; i++ { <- done } } func BenchmarkTypeGoroutinesAtomic(b * testing.B) { var s uint64 done := make(chan bool, 4) f := func() { for i := 0; i < b.N; i++ { atomic.AddUint64(&s, sumT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) } done <- true } for i := 0; i < 4; i++ { go f(); } for i := 0; i < 4; i++ { <- done } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 package main import ( "testing" "sync/atomic" ) func sumI ( nums . . . interface { } ) interface { } { var s uint64 for _ , n : = range nums { s += uint64 ( n . ( int ) ) } return s } func sumT ( nums . . . int ) uint64 { var s uint64 for _ , n : = range nums { s += uint64 ( n ) } return s } func BenchmarkInterface ( b * testing . B ) { var s uint64 for i : = 0 ; i < b . N ; i ++ { s += sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) s += sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) s += sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) s += sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) } } func BenchmarkType ( b * testing . B ) { var s uint64 for i : = 0 ; i < b . N ; i ++ { s += sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) s += sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) s += sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) s += sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) } } func BenchmarkInterfaceGoroutines ( b * testing . B ) { var s uint64 ch , done : = make ( chan uint64 ) , make ( chan bool , 4 ) f : = func ( ) { for i : = 0 ; i < b . N ; i ++ { ch < - sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) } } l : = func ( ) { for i : = 0 ; i < b . N ; i ++ { s += < - ch } done < - true } for i : = 0 ; i < 4 ; i ++ { go f ( ) ; go l ( ) ; } for i : = 0 ; i < 4 ; i ++ { < - done } } func BenchmarkTypeGoroutines ( b * testing . B ) { var s uint64 ch , done : = make ( chan uint64 ) , make ( chan bool , 4 ) f : = func ( ) { for i : = 0 ; i < b . N ; i ++ { ch < - sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) } } l : = func ( ) { for i : = 0 ; i < b . N ; i ++ { s += < - ch } done < - true } for i : = 0 ; i < 4 ; i ++ { go f ( ) ; go l ( ) ; } for i : = 0 ; i < 4 ; i ++ { < - done } } func BenchmarkInterfaceGoroutinesBuffered ( b * testing . B ) { var s uint64 ch , done : = make ( chan uint64 , b . N / 4 ) , make ( chan bool , 4 ) f : = func ( ) { for i : = 0 ; i < b . N ; i ++ { ch < - sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) } } l : = func ( ) { for i : = 0 ; i < b . N ; i ++ { s += < - ch } done < - true } for i : = 0 ; i < 4 ; i ++ { go f ( ) ; go l ( ) ; } for i : = 0 ; i < 4 ; i ++ { < - done } } func BenchmarkTypeGoroutinesBuffered ( b * testing . B ) { var s uint64 ch , done : = make ( chan uint64 , b . N / 4 ) , make ( chan bool , 4 ) f : = func ( ) { for i : = 0 ; i < b . N ; i ++ { ch < - sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) } } l : = func ( ) { for i : = 0 ; i < b . N ; i ++ { s += < - ch } done < - true } for i : = 0 ; i < 4 ; i ++ { go f ( ) ; go l ( ) ; } for i : = 0 ; i < 4 ; i ++ { < - done } } func BenchmarkInterfaceGoroutinesAtomic ( b * testing . B ) { var s uint64 done : = make ( chan bool , 4 ) f : = func ( ) { for i : = 0 ; i < b . N ; i ++ { atomic . AddUint64 ( & s , sumI ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) . ( uint64 ) ) } done < - true } for i : = 0 ; i < 4 ; i ++ { go f ( ) ; } for i : = 0 ; i < 4 ; i ++ { < - done } } func BenchmarkTypeGoroutinesAtomic ( b * testing . B ) { var s uint64 done : = make ( chan bool , 4 ) f : = func ( ) { for i : = 0 ; i < b . N ; i ++ { atomic . AddUint64 ( & s , sumT ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) ) } done < - true } for i : = 0 ; i < 4 ; i ++ { go f ( ) ; } for i : = 0 ; i < 4 ; i ++ { < - done } }

The Results :