github.com/goccy/go-reflect@v1.2.1-0.20220925055700-4646ad15ec8a/benchmark_marshaler_test.go (about)

     1  package reflect_test
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"sync"
     7  	"testing"
     8  	"unsafe"
     9  
    10  	"github.com/goccy/go-reflect"
    11  )
    12  
    13  var (
    14  	typeToEncoderMap sync.Map
    15  	bufpool          = sync.Pool{
    16  		New: func() interface{} {
    17  			return &buffer{
    18  				b: make([]byte, 0, 1024),
    19  			}
    20  		},
    21  	}
    22  )
    23  
    24  type buffer struct {
    25  	b []byte
    26  }
    27  
    28  type encoder func(*buffer, unsafe.Pointer) error
    29  
    30  func Marshal(v interface{}) ([]byte, error) {
    31  
    32  	// Technique 1.
    33  	// Get type information and pointer from interface{} value without allocation.
    34  	typ, ptr := reflect.TypeAndPtrOf(v)
    35  	typeID := reflect.TypeID(v)
    36  
    37  	// Technique 2.
    38  	// Reuse the buffer once allocated using sync.Pool
    39  	buf := bufpool.Get().(*buffer)
    40  	buf.b = buf.b[:0]
    41  	defer bufpool.Put(buf)
    42  
    43  	// Technique 3.
    44  	// builds a optimized path by typeID and caches it
    45  	if enc, ok := typeToEncoderMap.Load(typeID); ok {
    46  		if err := enc.(encoder)(buf, ptr); err != nil {
    47  			return nil, err
    48  		}
    49  
    50  		// allocate a new buffer required length only
    51  		b := make([]byte, len(buf.b))
    52  		copy(b, buf.b)
    53  		return b, nil
    54  	}
    55  
    56  	// First time,
    57  	// builds a optimized path by type and caches it with typeID.
    58  	enc, err := compile(typ)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	typeToEncoderMap.Store(typeID, enc)
    63  	if err := enc(buf, ptr); err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	// allocate a new buffer required length only
    68  	b := make([]byte, len(buf.b))
    69  	copy(b, buf.b)
    70  	return b, nil
    71  }
    72  
    73  func compile(typ reflect.Type) (encoder, error) {
    74  	switch typ.Kind() {
    75  	case reflect.Struct:
    76  		return compileStruct(typ)
    77  	case reflect.Int:
    78  		return compileInt(typ)
    79  	}
    80  	return nil, errors.New("unsupported type")
    81  }
    82  
    83  func compileStruct(typ reflect.Type) (encoder, error) {
    84  
    85  	encoders := []encoder{}
    86  
    87  	for i := 0; i < typ.NumField(); i++ {
    88  		field := typ.Field(i)
    89  		enc, err := compile(field.Type)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  		offset := field.Offset
    94  		encoders = append(encoders, func(buf *buffer, p unsafe.Pointer) error {
    95  			return enc(buf, unsafe.Pointer(uintptr(p)+offset))
    96  		})
    97  	}
    98  	return func(buf *buffer, p unsafe.Pointer) error {
    99  		buf.b = append(buf.b, '{')
   100  		for i, enc := range encoders {
   101  			if i != 0 {
   102  				buf.b = append(buf.b, ' ')
   103  			}
   104  			if err := enc(buf, p); err != nil {
   105  				return err
   106  			}
   107  		}
   108  		buf.b = append(buf.b, '}')
   109  		return nil
   110  	}, nil
   111  }
   112  
   113  func compileInt(typ reflect.Type) (encoder, error) {
   114  	return func(buf *buffer, p unsafe.Pointer) error {
   115  		value := *(*int)(p)
   116  		buf.b = strconv.AppendInt(buf.b, int64(value), 10)
   117  		return nil
   118  	}, nil
   119  }
   120  
   121  func Benchmark_Marshal(b *testing.B) {
   122  	b.ReportAllocs()
   123  	for n := 0; n < b.N; n++ {
   124  		bytes, err := Marshal(struct{ I int }{10})
   125  		if err != nil {
   126  			b.Fatal(err)
   127  		}
   128  		if string(bytes) != "{10}" {
   129  			b.Fatalf("unexpected error: %s", string(bytes))
   130  		}
   131  		bytes2, err := Marshal(struct{ I, J int }{10, 20})
   132  		if err != nil {
   133  			b.Fatal(err)
   134  		}
   135  		if string(bytes2) != "{10 20}" {
   136  			b.Fatalf("unexpected error: %s", string(bytes2))
   137  		}
   138  	}
   139  }