Two providers one caller

I could use some of your thoughts on the ways to tackle this situation:

I have two packages - say A and B providing very similar functions but encoding the data in its own ways.

these packages are invoked by another package C.

Package C invokes either A’s function or B’s function depending on the kind of encoding requested. Package C has a large number of functions that would in turn call A or B.

Considering the highest priority is very low cpu overhead and very low memory allocation overhead and high readability of the code, what is the best way to write the functions in package C. One way i did do was:

package C

import "A"
import "B"

func DoOperation (arg1) {
    if encoding==A {
          data = A.DoOperation(arg1)
    }  else if encoding == B {
          data = B.DoOperation(arg1)
    //post process data

Would appreciate your comments.

I did consider invoking through an interface - based on few initial analysis - go compiler would be forced to store all arguments in the heap (instead of a stack) - this will increase allocations - am not entirely sure about this…


Regarding speed, is the given code already fast enough? Squeezing out as much speed as possible would only be worth the effort if the current code is too slow for the given use case.

Regarding readability, this is difficult to comment without seeing the code that uses interfaces.

go compiler would be forced to store all arguments in the heap (instead of a stack)

I guess this depends on how the code uses the interfaces. Usually, the compiler does an escape analysis for local variables and if it can prove that a variable is never used outside the function, it creates the variable on the stack.

You can get the results of the escape analysis by passing these flags to go run or go build:

-gcflags '-m -l'

where the -m flag prints escape information and -l prevents functions from getting inlined.

1 Like

Here’s the snippet of code. Implementation #2 is most efficient. #1 is NOT.

type encoderIntf interface {
        AppendString([]byte, string) []byte

type jsonEncoder struct{}
type cborEncoder struct{}

func (j jsonEncoder) AppendString(dst []byte, s string) []byte {
        return json.AppendString(dst, s)

func (c cborEncoder) AppendString(dst []byte, s string) []byte {
        return cbor.AppendString(dst, s)

var encoders = []encoderIntf{jsonEncoder{}, cborEncoder{}}

func (c Context) Str(key, val string) Context {
        enc := encoders[c.l.encoderType] // Picks up the right encoder object (A or B)
        c.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val)
        return c

func (c Context) Str(key, val string) Context {
       if c.l.isBinary {
              c.l.context = cbor.AppendString(cbor.AppendKey(c.l.context, key), val)
      } else {
              c.l.context = json.AppendString(json.AppendKey(c.l.context, key), val)
      return c

gcflags “-m -m” output shows:

./context.go:85:39: leaking param content: c
./context.go:85:39:     from c.l (dot) at ./context.go:87:51
./context.go:85:39:     from c.l.context (dot) at ./context.go:87:53
./context.go:85:39:     from *c.l.context (indirection) at ./context.go:87:53
./context.go:85:39:     from c.l.context (passed to call[argument content escapes]) at ./context.go:87:49


./context.go:85:39: leaking param: c
./context.go:85:39:     from enc.AppendKey(c.l.context, key) (parameter to indirect call) at ./context.go:90:46
./context.go:85:39: leaking param: key
./context.go:85:39:     from enc.AppendKey(c.l.context, key) (parameter to indirect call) at ./context.go:90:46
./context.go:85:39: leaking param: val
./context.go:85:39:     from enc.AppendString(enc.AppendKey(c.l.context, key), val) (parameter to indirect call) at ./context.go:90:32

I am a newbie to be able to understand this gc output - could use some help understanding it…

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.