개요
구조체를 여러가지 방법으로 깊은 복사
각 복사 방법의 성능 비교 (Benchmark)
벤치마크 참고: https://dogfootman.com/14
1. 수동으로 깊은 복사
package main
import "fmt"
type Address struct {
City string
State string
}
type Student struct {
Name string
Age int
Hobbies []string
Address Address
}
func deepCopyStudent(original Student) Student {
hobbiesCopy := make([]string, len(original.Hobbies))
copy(hobbiesCopy, original.Hobbies)
return Student{
Name: original.Name,
Age: original.Age,
Hobbies: hobbiesCopy,
Address: Address{
City: original.Address.City,
State: original.Address.State,
},
}
}
func main() {
original := Student{
Name: "Alice",
Age: 21,
Hobbies: []string{"Reading", "Cycling", "Swimming"},
Address: Address{
City: "San Francisco",
State: "CA",
},
}
// deep copy
copyStudent := deepCopyStudent(original)
// update original
original.Name = "Bob"
original.Hobbies[0] = "Running"
original.Address.City = "Los Angeles"
fmt.Println("Original:", original)
fmt.Println("Copy:", copyStudent)
}
result:
Original: {Bob 21 [Running Cycling Swimming] {Los Angeles CA}}
Copy: {Alice 21 [Reading Cycling Swimming] {San Francisco CA}}
2. Json을 이용한 깊은 복사
package main
import (
"encoding/json"
"fmt"
)
type Address struct {
City string
State string
}
type Student struct {
Name string
Age int
Hobbies []string
Address Address
}
func deepCopyStudent(original Student) (Student, error) {
var copyStudent Student
data, err := json.Marshal(original)
if err != nil {
return copyStudent, err
}
err = json.Unmarshal(data, ©Student)
return copyStudent, err
}
func main() {
original := Student{
Name: "Alice",
Age: 21,
Hobbies: []string{"Reading", "Cycling", "Swimming"},
Address: Address{
City: "San Francisco",
State: "CA",
},
}
// deep copy
copyStudent, err := deepCopyStudent(original)
if err != nil {
fmt.Println("Error:", err)
return
}
// update original
original.Name = "Bob"
original.Hobbies[0] = "Running"
original.Address.City = "Los Angeles"
fmt.Println("Original:", original)
fmt.Println("Copy:", copyStudent)
}
result:
Original: {Bob 21 [Running Cycling Swimming] {Los Angeles CA}}
Copy: {Alice 21 [Reading Cycling Swimming] {San Francisco CA}}
3. 리플렉션을 이용한 깊은 복사
package main
import (
"fmt"
"reflect"
)
type Address struct {
City string
State string
}
type Student struct {
Name string
Age int
Hobbies []string
Address Address
}
func deepCopy(dst, src interface{}) {
srcVal := reflect.ValueOf(src)
dstVal := reflect.ValueOf(dst).Elem()
deepCopyValue(dstVal, srcVal)
}
func deepCopyValue(dst, src reflect.Value) {
switch src.Kind() {
case reflect.Ptr:
if !src.IsNil() {
dst.Set(reflect.New(src.Elem().Type()))
deepCopyValue(dst.Elem(), src.Elem())
}
case reflect.Struct:
for i := 0; i < src.NumField(); i++ {
deepCopyValue(dst.Field(i), src.Field(i))
}
case reflect.Slice:
if !src.IsNil() {
dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap()))
for i := 0; i < src.Len(); i++ {
deepCopyValue(dst.Index(i), src.Index(i))
}
}
default:
dst.Set(src)
}
}
func main() {
original := Student{
Name: "Alice",
Age: 21,
Hobbies: []string{"Reading", "Cycling", "Swimming"},
Address: Address{
City: "San Francisco",
State: "CA",
},
}
// deep copy
var copyStudent Student
deepCopy(©Student, original)
// update original
original.Name = "Bob"
original.Hobbies[0] = "Running"
original.Address.City = "Los Angeles"
fmt.Println("Original:", original)
fmt.Println("Copy:", copyStudent)
}
result:
Original: {Bob 21 [Running Cycling Swimming] {Los Angeles CA}}
Copy: {Alice 21 [Reading Cycling Swimming] {San Francisco CA}}
4. Gob을 이용한 깊은 복사
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Address struct {
City string
State string
}
type Student struct {
Name string
Age int
Hobbies []string
Address Address
}
func deepCopyStudent(original Student) (Student, error) {
var copyStudent Student
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
dec := gob.NewDecoder(buf)
if err := enc.Encode(original); err != nil {
return copyStudent, err
}
if err := dec.Decode(©Student); err != nil {
return copyStudent, err
}
return copyStudent, nil
}
func main() {
original := Student{
Name: "Alice",
Age: 21,
Hobbies: []string{"Reading", "Cycling", "Swimming"},
Address: Address{
City: "San Francisco",
State: "CA",
},
}
// deep copy
copyStudent, err := deepCopyStudent(original)
if err != nil {
fmt.Println("Error:", err)
return
}
// update original
original.Name = "Bob"
original.Hobbies[0] = "Running"
original.Address.City = "Los Angeles"
fmt.Println("Original:", original)
fmt.Println("Copy:", copyStudent)
}
result:
Original: {Bob 21 [Running Cycling Swimming] {Los Angeles CA}}
Copy: {Alice 21 [Reading Cycling Swimming] {San Francisco CA}}
5. 성능 비교
실행 커맨드
go test -bench="." -benchmem
실행 코드
방법 | 실행 횟수 (1초) | 평균 실행 속도 | 평균 메모리 사용량 | 메모리 할당 수 |
수동 | 35818543 | 32.92 ns/op | 48 B/op | 1 allocs/op |
리플렉션 | 4314712 | 272.6 ns/op | 232 B/op | 4 allocs/op |
JSON | 518389 | 2220 ns/op | 720 B/op | 18 allocs/op |
Gob | 71022 | 17085 ns/op | 10284 B/op | 261 allocs/op |
해당 테스트 코드에서의 속도는
수동 > 리플렉션 > JSON > Gob
의 순서로 빠르고 메모리 사용량도 적다
'[Go]' 카테고리의 다른 글
[Go] Benchmark 사용법 (How to Use Benchmarks) (0) | 2024.06.25 |
---|---|
[Go] 제네릭 사용 예 (Example of using generics) (0) | 2024.06.23 |
[Go] 고랭에서의 예외처리 (exception handling in golang) (0) | 2024.06.22 |
[Go] import cycle not allowed - 해결 방안1: interface를 활용한 해결 방안 (1) | 2024.06.18 |
[Go] import cycle not allowed - 예제 (0) | 2024.06.18 |