프로그래밍/golang

ultimate go (2)- data structure(배열/슬라이스)

나도한강뷰 2023. 1. 19. 00:31

자료구조

배열(array)

cpu 캐시

  • 메인메모리는 생각보다 느리고, 그렇기에 cpu는 처음에는 l1 l2 l3캐시를 참조한다.
  • 그리고 캐싱히트를 하는것(캐싱미스를 하지않는것)이 중요하다.
  • 그것은 prefetcher에 의해서 결정된다.
  • 64 바이트를 한번에 읽어오는 캐싱라인이라는 것을 이용한다.
  • 한번에 가져오기때문에, 연속된 데이터가 동시에 쓰이게 되면 캐싱히트가 많이 발생한다.
  • 연속된 메모리를 갖는건 go에서 slice이다.-> 성능에 좋다
  • n*n 행렬에서의 열순회/ 행순회/ 연결리스트 순회 속도차이가 발생한다. 행순회가 빠르다. 캐싱히트때문에
  • 연결리스트는 왜 성능이 중간인가?(translation lookaside buffer, 변환 색인 버퍼)

변환 색인 버퍼

  • to study

선언과 초기화

  • 길이가 5이고 원소가 문자열인 array선언
  • nil | nil | nil | nil | nil |
  • 0 | 0 | 0 | 0 | 0 |
  • 으로 선언됨
  • nil은 문자열의 메모리값, 0는 문자열의 길이
  • 실제로 값을 할당될때는 메모리값과 그 문자열의 길이가 들어가게됨
    • 그렇기때문에 복사나 선언에 비용이 별로 안들어감array 배열 반복
  • for range를 통해서 문자열을 반복할때, 문자열의 값이 전달된다. 주소값이 아닌.
  • 문자열의 길이를 알고있기때문에 stack에 할당 가능-> 힙 할당x -> gc작동 안함 -> 성능에 좋음메모리 할당 방식
  • six := [6]string{"Annie", "Betty", "Charley", "Doug", "Edward", "Hoanh"}
fmt.Printf("\n=> Contiguous memory allocations\n")
for i, v := range six {
    fmt.Printf("Value[%s]\tAddress[%p] IndexAddr[%p]\n", v, &v, &six[i])
}
=> Contiguous memory allocations
Value[Annie] Address[0xc000010250] IndexAddr[0xc000052180]
Value[Betty] Address[0xc000010250] IndexAddr[0xc000052190]
Value[Charley] Address[0xc000010250] IndexAddr[0xc0000521a0]
Value[Doug] Address[0xc000010250] IndexAddr[0xc0000521b0]
Value[Edward] Address[0xc000010250] IndexAddr[0xc0000521c0]
Value[Hoanh] Address[0xc000010250] IndexAddr[0xc0000521d0]
  • 동일한 메모리 값에 연속된 six의 요소의 값을 넣는다.
  • v의 메모리 값은 동일하고, 참조하는 six의 요소의 메모리값은 연속적으로 뒤로 밀리게 된다.

슬라이스(slice)

선언과 초기화

  • x nil | nil | nil | nil | nil |
  • 5 0 | 0 | 0 | 0 | 0 |
  • 5
  • 3가지 정보로 구성되며 첫번째 배열의 위치(주소값), 두번째 길이, 세번째 용량

길이와 용량(lenght, capacity)

  • 길이는 포인터위치부터 읽고 쓸 수 있는 요소, 용량은 배열이 존재할 수 있는 총량
  • array와 비슷해보이고, 비용도 동일하게 들지만 []string 대괄호안에 숫자가 없는게 차이점이다.

idea of appending: make slice a dynamic data structure

var data []string   / nil slice / json marshal하면 null
data := string{}    / 포인터를 갖고있는 빈 슬라이스/ json marshal하면 빈 json
  • 위에 두개는 서로 다르다.
  • 계속 append하면 처음에는 초기용량의 2배씩 할당하다가, 1000개가 넘어가면 25프로씩 증가시킨다.
  • 슬라이스에 추가하는건 기존 메모리값을 공유하는것이 아니라 새로운곳에 기존내용을 붙여넣고 새롭게 만드는 것이다.
  • 그렇기때문에 스택에 저장되게 된다.

slice of slice

slice3 := slice2[2:4]
  • 일때, 두 슬라이스는 동일한 메모리주소를 공유한다.
  • 메모리를 가르키는 헤더부분은 스택에 존재하고, 데이터는 힙에 존재한다.
  • 그렇기때문에 기존 슬라이스를 변경하더라도, 복사된 슬라이스가 영향을 받는다.
  • 슬라이스가 append로 복사되게되면 분리된다.

UTF-8

  • go의 모든것은 utf-8 문자집합을 근간으로 한다.
  • 다른 인코딩 구조를 사용하면 에러가 난다.
  • todo study

맵(map)

선언과 초기화

func main() {
    users1 := make(map[string]user)

    // 맵에 키/값 쌍을 추가한다.
    users1["Roy"] = user{"Rob", "Roy"}
    users1["Ford"] = user{"Henry", "Ford"}
    users1["Mouse"] = user{"Mickey", "Mouse"}
    users1["Jackson"] = user{"Michael", "Jackson"}

    // `map`을 순회한다.
    fmt.Printf("\n=> Iterate over map\n")
    for key, value := range users1 {
            fmt.Println(key, value)
    }
}

키삭제

delete(map, "key")

키찾기

u1, found1 := users2["Roy"]
u2, found2 := users2["Ford"]

맵의 키의 제한

  • 맵의 키를 쓸때 자료구조의 제한이 있다. 단순 비교를 통해서 boolean값을 도출할 수 있어야된다.