rate: double reserveN perf by 2x

This commit increase the speed of reserveN by 2x by removing the defer
to unlock.

NOTE: I did an audit of the code between Lock and Unlock and did not see
anything that can panic causing the Unlock to averted.

benchmark             old ns/op     new ns/op     delta
BenchmarkAllowN-4     290           143           -50.69%

benchmark             old allocs     new allocs     delta
BenchmarkAllowN-4     0              0              +0.00%

benchmark             old bytes     new bytes     delta
BenchmarkAllowN-4     0             0             +0.00%

Change-Id: I01c23f4998f5451ea8efedd02e744fb5a74eae0d
Reviewed-on: https://go-review.googlesource.com/29379
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/rate/rate.go b/rate/rate.go
index 2131b92..26ed8b3 100644
--- a/rate/rate.go
+++ b/rate/rate.go
@@ -281,9 +281,9 @@
 // reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
 func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {
 	lim.mu.Lock()
-	defer lim.mu.Unlock()
 
 	if lim.limit == Inf {
+		lim.mu.Unlock()
 		return Reservation{
 			ok:        true,
 			lim:       lim,
@@ -326,6 +326,7 @@
 		lim.last = last
 	}
 
+	lim.mu.Unlock()
 	return r
 }
 
diff --git a/rate/rate_test.go b/rate/rate_test.go
index cd152a5..a7c0285 100644
--- a/rate/rate_test.go
+++ b/rate/rate_test.go
@@ -425,3 +425,15 @@
 	runWait(t, lim, wait{"act-now", ctx, 2, 0, true})
 	runWait(t, lim, wait{"w-timeout-err", ctx, 3, 0, false})
 }
+
+func BenchmarkAllowN(b *testing.B) {
+	lim := NewLimiter(Every(1*time.Second), 1)
+	now := time.Now()
+	b.ReportAllocs()
+	b.ResetTimer()
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			lim.AllowN(now, 1)
+		}
+	})
+}