[t:/]$ 지식_

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에 맡기면 쑝힛툭땡하고 끝난다... 헐..









[t:/] is not "technology - root". dawnsea, rss