go 간단한 리스트

2020/06/25

이미 다양한 자료구조들을 지원하고 있지만 연습삼아 만들어 본다.

go는 gc가 되는데다가 함수 내에서 새 할당을 하면 알아서 힙에서 뽑아다 준다. 이는 추후 gc에서 제거될 것인데 생명주기를 잘 생각해봐야겠다. 런타임 명시적 GC가 필요할 수도 있고 단순히 nil 처리만 해서 go에서 맡길 수도 있을 것 같다. nil처리 하지 않으면 어찌 될 것인가? gc가 제거하지 못하고 런타임 내내 메모리 릭이 될 것인가? 아직은 잘 모르겠다. 예컨데 구조체의 자식에 다른 구조체가 포인팅 되어 있다. 부모 구조체를 nil 처리해서 gc가 잡아 지웠다. 자식은? 아마도 쫒아다니며 nil 처리를 해야 하지 않을런지. 데몬형 SW라면 이 구조체의 생명주기를 go가 판단할 방법이 없다. (데몬형이 아니라면 종료 시점을 죽음으로 보면 될 것이다) 방법이 완전 없는 것은 아니다. gc가 자식들을 다 찾아다니도록 go가 만들어져있다면 가능하겠다. go는 대머리가 팀원(=천재)인 어벤저스 팀이 만든 언어이므로 가능할지도 모른다. 이 메커니즘을 뭘로 검색해야 할 지 모르겠다. 그러니까 영어를 공부하자. 기승전영어.

키 쫑 검사는 하지 않는 단방향 리스트다.


내용 추가한다.

코드 하단부에 reset()를 넣었다. 얘는 연결 리스트를 통으로 지우는데 체인 족보를 다 훑어서 nil처리 하지 않는다. 걍 대가리 꼬리만 nil처리했다. gc 상황을 보면 부모를 nil 하는 것만으로 족보를 모두 gc하고 있다. go는 대단하다. 역시 팀원 중에 대머리가 있으면 짱이다.

go build ll.go

GODEBUG="gctrace=1" ./ll

package main

import (
	"os"
	"runtime/debug"
	"fmt"
	"runtime"
	
)

type item struct {

	next *item
	k string
	v string
}

var head *item = nil
var tail *item = nil

func add(k string, v string) {

	if head == nil {
	
		head = &item{nil, k, v}
		tail = head
		return
	
	}
	
	new_item := item{nil, k, v}
	tail.next = &new_item
	tail = tail.next

}
func del(k string) {

	pos := head
	prev := pos
	
	for {
		if pos == nil {
			return
			
		} else {
		
			if pos.k == k {
			
				if pos == head {
					head = pos.next
				}
			
				if pos == tail {
					tail = prev
				}
				
							
				prev.next = pos.next
				pos = nil
//				pos.next = nil
				return 
			}
			prev = pos
			pos = pos.next
			
		}

	}
		

}

func all() {

	pos := head
	
	for {
		if pos == nil {
			return
		}
		
		println(pos.k, pos.v)
		pos = pos.next
	}

}


func reset() {

	head = nil
	tail = nil

}



func get(k string) (v interface{}) {

	pos := head
	
	for {
		if pos == nil {
			v = nil
			return
			
		} else {
		
			if pos.k == k {
				v = pos.v
				return 
			}
			
			pos = pos.next
			
		}

	}

}


func main() {

	println("hell")
	add("eee", "ddd")

	println("eee 찾기")
	
	a := get("eee")
	
	if a != nil {
		println("찾았다", a.(string))
	} else {
		println("없다 없어1")
	}
	
	println("전체 지움1111")
	reset()

	a = get("eee")
	
	if a != nil {
		println("찾았다", a.(string))
	} else {
		println("없다 없어2")
	}
	
	a = get("eee")
	
	if a != nil {
		println("찾았다", a.(string))
	} else {
		println("없다 없어3")
	}
	

	add("a", "1")
	add("b", "2")
	
	println("전체 순회")
	
	println("마지막 지움")
	del("b")

	a = get("b")
	
	if a != nil {
		println("찾았다", a.(string))
	} else {
		println("없다 없어2222")
	}	
	
	a = get("a")
	
	if a != nil {
		println("찾았다", a.(string))
	} else {
		println("없다 없어")
	}		
	
	
	del("a")
	
	
	add("c", "3")
	
	a = get("c")
	
	if a != nil {
		println("찾았다", a.(string))
	} else {
		println("없다 없어")
	}		
	
	
	add("d", "4")
	add("e", "5")
	add("f", "6")
	
	del("e")
	
	
	all()
	
	del("f")
	
	println("---")
	
	all()
	
	del("c")
	println("---")
	all()
	
	
	add("g", "6")
	add("v", "7")
	add("m", "8")
	
	println("---")
	all()
	
	del("v")
	
	println("---11")
	all()
				
	
//	del("eee")

	for i := 0; i < 1000000; i++ {
		add(fmt.Sprintf("하하하%d", i), fmt.Sprintf("히히히히 %d", i * 2))
	}
    debug.FreeOSMemory()
    println("------------------------1")
	for i := 5000; i < 1000000; i++ {
		del(fmt.Sprintf("하하하%d", i))
	}
	reset()
	runtime.GC()	
	debug.FreeOSMemory()
	println("------------------------2")
		
	runtime.GC()	
	debug.FreeOSMemory()
	
	println("------------------------3")
	
	println(get("하하하10").(string))
	
	os.Exit(0)
}

reset 안 하면

scvg-1: inuse: 97, idle: 0, sys: 97, released: 0, consumed: 97 (MB)
gc 8 @0.479s 4%: 0.001+54+0.11 ms clock, 0.015+0/41/13+0.95 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
gc 9 @0.537s 5%: 0.002+56+0.096 ms clock, 0.017+0/56/0.15+0.76 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
scvg-1: 0 MB released
scvg-1: inuse: 97, idle: 0, sys: 97, released: 0, consumed: 97 (MB)
------------------------2
gc 10 @0.597s 5%: 0.016+55+0.095 ms clock, 0.13+0/38/16+0.76 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
gc 11 @0.655s 6%: 0.002+56+0.095 ms clock, 0.017+0/56/0.15+0.76 ms cpu, 91->91->91 MB, 183 MB goal, 8 P (forced)
scvg-1: 0 MB released
scvg-1: inuse: 97, idle: 0, sys: 97, released: 0, consumed: 97 (MB)
------------------------3

reset 하면

gc 7 @0.331s 4%: 0.005+55+0.10 ms clock, 0.043+0/55/0.39+0.84 ms cpu, 93->93->91 MB, 173 MB goal, 8 P (forced)
scvg-1: 1 MB released
scvg-1: inuse: 98, idle: 1, sys: 99, released: 1, consumed: 98 (MB)
gc 8 @0.390s 4%: 0.002+0.12+0.10 ms clock, 0.019+0/0.12/0.12+0.86 ms cpu, 91->91->0 MB, 183 MB goal, 8 P (forced)
gc 9 @0.394s 4%: 0.002+0.045+0.096 ms clock, 0.023+0/0.067/0+0.77 ms cpu, 0->0->0 MB, 4 MB goal, 8 P (forced)
scvg-1: 99 MB released
scvg-1: inuse: 0, idle: 99, sys: 99, released: 99, consumed: 0 (MB)
------------------------2
gc 10 @0.398s 4%: 0.001+0.10+0.056 ms clock, 0.014+0/0.11/0.086+0.45 ms cpu, 0->0->0 MB, 4 MB goal, 8 P (forced)
gc 11 @0.398s 4%: 0.024+0.044+0.017 ms clock, 0.19+0/0.035/0.051+0.14 ms cpu, 0->0->0 MB, 4 MB goal, 8 P (forced)
scvg-1: inuse: 0, idle: 99, sys: 99, released: 99, consumed: 0 (MB)

심지어는 nil 처리 족보 훑는다고 뺑이치면 뺑이친다고 느려지지만 그냥 gc에 맡기면 쑝힛툭땡하고 끝난다... 헐..