Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 71446e9

Browse files
committed
os: optimise cgo clearenv
For programs with very large environments, calling unsetenv(3) for each environment variable can be fairly expensive because of CGo overhead, but clearenv(3) is much faster. The only thing we have to track is whether GODEBUG is being unset by the operation, which can be done very quickly without resorting to doing unsetenv(3) for every variable. This change makes os.Clearenv() ~30% faster when run an environment with 1000 environment variable set (and gets better with more environment variables): cpu: AMD Ryzen 7 7840U w/ Radeon 780M Graphics │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ Env-16 213.6μ ± 1% 185.1μ ± 3% -13.32% (p=0.000 n=10) (The time taken to set up 1000 variables in this benchmark is 117.2μs, so while the naive timing is 13% the speedup for os.Clearenv() itself the speedup is from 96.4μs to 67.9μs which is ~30%.) Change-Id: I589794845854e1721e6fc5badc671474c2bbd037
1 parent c46ba1f commit 71446e9

File tree

6 files changed

+87
-3
lines changed

6 files changed

+87
-3
lines changed

‎src/runtime/cgo/clearenv.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build linux
6+
7+
package cgo
8+
9+
import _ "unsafe" // for go:linkname
10+
11+
//go:cgo_import_static x_cgo_clearenv
12+
//go:linkname x_cgo_clearenv x_cgo_clearenv
13+
//go:linkname _cgo_clearenv runtime._cgo_clearenv
14+
var x_cgo_clearenv byte
15+
var _cgo_clearenv = &x_cgo_clearenv

‎src/runtime/cgo/gcc_clearenv.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build linux
6+
7+
#include "libcgo.h"
8+
9+
#include <stdlib.h>
10+
11+
/* Stub for calling clearenv */
12+
void
13+
x_cgo_clearenv(void **_unused)
14+
{
15+
_cgo_tsan_acquire();
16+
clearenv();
17+
_cgo_tsan_release();
18+
}
19+

‎src/runtime/runtime_clearenv.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build linux
6+
7+
package runtime
8+
9+
import "unsafe"
10+
11+
var _cgo_clearenv unsafe.Pointer // pointer to C function
12+
13+
// Clear the C environment if cgo is loaded.
14+
func clearenv_c() {
15+
if _cgo_clearenv == nil {
16+
return
17+
}
18+
asmcgocall(_cgo_clearenv, nil)
19+
}
20+
21+
//go:linkname syscall_runtimeClearenv syscall.runtimeClearenv
22+
func syscall_runtimeClearenv(env map[string]int) {
23+
clearenv_c()
24+
// Did we just unset GODEBUG?
25+
if _, ok := env["GODEBUG"]; ok {
26+
godebugEnv.Store(nil)
27+
godebugNotify(true)
28+
}
29+
}

‎src/runtime/runtime_noclearenv.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build !linux
6+
7+
package runtime
8+
9+
import _ "unsafe" // for go:linkname
10+
11+
//go:linkname syscall_runtimeClearenv syscall.runtimeClearenv
12+
func syscall_runtimeClearenv(env map[string]int) {
13+
// The system doesn't have clearenv(3) so emulate it by unsetting all of
14+
// the variables manually.
15+
for k := range env {
16+
syscall_runtimeUnsetenv(k)
17+
}
18+
}

‎src/syscall/env_unix.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,8 @@ func Clearenv() {
129129
envLock.Lock()
130130
defer envLock.Unlock()
131131

132-
for k := range env {
133-
runtimeUnsetenv(k)
134-
}
132+
runtimeClearenv(env)
133+
135134
env = make(map[string]int)
136135
envs = []string{}
137136
}

‎src/syscall/syscall.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,7 @@ func Exit(code int)
104104
// runtimeSetenv and runtimeUnsetenv are provided by the runtime.
105105
func runtimeSetenv(k, v string)
106106
func runtimeUnsetenv(k string)
107+
108+
// runtimeClearenv is provided by the runtime (on platforms without
109+
// clearenv(3), it is just a wrapper around runtimeUnsetenv).
110+
func runtimeClearenv(env map[string]int)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /