go_memoize package provides a set of functions to memoize the results of computations, allowing for efficient caching and retrieval of results based on input parameters. This can significantly improve performance for expensive or frequently called functions.
- Memoizes functions with TTL, supporting 0 to 7 comparable parameters. List of Memoize Functions
- Error-aware variants (suffix
E) that support compute functions returning(V, error)and do NOT cache when an error is returned. - High performance, zero allocation, and zero dependencies.
- Utilizes the FNV-1a hash algorithm for caching.
- Thread-safe and concurrent-safe.
To install the package, use go get:
go get github.com/agkloop/go_memoizeThe Memoize function can be used to memoize a function with no parameters:
computeFn := func() int {
// Expensive computation
return 42
}
memoizedFn := Memoize(computeFn, 10*time.Second)
result := memoizedFn()The same for functions with Context:
computeCtxFn := func(ctx context.Context) int {
// Expensive computation
return 42
}
memoizedCtxFn := MemoizeCtx(computeCtxFn, 10*time.Second)
result := memoizedCtxFn(context.Background())If your compute function can fail and returns (V, error), use the E variants. These versions will NOT store a cached value when the compute function returns a non-nil error. This is useful for transient failures where you want the next call to retry the computation rather than returning a cached error result.
Available E variants:
MemoizeE(no-arg)Memoize1E..Memoize7E(1..7 args)MemoizeCtxEandMemoizeCtx1E..MemoizeCtx7E(context-aware)
Behavior:
- If a cached value exists for the key, the function returns it and a nil error.
- If no cached value exists, the compute function is executed.
- If compute returns
(v, nil),vis cached and returned. - If compute returns
(zeroValue, err)(err != nil), the error is returned and nothing is cached.
- If compute returns
Example:
computeFn := func(id int) (string, error) {
// may return an error sometimes
}
memo := Memoize1E(func(id int) (string, error) { return computeFn(id) }, 30*time.Second)
val, err := memo(123)
if err != nil {
// transient error; next call will retry since nothing was cached
}The package provides functions to memoize functions with up to 7 parameters. Here are some examples:
computeFn := func(a int) int {
// Expensive computation
return a * 2
}
memoizedFn := Memoize1(computeFn, 10*time.Second)
result := memoizedFn(5)The same for functions with Context:
computeCtxFn := func(ctx context.Context, a int) int {
// Expensive computation
return a * 2
}
memoizedCtxFn := MemoizeCtx1(computeCtxFn, 10*time.Second)
result := memoizedCtxFn(context.Background(), 5)computeFn := func(a int, b string) string {
// Expensive computation
return fmt.Sprintf("%d-%s", a, b)
}
memoizedFn := Memoize2(computeFn, 10*time.Second)
result := memoizedFn(5, "example")The same for functions with Context:
computeCtxFn := func(ctx context.Context, a int, b string) string {
// Expensive computation
return fmt.Sprintf("%d-%s", a, b)
}
memoizedCtxFn := MemoizeCtx2(computeCtxFn, 10*time.Second)
result := memoizedCtxFn(context.Background(), 5, "example")computeFn := func(a int, b string, c float64) string {
// Expensive computation
return fmt.Sprintf("%d-%s-%f", a, b, c)
}
memoizedFn := Memoize3(computeFn, 10*time.Second)
result := memoizedFn(5, "example", 3.14)The same for functions with Context:
computeCtxFn := func(ctx context.Context, a int, b string, c float64) string {
// Expensive computation
return fmt.Sprintf("%d-%s-%f", a, b, c)
}
memoizedCtxFn := MemoizeCtx3(computeCtxFn, 10*time.Second)
result := memoizedCtxFn(context.Background(), 5, "example", 3.14)The Cache struct is used internally to manage the cached entries. It supports setting, getting, and deleting entries, as well as computing new values if they are not already cached or have expired.
Unit tests cover the memoization behavior, including the new error-aware variants. To run tests:
# run all tests
go test ./...
# run a specific test
go test ./... -run TestMemoizeE_DoesNotCacheError -vNew tests were added in memoize_error_test.go to verify that error results are not cached and that successful results are cached.
Here is a complete example of using the memoize package:
package main
import (
"fmt"
"time"
m "github.com/agkloop/go_memoize"
)
func main() {
computeFn := func(a int, b string) string {
// Simulate an expensive computation
time.Sleep(2 * time.Second)
return fmt.Sprintf("%d-%s", a, b)
}
memoizedFn := m.Memoize2(computeFn, 10*time.Second)
// First call will compute the result
result := memoizedFn(5, "example")
fmt.Println(result) // Output: 5-example
// Subsequent calls within 10 seconds will use the cached result
result = memoizedFn(5, "example")
fmt.Println(result) // Output: 5-example
}Function |
Description |
Example |
|---|---|---|
Memoize |
Memoizes a function with no params |
|
Memoize1 |
Memoizes a function with 1 param |
|
Memoize2 |
Memoizes a function with 2 params |
|
Memoize3 |
Memoizes a function with 3 params |
|
Memoize4 |
Memoizes a function with 4 params |
|
Memoize5 |
Memoizes a function with 5 params |
|
Memoize6 |
Memoizes a function with 6 params |
|
Memoize7 |
Memoizes a function with 7 params |
|
MemoizeE |
Memoizes a function with no params, error-aware |
|
Memoize1E |
Memoizes a function with 1 param, error-aware |
|
Memoize2E |
Memoizes a function with 2 params, error-aware |
|
Memoize3E |
Memoizes a function with 3 params, error-aware |
|
MemoizeCtx |
Memoizes a function with context and no params |
|
MemoizeCtx1 |
Memoizes a function with context and 1 param |
|
MemoizeCtx2 |
Memoizes a function with context and 2 params |
|
MemoizeCtx3 |
Memoizes a function with context and 3 params |
|
MemoizeCtx4 |
Memoizes a function with context and 4 params |
|
MemoizeCtx5 |
Memoizes a function with context and 5 params |
|
MemoizeCtx6 |
Memoizes a function with context and 6 params |
|
MemoizeCtx7 |
Memoizes a function with context and 7 params |
|
Device "Apple M2 Pro"
goos: darwin
goarch: arm64
BenchmarkDo0Mem-10 | 811289566 | 14.77 ns/op | 0 B/op | 0 allocs/op
BenchmarkDo1Mem-10 | 676579908 | 18.26 ns/op | 0 B/op | 0 allocs/op
BenchmarkDo2Mem-10 | 578134332 | 20.99 ns/op | 0 B/op | 0 allocs/op
BenchmarkDo3Mem-10 | 533455237 | 22.67 ns/op | 0 B/op | 0 allocs/op
BenchmarkDo4Mem-10 | 487471639 | 24.73 ns/op | 0 B/op | 0 allocs/op
This project is licensed under the Apache License. See the LICENSE file for details.