Skip to content

Commit 8e11cb3

Browse files
raskyaclements
authored andcommitted
runtime: improve comments for nextSample
The previous comment of nextSample didn't mention Poisson processes, which is the reason why it needed to create an exponential distribution, so it was hard to follow the reasoning for people not highly familiar with statistics. Since we're at it, we also make it clear that we are just creating a random number with exponential distribution by moving the bulk of the function into a new fastexprand(). No functional changes. Change-Id: I9c275e87edb3418ee0974257af64c73465028ad7 Reviewed-on: https://go-review.googlesource.com/65657 Reviewed-by: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
1 parent 3f04db4 commit 8e11cb3

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

src/runtime/malloc.go

+26-20
Original file line numberDiff line numberDiff line change
@@ -865,11 +865,13 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
865865
mProf_Malloc(x, size)
866866
}
867867

868-
// nextSample returns the next sampling point for heap profiling.
869-
// It produces a random variable with a geometric distribution and
870-
// mean MemProfileRate. This is done by generating a uniformly
871-
// distributed random number and applying the cumulative distribution
872-
// function for an exponential.
868+
// nextSample returns the next sampling point for heap profiling. The goal is
869+
// to sample allocations on average every MemProfileRate bytes, but with a
870+
// completely random distribution over the allocation timeline; this
871+
// corresponds to a Poisson process with parameter MemProfileRate. In Poisson
872+
// processes, the distance between two samples follows the exponential
873+
// distribution (exp(MemProfileRate)), so the best return value is a random
874+
// number taken from an exponential distribution whose mean is MemProfileRate.
873875
func nextSample() int32 {
874876
if GOOS == "plan9" {
875877
// Plan 9 doesn't support floating point in note handler.
@@ -878,33 +880,37 @@ func nextSample() int32 {
878880
}
879881
}
880882

881-
period := MemProfileRate
883+
return fastexprand(MemProfileRate)
884+
}
882885

883-
// make nextSample not overflow. Maximum possible step is
884-
// -ln(1/(1<<kRandomBitCount)) * period, approximately 20 * period.
886+
// fastexprand returns a random number from an exponential distribution with
887+
// the specified mean.
888+
func fastexprand(mean int) int32 {
889+
// Avoid overflow. Maximum possible step is
890+
// -ln(1/(1<<randomBitCount)) * mean, approximately 20 * mean.
885891
switch {
886-
case period > 0x7000000:
887-
period = 0x7000000
888-
case period == 0:
892+
case mean > 0x7000000:
893+
mean = 0x7000000
894+
case mean == 0:
889895
return 0
890896
}
891897

892-
// Let m be the sample rate,
893-
// the probability distribution function is m*exp(-mx), so the CDF is
894-
// p = 1 - exp(-mx), so
895-
// q = 1 - p == exp(-mx)
896-
// log_e(q) = -mx
897-
// -log_e(q)/m = x
898-
// x = -log_e(q) * period
899-
// x = log_2(q) * (-log_e(2)) * period ; Using log_2 for efficiency
898+
// Take a random sample of the exponential distribution exp(-mean*x).
899+
// The probability distribution function is mean*exp(-mean*x), so the CDF is
900+
// p = 1 - exp(-mean*x), so
901+
// q = 1 - p == exp(-mean*x)
902+
// log_e(q) = -mean*x
903+
// -log_e(q)/mean = x
904+
// x = -log_e(q) * mean
905+
// x = log_2(q) * (-log_e(2)) * mean ; Using log_2 for efficiency
900906
const randomBitCount = 26
901907
q := fastrand()%(1<<randomBitCount) + 1
902908
qlog := fastlog2(float64(q)) - randomBitCount
903909
if qlog > 0 {
904910
qlog = 0
905911
}
906912
const minusLog2 = -0.6931471805599453 // -ln(2)
907-
return int32(qlog*(minusLog2*float64(period))) + 1
913+
return int32(qlog*(minusLog2*float64(mean))) + 1
908914
}
909915

910916
// nextSampleNoFP is similar to nextSample, but uses older,

0 commit comments

Comments
 (0)