diff --git a/pwhash/password.go b/pwhash/password.go
index f8f84e36a180e2d6634cf0cb7eb0071a548d5f6c..1318a92df1c41d929afdb476110d26954040368a 100644
--- a/pwhash/password.go
+++ b/pwhash/password.go
@@ -1,3 +1,24 @@
+// Package pwhash provides a simple interface to hashed passwords with
+// support for multiple hashing algorithms.
+//
+// The format is the well-known dollar-separated field string,
+// extended with optional algorithm-specific parameters:
+//
+//     $id[$params...]$salt$encrypted
+//
+// We extend 'id' beyond the values supported by the libc crypt(3)
+// function with the following hashing algorithms:
+//
+// Scrypt (id '$s$'), in which case the parameters are N, R and P.
+//
+// Argon2 (id '$a2$'), with parameters time, memory and threads. To
+// tune Argon2 parameters, you can run the benchmarks in this package:
+// the parameterized benchmarks are named with
+// time/memory(MB)/threads. For nicer results:
+//
+//     go test -bench=Argon2 -run=none . 2>&1 | \
+//         awk '/^Bench/ {ops=1000000000 / $3; print $1 " " ops " ops/sec"}'
+//
 package pwhash
 
 import (
diff --git a/pwhash/password_test.go b/pwhash/password_test.go
index 27ef8f4e23d28f90c4baf17ff674a153e6ae8c98..ab08ea213a7e90d2d812c3f377146ff5be45a61e 100644
--- a/pwhash/password_test.go
+++ b/pwhash/password_test.go
@@ -1,6 +1,9 @@
 package pwhash
 
-import "testing"
+import (
+	"fmt"
+	"testing"
+)
 
 func TestArgon2(t *testing.T) {
 	testImpl(t, NewArgon2())
@@ -57,3 +60,34 @@ func testImpl(t *testing.T, h PasswordHash) {
 		}
 	}
 }
+
+func BenchmarkArgon2(b *testing.B) {
+	var testParams []argon2Params
+	for iTime := 1; iTime <= 5; iTime++ {
+		for iThreads := 1; iThreads <= 8; iThreads *= 2 {
+			for iMem := 1; iMem <= 16; iMem *= 2 {
+				testParams = append(testParams, argon2Params{
+					Time:    uint32(iTime),
+					Memory:  uint32(iMem * 1024),
+					Threads: uint8(iThreads),
+				})
+			}
+		}
+	}
+
+	goodPw := "good password"
+	badPw := "definitely not the good password"
+
+	for _, tp := range testParams {
+		name := fmt.Sprintf("%d/%d/%d", tp.Time, tp.Memory, tp.Threads)
+		b.Run(name, func(b *testing.B) {
+			h := NewArgon2WithParams(tp.Time, tp.Memory, tp.Threads)
+			encPw := h.Encrypt(goodPw)
+
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				h.ComparePassword(encPw, badPw) // nolint
+			}
+		})
+	}
+}