Re:What could Go wrong with a mutex
by treekogreen on Friday November 12, 2021 @04:36PM
(#29204829)
You could simply place `l.unlock()` underneath the function call to remove the overhead of the defer.
That doesn't address the overhead of defer though (point 2) which can be a problem if your locks are in a well travelled path.
One might argue that if you have a hot path that depends on mutexes then perhaps you need to restructure your code to avoid sharing data (and thus the need for a mutex). And that point would be valid, generally speaking. But there's always that one edge case where it is unavoidable (or even still the cleaner and faster code).
In recent versions defer is basically zero overhead if no loops are involved. It's just a function call at every function exit. Basically what you'd do yourself manually.
Yes, the overhead isn't avoided. This example was thought as the "safe" choice. If you want to be "fast", you have to very carefully analyze what you are doing and then deciding accordingly. If you have code which cannot panic, you have no need to use defer and can unlock directly after the access. And the biggest gain would be avoiding acquiring and releasing the lock in the hot path in the first place.
I think the best practice is to approach a problem from the safe direction as long as performance isn't a total disaster and then try to find the spots which carefully have to be optimized relaxing some of the safeties. But as you are dealing with small hot spots, you can manually analyze the code in question. Similar to the "unsafe" package - if you use it only punctually, the safety loss is usually not a problem.
As a Lisper, this situation of course screams for having a with-lock macro which allows to add an elegant, but safe primitive which mostly takes care of this. This would usually be implemented in terms of a function which performs the locking/unlocking and calls a function which executes the body. Fortunately, Go allows doing exactly that, so this pattern could be used, here an untested sketch:
Of course, this doesn't protect you doing some potentially blocking actions in the body of the function executed, but the leaner the syntax is and as the defer is guaranteed to be executed at the end of the body, the risk is greatly reduced.