* (global): added --no-mem-optimization flag

This commit adds a new command-line argument that disables memory
optimizations AGH is using. These memory optimizations might be
necessary on low-memory devices, but they aren't free and there's a
performance hit (see #2044). Now they can be disabled - just pass
--no-mem-optimization when you run AGH or when you install the service
-- ./AdGuardHome -s install --no-mem-optimization

Closes: #2044
This commit is contained in:
Andrey Meshkov 2020-09-11 13:19:37 +03:00
parent cb8afde629
commit b3a68bb806
4 changed files with 60 additions and 32 deletions

View File

@ -161,6 +161,9 @@ func run(args options) {
// configure log level and output // configure log level and output
configureLogger(args) configureLogger(args)
// Go memory hacks
memoryUsage(args)
// print the first message after logger is configured // print the first message after logger is configured
log.Println(version()) log.Println(version())
log.Debug("Current working directory is %s", Context.workDir) log.Debug("Current working directory is %s", Context.workDir)

44
home/memory.go Normal file
View File

@ -0,0 +1,44 @@
package home
import (
"os"
"runtime/debug"
"time"
"github.com/AdguardTeam/golibs/log"
)
// memoryUsage implements a couple of not really beautiful hacks which purpose is to
// make OS reclaim the memory freed by AdGuard Home as soon as possible.
// See this for the details on the performance hits & gains:
// https://github.com/AdguardTeam/AdGuardHome/issues/2044#issuecomment-687042211
func memoryUsage(args options) {
if args.disableMemoryOptimization {
log.Info("Memory optimization is disabled")
return
}
// Makes Go allocate heap at a slower pace
// By default we keep it at 50%
debug.SetGCPercent(50)
// madvdontneed: setting madvdontneed=1 will use MADV_DONTNEED
// instead of MADV_FREE on Linux when returning memory to the
// kernel. This is less efficient, but causes RSS numbers to drop
// more quickly.
_ = os.Setenv("GODEBUG", "madvdontneed=1")
// periodically call "debug.FreeOSMemory" so
// that the OS could reclaim the free memory
go func() {
ticker := time.NewTicker(5 * time.Minute)
for {
select {
case t := <-ticker.C:
t.Second()
log.Debug("Free OS memory")
debug.FreeOSMemory()
}
}
}()
}

View File

@ -24,7 +24,11 @@ type options struct {
// runningAsService flag is set to true when options are passed from the service runner // runningAsService flag is set to true when options are passed from the service runner
runningAsService bool runningAsService bool
glinetMode bool // Activate GL-Inet mode // disableMemoryOptimization - disables memory optimization hacks
// see memoryUsage() function for the details
disableMemoryOptimization bool
glinetMode bool // Activate GL-Inet compatibility mode
} }
// functions used for their side-effects // functions used for their side-effects
@ -151,6 +155,13 @@ var noCheckUpdateArg = arg{
func(o options) []string { return boolSliceOrNil(o.disableUpdate) }, func(o options) []string { return boolSliceOrNil(o.disableUpdate) },
} }
var disableMemoryOptimizationArg = arg{
"Disable memory optimization",
"no-mem-optimization", "",
nil, func(o options) (options, error) { o.disableMemoryOptimization = true; return o, nil }, nil,
func(o options) []string { return boolSliceOrNil(o.disableMemoryOptimization) },
}
var verboseArg = arg{ var verboseArg = arg{
"Enable verbose output", "Enable verbose output",
"verbose", "v", "verbose", "v",
@ -194,6 +205,7 @@ func init() {
pidfileArg, pidfileArg,
checkConfigArg, checkConfigArg,
noCheckUpdateArg, noCheckUpdateArg,
disableMemoryOptimizationArg,
verboseArg, verboseArg,
glinetArg, glinetArg,
versionArg, versionArg,

31
main.go
View File

@ -4,10 +4,6 @@
package main package main
import ( import (
"os"
"runtime/debug"
"time"
"github.com/AdguardTeam/AdGuardHome/home" "github.com/AdguardTeam/AdGuardHome/home"
) )
@ -21,32 +17,5 @@ var channel = "release"
var goarm = "" var goarm = ""
func main() { func main() {
memoryUsage()
home.Main(version, channel, goarm) home.Main(version, channel, goarm)
} }
// memoryUsage implements a couple of not really beautiful hacks which purpose is to
// make OS reclaim the memory freed by AdGuard Home as soon as possible.
func memoryUsage() {
debug.SetGCPercent(10)
// madvdontneed: setting madvdontneed=1 will use MADV_DONTNEED
// instead of MADV_FREE on Linux when returning memory to the
// kernel. This is less efficient, but causes RSS numbers to drop
// more quickly.
_ = os.Setenv("GODEBUG", "madvdontneed=1")
// periodically call "debug.FreeOSMemory" so
// that the OS could reclaim the free memory
go func() {
ticker := time.NewTicker(15 * time.Second)
for {
select {
case t := <-ticker.C:
t.Second()
debug.FreeOSMemory()
}
}
}()
}