There are a number of design patterns I am using on daily bases, and one of them is the Singleton pattern. In Golang penalties in performance are not so harsh as in other languages, if the application is not designed and written to perform with minimal CPU and memory footprint. I’m trying to keep my implementations as performing as possible, but sometimes explicit benchmarks can revile the details.

The singleton pattern is widely used in Object Oriented languages, and it is well worth to use it in golang. It can solve problems of not generating multiple instances within the application runtime, hide the instantiation of the struct among others. The performance can be somewhat better compared to initializing an instance each time, the reasons are as simple as garbage collection and memory allocation. In some cases if the object contains complex initialization process (involving computation), a singleton can be an option. That said, unfortunately, some developers are using it in places where other patterns like factory, fly-weight etc. would be needed. Singleton is not the holy grail.

Eager singleton

Eager singleton uses a pre-initialized struct, and does return this each time “getInstance” function is called. A simple example for this:

...
var eagerInstance *singleton</code>

func init(){
//initialize static instance on load
eagerInstance = &singleton{t: time.Now()}
}

//GetInstanceEager - get singleton instance pre-initialized
func GetInstanceEager() *singleton{
     return eagerInstance
}

Lazy sinlgeton

Lazy singleton initalizes the struct when it is needed.

var lazyInstance *singleton</code>

//GetInstanceLazy - get singleton instance initialized on call
func GetInstanceLazy() *singleton{
    if lazyInstance == nil{
        lazyInstance = &singleton{t: time.Now()}
    }

    return lazyInstance
}

Thread safe singleton

Thread safe singleton is initalized when it is needed, but contains a locking mechanism to write the object in memory. This can be performed with lock or by using the “sync.Once” function, which is similar to “init”, but is not called on package initialization as default. Please note, that being “Thread safe” only referrs to getting the instance and not the subsequent functions.

...
var (
   threadSafeInstance *singleton
   once sync.Once
)

//GetInstanceThreadSafe - get singleton instance, with thread safe initialization
func GetInstanceThreadSafe() *singleton{
    once.Do(func() {
        if threadSafeInstance == nil{
           threadSafeInstance = &singleton{t: time.Now()}
        }
    })

    return threadSafeInstance
}
...

Benchmark

The benchmark test clearly shows that the eager with pre-initalized struct brings the best performance, as we need the least amount of allocation and wait.

BenchmarkEager-4        2000000000          0.61 ns/op
BenchmarkLazy-4          1000000000          2.14 ns/op
BenchmarkThreadSafe-4    300000000          5.81 ns/op
PASS
Share This