From 2ca5d105b00353d3f3aa4da023e405b0550117d0 Mon Sep 17 00:00:00 2001
From: Sameer Ajmani <sameer@golang.org>
Date: Mon, 6 Feb 2017 13:21:57 -0500
Subject: [PATCH] os/user: cache the result of user.Current

This has a notable impact on systems with very large passwd files.

Before:
BenchmarkCurrent-12    	   30000	     42546 ns/op

After:
BenchmarkCurrent-12    	20000000	        77.5 ns/op

Saved in perf dashboard:
https://perf.golang.org/search?q=upload:20170206.1

Fixes #11625

Change-Id: Iebc9bf122cc64a4cab24ac06843c7b2bc450ded9
Reviewed-on: https://go-review.googlesource.com/36391
Reviewed-by: Ian Lance Taylor <iant@golang.org>
---
 src/os/user/lookup.go    | 22 +++++++++++++++++++++-
 src/os/user/user_test.go |  6 ++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/os/user/lookup.go b/src/os/user/lookup.go
index 3b4421badd5..2243a25788a 100644
--- a/src/os/user/lookup.go
+++ b/src/os/user/lookup.go
@@ -4,20 +4,40 @@
 
 package user
 
+import "sync"
+
 // Current returns the current user.
 func Current() (*User, error) {
-	return current()
+	cache.Do(func() { cache.u, cache.err = current() })
+	if cache.err != nil {
+		return nil, cache.err
+	}
+	u := *cache.u // copy
+	return &u, nil
+}
+
+// cache of the current user
+var cache struct {
+	sync.Once
+	u   *User
+	err error
 }
 
 // Lookup looks up a user by username. If the user cannot be found, the
 // returned error is of type UnknownUserError.
 func Lookup(username string) (*User, error) {
+	if u, err := Current(); err == nil && u.Username == username {
+		return u, err
+	}
 	return lookupUser(username)
 }
 
 // LookupId looks up a user by userid. If the user cannot be found, the
 // returned error is of type UnknownUserIdError.
 func LookupId(uid string) (*User, error) {
+	if u, err := Current(); err == nil && u.Uid == uid {
+		return u, err
+	}
 	return lookupUserId(uid)
 }
 
diff --git a/src/os/user/user_test.go b/src/os/user/user_test.go
index 9d8d94d8dae..73e8ed8de73 100644
--- a/src/os/user/user_test.go
+++ b/src/os/user/user_test.go
@@ -31,6 +31,12 @@ func TestCurrent(t *testing.T) {
 	}
 }
 
+func BenchmarkCurrent(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		Current()
+	}
+}
+
 func compare(t *testing.T, want, got *User) {
 	if want.Uid != got.Uid {
 		t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid)
-- 
GitLab