0

I have a service that selects tasks from Redis using a Lua script. I can select up to 1000 tasks at a time. Every 250 milliseconds I will retrieve tasks, and there are several such modules in one service (each module is responsible for processing background tasks, conditionally 3 modules each will retrieve 1000 tasks. So I have a question, do I need to use ```sync.Pool`` for memory allocation and optimisation? Will it not cause other problems and will I be able to get a performance gain, since the tasks consist of 5-8 fields in the structure.

My code


var taskPool = sync.Pool{
    New: func() interface{} {
        // This will be overridden by the generic type T at runtime
        var t interface{}
        return &t
    },
}

// bytePool is a sync.Pool for reusing []byte buffers for JSON unmarshaling.
var bytePool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 0, 1024) // Initial capacity for typical JSON task size
    },
}

var extractCommand = redis.NewScript(`
local key = KEYS[1]
local max_tasks = tonumber(ARGV[1])
local tasks = {}

for i = 1, max_tasks do
    local task = redis.call('LPOP', key)
    if not task then
        break
    end
    table.insert(tasks, task)
end

return tasks
`)

const maxTask = 1000

type Fetcher[T any] struct {
    rdb    redis.UniversalClient
    logger *zerolog.Logger
}


func (f *Fetcher[T]) Fetch(ctx context.Context, keys []string) ([]T, error) {

    result, err := extractCommand.Run(ctx, f.rdb, keys, maxTask).Result()
    if err != nil {
        return nil, err
    }

    tasks := make([]T, 0)

    if results, ok := result.([]interface{}); ok && len(results) > 0 {
        for _, task := range results {
            outPtr := taskPool.Get()
            out, ok := outPtr.(*T)
            if !ok {
                out = new(T)
            }
            if tasksVal, valOk := task.(string); valOk {
                buf := bytePool.Get().([]byte)
                buf = buf[:0]
                buf = append(buf, tasksVal...)
                if err = json.Unmarshal(buf, out); err != nil {
                    taskPool.Put(out)
                    bytePool.Put(buf)

                    continue
                }

                tasks = append(tasks, *out)
                taskPool.Put(out)
                bytePool.Put(buf)
            }
        }
    }

    return tasks, nil
}
4
  • 3
    1) «do I need to use sync.Pool for memory allocation and optimisation?» — No. You can but certainly are not required to. 2) «Will it not cause other problems» — No, it won't. Or, rather, it may make allocation a bit slower, but not necessarily; see below. 3) «and will I be able to get a performance gain, since the tasks consist of 5-8 fields in the structure.» — Not necessarily. One cannot say w/o actually profiling both cases. There do exist cases where some perf. hack solves an obvious problem but yours is not one of them. Possibly you will get some gain, but don't expect wonders. Commented Mar 31 at 12:09
  • Are you currently limited by allocation and garbage collection? If you are, then definitely benchmark using the pool. If you are not, how is the pool supposed to help? If you're still unsure, try it anyway and measure the results, we can't really answer for you. Commented Mar 31 at 12:26
  • 1
    Actually, this one tasks = append(tasks, *out) appears to negate half of your efforts: you pool Ts but then have tasks grow to be at least len(results) (it can, and most probably will, allocate more than that internally) and then copy over bytes from these Ts. In this case, it's more idiomatic to preallocate tasks to hold exactly len(results), then have an index to tasks be incremented on each iteration over results (starting from 0) and then directly manipulate a task at that index instead of appending. Say, if that index variable is i, you'd unmarshal to &tasks[i]. Commented Mar 31 at 12:39
  • 1
    “do I need to use ```sync.Pool`` for memory allocation and optimisation” - do you need memory optimization? Any profiles that shows gc is not working fine? You do a lot of allocations in this code anyway. Commented Apr 2 at 9:43

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.