Как выделить память для карты, указывающей на срез в golang - PullRequest
0 голосов
/ 02 октября 2018

Есть ли способ выделить память карты, которая имеет не более Nmax ключей, указывающих на фрагмент максимальной длины Nmax?

В настоящее время я просто указываю максимальное количество ключей через make(map[int][]int,Nmax), но я не уверен, как сказать Go, что каждый фрагмент будет иметь максимальную длину Nmax, потому что я не знаю, чтоключи будут apriori.

По сути, у меня есть несколько сайтов с целым населением.Я использую карту, чтобы отслеживать, сколько сайтов имеет данное население N.Узким местом в моей программе является runtime.memmove, которое, как я предполагаю, происходит из-за постоянного изменения размера среза, на который указывает карта.

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Так что, учитывая, что ваше описание проблемы действительно довольно расплывчато, я просто начну с того, как мне нужно «управлять» картой.Для простоты я собираюсь обернуть всю логику в функции приемника, поэтому оборачиваем карту в пользовательский тип:

type dataMap struct {
    data map[int][]int
    nmax int
}

func New(Nmax int) *dataMap {
    return &dataMap{
        data: make(map[int][]int, Nmax),
        nmax: Nmax,
    }
}

// Get - return slice for given key
func (d dataMap) Get(k int) []int {
    s, ok := d.data[k]
    if !ok {
        return nil // optionally return error
    }
    return s
}

// Set - set/append values to a given key - this is not safe for concurrent use
// if that's needed, add a RWMutex to the type
func (d *dataMap) Set(k int, vals ...int) error {
    s, ok := d.data[k]
    if !ok {
        s = make([]int, 0, d.nmax) // allocate slice of given length
    }
    // optionally check for nil-values + ensure we're not exceeding the nmax
    checked := make([]int, 0, len(vals))
    for i := range vals {
        if vals[i] != 0 {
            checked = append(checked, vals[i])
        }
    }
    if len(s) + len(checked) > d.nmax {
        return errors.New("max capacity exceeded")
    }
    s = append(s, checked...) // append values
    d.data[k] = s // update map
    return nil
}

Это сокращает ненужные вызовы (пере) выделения памяти,Это также гарантирует, что я могу получить длину любого среза на карте в операции O (1), не беспокоясь о значениях nil:

myData := New(10)
fmt.Println(myData.Set(4, 1, 2, 3, 4))
fmt.Println(len(myData.Get(4))) // 4
fmt.Println(cap(myData.Get(4))) // 10
// nil-values are filtered out
myData.Set(4, 5, 6, 7, 0, 0, 0, 0)
fmt.Println(len(myData.Get(4))) // 7
fmt.Println(cap(myData.Get(4))) // 10
// exceeding capacity of 10
fmt.Println(myData.Set(4, 8, 9, 10, 11)) // max capacity exceeded

работает демо


Вы могли бы управлять емкостью, используя массив вместо среза, но для этого необходимо вручную отслеживать индекс / смещение, с которого вы хотите начать добавление значений.Вообще говоря, вы не используете массивы в golang, чтобы в очень, очень специфических случаях.В этом случае я бы просто выбрал кусок с установленной крышкойПреимущество этого в том, что вы можете, например, иметь кусочки разной длины.Результат также очень легко проверить, потому что подобный тип вполне пригоден для замены его типом интерфейса

type DataContainer interface {
    Get(k int) []int
    Set(k int, vals ...int) error
    Declare(k, capacity int) error // error if k is already in use?
}
0 голосов
/ 02 октября 2018

Ваше описание вашей проблемы расплывчато, и вы не предоставили код, иллюстрирующий вашу проблему.

Если емкость фрагмента карты равна нулю, тогда установите его на Nmax.Например,

package main

import "fmt"

func main() {
    Nmax := 42

    m := make(map[int][]int, Nmax)

    k, e := 7, 11
    v := m[k]
    if cap(v) == 0 {
        v = make([]int, 0, Nmax)
    }
    m[k] = append(v, e)

    v = m[k]
    fmt.Println(k, len(v), cap(v), v)

    fmt.Println(m)
}

Детская площадка: https://play.golang.org/p/csoUCUvVDAp

Вывод:

7 1 42 [11]
map[7:[11]]
...