Commit 2ca5d105 authored by Sameer Ajmani's avatar Sameer Ajmani

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/36391Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent fd37b8cc
...@@ -4,20 +4,40 @@ ...@@ -4,20 +4,40 @@
package user package user
import "sync"
// Current returns the current user. // Current returns the current user.
func Current() (*User, error) { 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 // Lookup looks up a user by username. If the user cannot be found, the
// returned error is of type UnknownUserError. // returned error is of type UnknownUserError.
func Lookup(username string) (*User, error) { func Lookup(username string) (*User, error) {
if u, err := Current(); err == nil && u.Username == username {
return u, err
}
return lookupUser(username) return lookupUser(username)
} }
// LookupId looks up a user by userid. If the user cannot be found, the // LookupId looks up a user by userid. If the user cannot be found, the
// returned error is of type UnknownUserIdError. // returned error is of type UnknownUserIdError.
func LookupId(uid string) (*User, error) { func LookupId(uid string) (*User, error) {
if u, err := Current(); err == nil && u.Uid == uid {
return u, err
}
return lookupUserId(uid) return lookupUserId(uid)
} }
......
...@@ -31,6 +31,12 @@ func TestCurrent(t *testing.T) { ...@@ -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) { func compare(t *testing.T, want, got *User) {
if want.Uid != got.Uid { if want.Uid != got.Uid {
t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid) t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment