2013-05-25 6 views
15

Ho provato Go da un po 'di tempo e questa domanda continua a infastidirmi. Supponiamo che io costruisca un dataset piuttosto grande in una porzione (ad esempio, 10 milioni di int64).Does Go non ha un modo reale per ridurre una fetta? È un problema?

package main 

import (
    "math" 
    "fmt" 
) 

func main() { 
    var a []int64 
    var i int64; 
    upto := int64(math.Pow10(7)) 
    for i = 0; i < upto; i++ { 
     a = append(a, i) 
    } 
    fmt.Println(cap(a)) 
} 

Ma poi decido che non voglio la maggior parte di loro in modo che voglio finire con una fetta di appena il 10 di quelli. Ho provato entrambe le sezioni e delete techniques on Go's wiki ma nessuno di loro sembra ridurre la capacità della sezione.

Quindi questa è la mia domanda: fa Go ha nessun vero modo di contrazione della capacità di una fetta che sarebbe simile a realloc() -ing con un argomento taglia più piccola rispetto a chiamata precedente sullo stesso puntatore in C? È un problema e come si dovrebbe affrontarlo?

risposta

16

Per eseguire una, in effetti, un realloc di una fetta:

a = append([]T(nil), a[:newSize]...) // Thanks to @Dijkstra for pointing out the missing ellipsis. 

Se lo fa una copia di newSize elementi per un nuovo posto di memoria o se lo fa un vero e proprio in atto ridimensionare come in realloc (3) è a completa discrezione del compilatore. Potresti voler indagare sullo stato attuale e magari aumentare un issue se c'è un margine di miglioramento in questo.

Tuttavia, questa è probabilmente una micro-ottimizzazione. La prima fonte di miglioramenti delle prestazioni risiede quasi sempre nella selezione di un algoritmo migliore e/o di una migliore struttura dei dati. L'uso di un vettore di dimensioni enormi per mantenere solo alcuni elementi non è probabilmente l'opzione migliore per il consumo di memoria.

MODIFICA: Quanto sopra è solo parzialmente corretto. Il compilatore non può, nel caso generale, derivare se ci sono altri puntatori all'array di supporto della slice. Quindi il realloc non è applicabile. Lo snippet sopra riportato è in realtà garantito per eseguire una copia di elementi 'newSize'. Ci scusiamo per eventuali confusione eventualmente create.

7

Go non ha un modo per restringere le fette. Questo non è un problema nella maggior parte dei casi, ma se si profila la memoria e si scopre che si sta utilizzando troppo, è possibile fare qualcosa al riguardo:

In primo luogo, è sufficiente creare una porzione delle dimensioni necessarie e copia i tuoi dati in esso. Il garbage collector libererà quindi la grande fetta. Copy built-in

In secondo luogo, è possibile riutilizzare la fetta grande ogni volta che si desidera generarlo, in modo da non assegnarlo mai più di una volta.

In una nota finale, è possibile utilizzare 1e7 anziché math.Pow10(7).

3

Inoltre è possibile riutilizzare la maggior parte della memoria allocata durante il lavoro di tuo app, date un'occhiata a: bufs package

PS se si ri-alocate nuova memoria per i più piccoli fetta, memoria vecchio potrebbero non essere liberato nello stesso tempo, sarà liberato quando il garbage collector decide di farlo.

1

Vediamo questo esempio:

func main() { 
    s := []string{"A", "B", "C", "D", "E", "F", "G", "H"} 
    fmt.Println(s, len(s), cap(s)) // slice, length, capacity 

    t := s[2:4] 
    fmt.Println(t, len(t), cap(t)) 

    u := make([]string, len(t)) 
    copy(u, t) 
    fmt.Println(u, len(u), cap(u)) 
} 

produce il seguente risultato:

[A B C D E F G H] 8 8 
[C D] 2 6 
[C D] 2 2 

s è una fetta che contiene 8 pezzi di stringhe. t è una sezione che mantiene la parte [C D]. La lunghezza di t è 2, ma poiché utilizza lo stesso array nascosto di s, la sua capacità è 6 (da "C" a "H"). La domanda è: come avere una porzione di [C D] indipendente dall'array nascosto di s? Basta creare una nuova porzione di stringhe con lunghezza 2 (fetta u) e copiare il contenuto di t a u. L'array nascosto sottostante di u è diverso dall'array nascosto di s.

Il problema iniziale era questo: si dispone di una grande fetta e si crea una nuova fetta più piccola su di esso. Poiché la porzione più piccola utilizza lo stesso array nascosto, il garbage collector non eliminerà l'array nascosto.

Vedere la parte inferiore di questo post per ulteriori informazioni: http://blog.golang.org/go-slices-usage-and-internals.