개요
- Go 1.18 버전부터 제네릭을 지원함
- 제네릭과 유사하게(?) 사용하기위해 빈 인터페이스를 사용했었는데, 제네릭과 직접 성능 비교
- 빈 인터페이스 방식은 실제 사용하려면 추가적인 타입 변환이 필요하기 때문에, 제네릭이 우세할 것 같음
코드
interface_queue.go
package algorithm
import "errors"
type MyQueue struct {
slice []interface{}
}
func NewQueue() *MyQueue {
return &MyQueue{
slice: make([]interface{}, 0),
}
}
func (q *MyQueue) Enqueue(val interface{}) {
q.slice = append(q.slice, val)
}
func (q *MyQueue) Dequeue() (interface{}, error) {
if len(q.slice) == 0 {
return nil, errors.New("Queue is empty")
}
val := q.slice[0]
q.slice = q.slice[1:]
return val, nil
}
func (q *MyQueue) Count() int {
return len(q.slice)
}
generic_queue.go
package algorithm
import "errors"
type GenericQueue[T any] struct {
slice []T
}
func NewGenericQueue[T any]() *GenericQueue[T] {
return &GenericQueue[T]{
slice: make([]T, 0),
}
}
func (q *GenericQueue[T]) Enqueue(val T) {
q.slice = append(q.slice, val)
}
func (q *GenericQueue[T]) Dequeue() (T, error) {
var val T
if len(q.slice) == 0 {
return val, errors.New("Queue is empty")
}
val = q.slice[0]
q.slice = q.slice[1:]
return val, nil
}
func (q *GenericQueue[T]) Count() int {
return len(q.slice)
}
queue_benchmark_test.go
package algorithm
import "testing"
func BenchmarkInterfaceQueue(b *testing.B) {
queue := NewQueue()
for i := 0; i < b.N/2; i++ {
queue.Enqueue(i)
}
queue.Count()
tempSlice := make([]int, b.N/2)
for i := 0; i < b.N/2; i++ {
val, _ := queue.Dequeue()
tempSlice[i] = val.(int)
}
}
func BenchmarkGenericQueue(b *testing.B) {
queue := NewGenericQueue[int]()
for i := 0; i < b.N/2; i++ {
queue.Enqueue(i)
}
queue.Count()
tempSlice := make([]int, b.N/2)
for i := 0; i < b.N/2; i++ {
val, _ := queue.Dequeue()
tempSlice[i] = val
}
}
출력
$ go test -bench . -benchmem
BenchmarkInterfaceQueue-8 49817638 22.46 ns/op 48 B/op 0 allocs/op
BenchmarkGenericQueue-8 258588016 6.914 ns/op 26 B/op 0 allocs/op
PASS
ok algorithm 4.300s
결론
- 제네릭을 사용하는게 시간 측면에서 약 3배, 메모리 측면에서 약 2배 우세