浏览代码

Improve darwin tun performance

世界 3 月之前
父节点
当前提交
72dbcd3ad4
共有 4 个文件被更改,包括 294 次插入6 次删除
  1. 286 0
      cmd/internal/tun_bench/main.go
  2. 2 2
      go.mod
  3. 4 4
      go.sum
  4. 2 0
      protocol/tun/inbound.go

+ 286 - 0
cmd/internal/tun_bench/main.go

@@ -0,0 +1,286 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net/netip"
+	"os"
+	"os/exec"
+	"strings"
+	"syscall"
+	"time"
+
+	C "github.com/sagernet/sing-box/constant"
+	"github.com/sagernet/sing-box/include"
+	"github.com/sagernet/sing-box/log"
+	"github.com/sagernet/sing-box/option"
+	"github.com/sagernet/sing/common"
+	E "github.com/sagernet/sing/common/exceptions"
+	"github.com/sagernet/sing/common/json"
+	"github.com/sagernet/sing/common/shell"
+)
+
+var iperf3Path string
+
+func main() {
+	err := main0()
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func main0() error {
+	err := shell.Exec("sudo", "ls").Run()
+	if err != nil {
+		return err
+	}
+	results, err := runTests()
+	if err != nil {
+		return err
+	}
+	encoder := json.NewEncoder(os.Stdout)
+	encoder.SetIndent("", "  ")
+	return encoder.Encode(results)
+}
+
+func runTests() ([]TestResult, error) {
+	boxPaths := []string{
+		//"/Users/sekai/Downloads/sing-box-1.11.15-darwin-arm64/sing-box",
+		//"/Users/sekai/Downloads/sing-box-1.11.15-linux-arm64/sing-box",
+		"./sing-box",
+	}
+	stacks := []string{
+		"gvisor",
+		"system",
+	}
+	mtus := []int{
+		// 1500,
+		// 4064,
+		// 16384,
+		32768,
+		49152,
+		65535,
+	}
+	flagList := [][]string{
+		{},
+	}
+	var results []TestResult
+	for _, boxPath := range boxPaths {
+		for _, stack := range stacks {
+			for _, mtu := range mtus {
+				if strings.HasPrefix(boxPath, ".") {
+					for _, flags := range flagList {
+						result, err := testOnce(boxPath, stack, mtu, false, flags)
+						if err != nil {
+							return nil, err
+						}
+						results = append(results, *result)
+					}
+				} else {
+					result, err := testOnce(boxPath, stack, mtu, false, nil)
+					if err != nil {
+						return nil, err
+					}
+					results = append(results, *result)
+				}
+			}
+		}
+	}
+	return results, nil
+}
+
+type TestResult struct {
+	BoxPath       string   `json:"box_path"`
+	Stack         string   `json:"stack"`
+	MTU           int      `json:"mtu"`
+	Flags         []string `json:"flags"`
+	MultiThread   bool     `json:"multi_thread"`
+	UploadSpeed   string   `json:"upload_speed"`
+	DownloadSpeed string   `json:"download_speed"`
+}
+
+func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags []string) (result *TestResult, err error) {
+	testAddress := netip.MustParseAddr("1.1.1.1")
+	testConfig := option.Options{
+		Inbounds: []option.Inbound{
+			{
+				Type: C.TypeTun,
+				Options: &option.TunInboundOptions{
+					Address:      []netip.Prefix{netip.MustParsePrefix("172.18.0.1/30")},
+					AutoRoute:    true,
+					MTU:          uint32(mtu),
+					Stack:        stackName,
+					RouteAddress: []netip.Prefix{netip.PrefixFrom(testAddress, testAddress.BitLen())},
+				},
+			},
+		},
+		Route: &option.RouteOptions{
+			Rules: []option.Rule{
+				{
+					Type: C.RuleTypeDefault,
+					DefaultOptions: option.DefaultRule{
+						RawDefaultRule: option.RawDefaultRule{
+							IPCIDR: []string{testAddress.String()},
+						},
+						RuleAction: option.RuleAction{
+							Action: C.RuleActionTypeRouteOptions,
+							RouteOptionsOptions: option.RouteOptionsActionOptions{
+								OverrideAddress: "127.0.0.1",
+							},
+						},
+					},
+				},
+			},
+			AutoDetectInterface: true,
+		},
+	}
+	ctx := include.Context(context.Background())
+	tempConfig, err := os.CreateTemp("", "tun-bench-*.json")
+	if err != nil {
+		return
+	}
+	defer os.Remove(tempConfig.Name())
+	encoder := json.NewEncoderContext(ctx, tempConfig)
+	encoder.SetIndent("", "  ")
+	err = encoder.Encode(testConfig)
+	if err != nil {
+		return nil, E.Cause(err, "encode test config")
+	}
+	tempConfig.Close()
+	var sudoArgs []string
+	if len(flags) > 0 {
+		sudoArgs = append(sudoArgs, "env")
+		for _, flag := range flags {
+			sudoArgs = append(sudoArgs, flag)
+		}
+	}
+	sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
+	boxProcess := shell.Exec("sudo", sudoArgs...)
+	boxProcess.Stdout = &stderrWriter{}
+	boxProcess.Stderr = io.Discard
+	err = boxProcess.Start()
+	if err != nil {
+		return
+	}
+
+	if C.IsDarwin {
+		iperf3Path, err = exec.LookPath("iperf3-darwin")
+	} else {
+		iperf3Path, err = exec.LookPath("iperf3")
+	}
+	if err != nil {
+		return
+	}
+	serverProcess := shell.Exec(iperf3Path, "-s")
+	serverProcess.Stdout = io.Discard
+	serverProcess.Stderr = io.Discard
+	err = serverProcess.Start()
+	if err != nil {
+		return nil, E.Cause(err, "start iperf3 server")
+	}
+
+	time.Sleep(time.Second)
+
+	args := []string{"-c", testAddress.String(), "-t", "5"}
+	if multiThread {
+		args = append(args, "-P", "10")
+	}
+
+	uploadProcess := shell.Exec(iperf3Path, args...)
+	output, err := uploadProcess.Read()
+	if err != nil {
+		boxProcess.Process.Signal(syscall.SIGKILL)
+		serverProcess.Process.Signal(syscall.SIGKILL)
+		println(output)
+		return
+	}
+
+	uploadResult := common.SubstringBeforeLast(output, "iperf Done.")
+	uploadResult = common.SubstringBeforeLast(uploadResult, "sender")
+	uploadResult = common.SubstringBeforeLast(uploadResult, "bits/sec")
+	uploadResult = common.SubstringAfterLast(uploadResult, "Bytes")
+	uploadResult = strings.ReplaceAll(uploadResult, " ", "")
+
+	result = &TestResult{
+		BoxPath:     boxPath,
+		Stack:       stackName,
+		MTU:         mtu,
+		Flags:       flags,
+		MultiThread: multiThread,
+		UploadSpeed: uploadResult,
+	}
+
+	downloadProcess := shell.Exec(iperf3Path, append(args, "-R")...)
+	output, err = downloadProcess.Read()
+	if err != nil {
+		boxProcess.Process.Signal(syscall.SIGKILL)
+		serverProcess.Process.Signal(syscall.SIGKILL)
+		println(output)
+		return
+	}
+
+	downloadResult := common.SubstringBeforeLast(output, "iperf Done.")
+	downloadResult = common.SubstringBeforeLast(downloadResult, "receiver")
+	downloadResult = common.SubstringBeforeLast(downloadResult, "bits/sec")
+	downloadResult = common.SubstringAfterLast(downloadResult, "Bytes")
+	downloadResult = strings.ReplaceAll(downloadResult, " ", "")
+
+	result.DownloadSpeed = downloadResult
+
+	printArgs := []any{boxPath, stackName, mtu, "upload", uploadResult, "download", downloadResult}
+	if len(flags) > 0 {
+		printArgs = append(printArgs, "flags", strings.Join(flags, " "))
+	}
+	if multiThread {
+		printArgs = append(printArgs, "(-P 10)")
+	}
+	fmt.Println(printArgs...)
+	err = boxProcess.Process.Signal(syscall.SIGTERM)
+	if err != nil {
+		return
+	}
+
+	err = serverProcess.Process.Signal(syscall.SIGTERM)
+	if err != nil {
+		return
+	}
+
+	boxDone := make(chan struct{})
+	go func() {
+		boxProcess.Cmd.Wait()
+		close(boxDone)
+	}()
+
+	serverDone := make(chan struct{})
+	go func() {
+		serverProcess.Process.Wait()
+		close(serverDone)
+	}()
+
+	select {
+	case <-boxDone:
+	case <-time.After(2 * time.Second):
+		boxProcess.Process.Kill()
+	case <-time.After(4 * time.Second):
+		println("box process did not close!")
+		os.Exit(1)
+	}
+
+	select {
+	case <-serverDone:
+	case <-time.After(2 * time.Second):
+		serverProcess.Process.Kill()
+	case <-time.After(4 * time.Second):
+		println("server process did not close!")
+		os.Exit(1)
+	}
+
+	return
+}
+
+type stderrWriter struct{}
+
+func (w *stderrWriter) Write(p []byte) (n int, err error) {
+	return os.Stderr.Write(p)
+}

+ 2 - 2
go.mod

@@ -28,13 +28,13 @@ require (
 	github.com/sagernet/gomobile v0.1.7
 	github.com/sagernet/gomobile v0.1.7
 	github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
 	github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
 	github.com/sagernet/quic-go v0.52.0-beta.1
 	github.com/sagernet/quic-go v0.52.0-beta.1
-	github.com/sagernet/sing v0.6.12-0.20250710134112-2f96887176ff
+	github.com/sagernet/sing v0.6.12-0.20250718132236-21daaa465e88
 	github.com/sagernet/sing-mux v0.3.2
 	github.com/sagernet/sing-mux v0.3.2
 	github.com/sagernet/sing-quic v0.5.0-beta.3
 	github.com/sagernet/sing-quic v0.5.0-beta.3
 	github.com/sagernet/sing-shadowsocks v0.2.8
 	github.com/sagernet/sing-shadowsocks v0.2.8
 	github.com/sagernet/sing-shadowsocks2 v0.2.1
 	github.com/sagernet/sing-shadowsocks2 v0.2.1
 	github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
 	github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
-	github.com/sagernet/sing-tun v0.6.10-0.20250710134146-6aeee7bc7db4
+	github.com/sagernet/sing-tun v0.6.10-0.20250718030019-3af7305b853e
 	github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88
 	github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88
 	github.com/sagernet/smux v1.5.34-mod.2
 	github.com/sagernet/smux v1.5.34-mod.2
 	github.com/sagernet/tailscale v1.80.3-mod.5
 	github.com/sagernet/tailscale v1.80.3-mod.5

+ 4 - 4
go.sum

@@ -168,8 +168,8 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
 github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
 github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
 github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
 github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
 github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
 github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
-github.com/sagernet/sing v0.6.12-0.20250710134112-2f96887176ff h1:2x29M8i86PwGEGqvWI1zJWw2BiT6WRg97ELRABZo0Xc=
-github.com/sagernet/sing v0.6.12-0.20250710134112-2f96887176ff/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
+github.com/sagernet/sing v0.6.12-0.20250718132236-21daaa465e88 h1:Wu6hu+JsZ2gsFRJMqGzaZJ4ctGnmNrLGm9ckmotBFOs=
+github.com/sagernet/sing v0.6.12-0.20250718132236-21daaa465e88/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
 github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
 github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
 github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
 github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
 github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric=
 github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric=
@@ -180,8 +180,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
 github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
 github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
-github.com/sagernet/sing-tun v0.6.10-0.20250710134146-6aeee7bc7db4 h1:OCGg2SecHgMhs+juCLoYAujZrpwWCy0/f7wF6KEF4EU=
-github.com/sagernet/sing-tun v0.6.10-0.20250710134146-6aeee7bc7db4/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
+github.com/sagernet/sing-tun v0.6.10-0.20250718030019-3af7305b853e h1:IH6N3oTKs4bqXLoKP7uFfIAAuZHCNq6OCV4MlrGGLqs=
+github.com/sagernet/sing-tun v0.6.10-0.20250718030019-3af7305b853e/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
 github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88 h1:0pVm8sPOel+BoiCddW3pV3cKDKEaSioVTYDdTSKjyFI=
 github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88 h1:0pVm8sPOel+BoiCddW3pV3cKDKEaSioVTYDdTSKjyFI=
 github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
 github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
 github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
 github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=

+ 2 - 0
protocol/tun/inbound.go

@@ -180,6 +180,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 		outputMark = tun.DefaultAutoRedirectOutputMark
 		outputMark = tun.DefaultAutoRedirectOutputMark
 	}
 	}
 	networkManager := service.FromContext[adapter.NetworkManager](ctx)
 	networkManager := service.FromContext[adapter.NetworkManager](ctx)
+	multiPendingPackets := C.IsDarwin && ((options.Stack == "gvisor" && tunMTU < 32768) || (options.Stack != "gvisor" && options.MTU <= 9000))
 	inbound := &Inbound{
 	inbound := &Inbound{
 		tag:            tag,
 		tag:            tag,
 		ctx:            ctx,
 		ctx:            ctx,
@@ -213,6 +214,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 			IncludePackage:           options.IncludePackage,
 			IncludePackage:           options.IncludePackage,
 			ExcludePackage:           options.ExcludePackage,
 			ExcludePackage:           options.ExcludePackage,
 			InterfaceMonitor:         networkManager.InterfaceMonitor(),
 			InterfaceMonitor:         networkManager.InterfaceMonitor(),
+			EXP_MultiPendingPackets:  multiPendingPackets,
 		},
 		},
 		udpTimeout:        udpTimeout,
 		udpTimeout:        udpTimeout,
 		stack:             options.Stack,
 		stack:             options.Stack,