2015-07-22 5 views
10

Wie bekannt zu simulieren, geht keinen Union-Typen hat, und soll nur über die Schnittstelle simuliert werden.golang: wie Union-Typen effizient

Ich versuche, zwei Methoden, um die Vereinigung zu simulieren, aber das Ergebnis ist bei weitem nicht gut, wie C.

package main 

import (
    "fmt" 
    "time" 
) 

type U interface { 
    i32() int32 
    i16() int16 
} 

type i32 int32 

func (u i32) i32() int32 { 
    return int32(u) 
} 

func (u i32) i16() int16 { 
    return int16(u) 
} 

type i16 int16 

func (u i16) i32() int32 { 
    return int32(u) 
} 

func (u i16) i16() int16 { 
    return int16(u) 
} 

func test() (total int64) { 
    type A struct { 
     t int32 
     u interface{} 
    } 
    a := [...]A{{1, int32(100)}, {2, int16(3)}} 

    for i := 0; i < 5000000000; i++ { 
     p := &a[i%2] 
     switch p.t { 
     case 1: 
      total += int64(p.u.(int32)) 
     case 2: 
      total += int64(p.u.(int16)) 
     } 
    } 
    return 
} 

func test2() (total int64) { 
    type A struct { 
     t int32 
     u U 
    } 
    a := [...]A{{1, i32(100)}, {2, i16(3)}} 

    for i := 0; i < 5000000000; i++ { 
     p := &a[i%2] 
     switch p.t { 
     case 1: 
      total += int64(p.u.i32()) 
     case 2: 
      total += int64(p.u.i16()) 
     } 
    } 
    return 
} 

type testfn func() int64 

func run(f testfn) { 
    ts := time.Now() 
    total := f() 
    te := time.Now() 
    fmt.Println(total) 
    fmt.Println(te.Sub(ts)) 
} 

func main() { 
    run(test) 
    run(test2) 
} 

Ergebnis:

257500000000 
1m23.508223094s 
257500000000 
34.95081661s 

Verfahren Weg ist besser, und der Typ-Cast Weg kostet mehr CPU-Zeit.

Die C-Version:

#include <stdio.h> 

struct A { 
    int t; 
    union { 
     int i; 
     short v; 
    } u; 
}; 

long test() 
{ 
    struct A a[2]; 
    a[0].t = 1; 
    a[0].u.i = 100; 
    a[1].t = 2; 
    a[1].u.v = 3; 

    long total = 0; 
    long i; 
    for (i = 0; i < 5000000000; i++) { 
     struct A* p = &a[i % 2]; 
     switch(p->t) { 
     case 1: 
      total += p->u.i; 
      break; 
     case 2: 
      total += p->u.v; 
      break; 
     } 
    } 
    return total; 
} 
int main() 
{ 
    long total = test(); 
    printf("%ld\n", total); 
} 

Ergebnis:

257500000000 

real 0m5.620s 
user 0m5.620s 
sys 0m0.000s 

Der Union-Typ ist für viele Anwendungen nützlich, zum Beispiel Das Netzwerkprotokoll kann einen konkreten Variantentyp enthalten. Also vielleicht der Zugang von Union Daten kann der Engpass der Anwendung geworden.

jemand helfen könnte? Vielen Dank.

Antwort

6

Sie können Arrays verwenden eine einzige int32 als zwei int16 s zu vertreten und montieren sie dann mit Verschiebungen as Rob Pike recommends:

func test3() (total int64) { 
    type A struct { 
     t int32 
     u [2]int16 
    } 
    a := [...]A{ 
     {1, [2]int16{100, 0}}, 
     {2, [2]int16{3, 0}}, 
    } 

    for i := 0; i < N; i++ { 
     p := &a[i%2] 
     switch p.t { 
     case 1: 
      total += int64(p.u[0]<<0 | p.u[1]<<8) 
     case 2: 
      total += int64(p.u[0]) 
     } 
    } 
    return 
} 

Mit dem Original Go-Compiler es etwa 2-mal langsamer als die C-Version läuft, und mit gccgo (O3) läuft etwa so schnell wie C.

gewarnt aber, dass dieser Ansatz little-endian ints annimmt. Sie müssen die Reihenfolge der Verschiebungen für die Big-Endian-Architektur ändern. Auch wenn Sie

, müssen Strukturen von einer Byte Scheibe dekodieren, sollten Sie wirklich encoding/binary werden. Diese Bibliothek wird erstellt, um zwischen Bytefolgen und anderen Typen zu übersetzen.

+1

Ja, kann es verbessern kann die Leistung, aber manchmal ist der Variantentyp andere Arten umfasst, z.B. String- oder Byte-Array. – kingluo

+0

Siehe Bearbeiten. Ich habe die Verwendung von 'unsicher 'entfernt, da es nicht wirklich benötigt wird, und eine Notiz über andere Typen hinzugefügt. –

+1

Danke. Ich probiere die binären Funktionen aus, sie scheinen sehr teuer zu sein. Da die Vereinigung nicht beabsichtigt ist, ein Drahtformat zu sein, ist eine binäre Darstellung für numerische Typen nicht notwendig. Ich denke, in meinem Fall macht das mehr Sinn. – kingluo

2

Die Union kann numerische Typen und Octet-Zeichenfolge enthalten, daher versuche ich, Byte-Slice als Wertcontainer zu verwenden und unsafe.Pointer zu verwenden, um entsprechend dem konkreten Typ darauf zuzugreifen.

func test3() (total int64) { 
    type A struct { 
     t int32 
     u []byte 
    } 

    a := [...]A{{1, make([]byte, 8)}, {2, make([]byte, 8)}} 
    *(*int32)(unsafe.Pointer(&a[0].u)) = 100 
    *(*int16)(unsafe.Pointer(&a[1].u)) = 3 

    for i := 0; i < 5000000000; i++ { 
     p := &a[i%2] 
     switch p.t { 
     case 1: 
      total += int64(*(*int32)(unsafe.Pointer(&p.u))) 
     case 2: 
      total += int64(*(*int16)(unsafe.Pointer(&p.u))) 
     } 
    } 
    return 
} 

Ergebnis:

$ go run union.go 
257500000000 
12.844752701s 

$ go run -compiler gccgo -gccgoflags -O3 union.go 
257500000000 
6.640667s 

Ist es die beste Version?

+0

Die Verwendung von Arrays (oder Zeigern zu Arrays) anstelle von Slices könnte theoretisch die Leistung verbessern. –

+0

Ich versuche Byte-Array, aber die Leistung ist fast gleich. Wie auch immer, in meinem Fall ist die Oktett-Zeichenfolge in variabler Länge, so dass Byte-Slice die einzige Wahl sein sollte. – kingluo