golang高性能并行目录遍历插件库fastwalkfzf的使用

Golang高性能并行目录遍历插件库fastwalk的使用

fastwalk是一个用于Golang的快速并行目录遍历库,它提供了比标准库filepath.WalkDir更高效的性能。

特性

  • 快速:使用多个goroutine并发遍历文件系统并调用回调函数
  • 安全的符号链接遍历(通过Config.Follow配置)
  • filepath.WalkDir相同的行为和回调签名
  • 提供忽略重复文件和目录的包装函数:IgnoreDuplicateFiles()IgnoreDuplicateDirs()
  • 在macOS、Linux和Windows上经过广泛测试

性能优势

fastwalk相比标准库filepath.WalkDir

  • 在macOS上快约2.5倍
  • 在Linux上快约4倍
  • 在Windows上快约6倍
  • 内存分配减少50%
  • 内存分配次数减少25%

相比godirwalk库,fastwalk在各种操作系统上都快约4-5倍。

使用示例

下面是一个类似POSIX find工具的简单实现示例:

// fwfind是一个类似POSIX find的示例程序,但更快(这是一个示例)
package main

import (
	"flag"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"

	"github.com/charlievieth/fastwalk"
)

const usageMsg = `Usage: %[1]s [-L] [-name] [PATH...]:

%[1]s是POSIX find工具的简单替代品

`

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stdout, usageMsg, filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}
	pattern := flag.String("name", "", "匹配文件名的模式")
	followLinks := flag.Bool("L", false, "跟随符号链接")
	flag.Parse()

	// 如果没有提供路径,默认为当前目录"."
	args := flag.Args()
	if len(args) == 0 {
		args = append(args, ".")
	}

	// 如果提供了"-L"标志,则跟随链接
	conf := fastwalk.Config{
		Follow: *followLinks,
	}

	walkFn := func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
			return nil // 返回错误会停止迭代
		}
		if *pattern != "" {
			if ok, err := filepath.Match(*pattern, d.Name()); !ok {
				// 无效模式(err != nil)或名称不匹配
				return err
			}
		}
		_, err = fmt.Println(path)
		return err
	}
	for _, root := range args {
		if err := fastwalk.Walk(&conf, root, walkFn); err != nil {
			fmt.Fprintf(os.Stderr, "%s: %v\n", root, err)
			os.Exit(1)
		}
	}
}

基准测试

Darwin (macOS)

硬件配置:

goos: darwin
goarch: arm64
cpu: Apple M1 Max

filepath.WalkDir vs. fastwalk.Walk():

              filepath       fastwalk       delta
time/op       27.9ms ± 1%    13.0ms ± 1%    -53.33%
alloc/op      4.33MB ± 0%    2.14MB ± 0%    -50.55%
allocs/op     50.9k ± 0%     37.7k ± 0%     -26.01%

godirwalk.Walk() vs. fastwalk.Walk():

              godirwalk      fastwalk       delta
time/op       58.5ms ± 3%    18.0ms ± 2%    -69.30%
alloc/op      25.3MB ± 0%    2.1MB ± 0%     -91.55%
allocs/op     57.6k ± 0%     37.7k ± 0%     -34.59%

Linux

硬件配置:

goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
drive: Samsung SSD 970 PRO 1TB

filepath.WalkDir vs. fastwalk.Walk():

              filepath       fastwalk       delta
time/op       10.1ms ± 2%    2.8ms ± 2%     -72.83%
alloc/op      2.44MB ± 0%    1.70MB ± 0%    -30.46%
allocs/op     47.2k ± 0%     36.9k ± 0%     -21.80%

godirwalk.Walk() vs. fastwalk.Walk():

              filepath       fastwalk       delta
time/op       13.7ms ±16%    2.8ms ± 2%     -79.88%
alloc/op      7.48MB ± 0%    1.70MB ± 0%    -77.34%
allocs/op     53.8k ± 0%     36.9k ± 0%     -31.38%

Windows

硬件配置:

goos: windows
goarch: amd64
pkg: github.com/charlievieth/fastwalk
cpu: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz

filepath.WalkDir vs. fastwalk.Walk():

              filepath       fastwalk       delta
time/op       88.0ms ± 1%    14.6ms ± 1%    -83.47%
alloc/op      5.68MB ± 0%    6.76MB ± 0%    +19.01%
allocs/op     69.6k ± 0%     90.4k ± 0%     +29.87%

godirwalk.Walk() vs. fastwalk.Walk():

              filepath       fastwalk       delta
time/op       87.4ms ± 1%    14.6ms ± 1%    -83.34%
alloc/op      6.14MB ± 0%    6.76MB ± 0%    +10.24%
allocs/op     100k ± 0%      90k ± 0%       -9.59%

更多关于golang高性能并行目录遍历插件库fastwalkfzf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能并行目录遍历插件库fastwalkfzf的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 高性能并行目录遍历插件库 fastwalk 和 fzf 使用指南

fastwalk 简介

fastwalk 是一个 Go 语言编写的高性能并行目录遍历库,特别适合需要快速扫描大型文件系统的场景。它比标准库的 filepath.Walk 性能更高,因为它使用了并行处理机制。

fastwalk 基本使用

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/karrick/godirwalk"
)

func main() {
	root := "/path/to/directory" // 替换为你要遍历的目录
	
	options := &godirwalk.Options{
		Callback: func(path string, de *godirwalk.Dirent) error {
			// 处理每个文件和目录
			if de.IsDir() {
				fmt.Printf("目录: %s\n", path)
			} else {
				fmt.Printf("文件: %s\n", path)
			}
			return nil
		},
		ErrorCallback: func(path string, err error) godirwalk.ErrorAction {
			// 错误处理
			fmt.Printf("访问 %s 时出错: %v\n", path, err)
			return godirwalk.SkipNode // 跳过出错的文件/目录
		},
		Unsorted: true, // 设置为 true 以获得更好的性能
	}

	// 开始遍历
	err := godirwalk.Walk(root, options)
	if err != nil {
		fmt.Fprintf(os.Stderr, "遍历目录出错: %v\n", err)
		os.Exit(1)
	}
}

fastwalk 高级特性

  1. 并行处理:

    options := &godirwalk.Options{
        Callback: func(path string, de *godirwalk.Dirent) error {
            // 处理逻辑
            return nil
        },
        PostChildrenCallback: func(path string, de *godirwalk.Dirent) error {
            // 在访问完子目录后执行
            return nil
        },
        NumberOfWorkers: 4, // 设置并行工作线程数
    }
    
  2. 过滤特定文件:

    options := &godirwalk.Options{
        Callback: func(path string, de *godirwalk.Dirent) error {
            if filepath.Ext(path) == ".go" {
                fmt.Println("Go 文件:", path)
            }
            return nil
        },
    }
    

fzf 集成

fzf 是一个命令行模糊查找工具,可以与 fastwalk 结合使用来构建高效的文件搜索工具。

基本 fzf 集成示例

package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/karrick/godirwalk"
)

func main() {
	root := "/path/to/search" // 替换为搜索目录
	
	// 使用 fastwalk 收集文件
	var files []string
	options := &godirwalk.Options{
		Callback: func(path string, de *godirwalk.Dirent) error {
			if !de.IsDir() {
				files = append(files, path)
			}
			return nil
		},
		Unsorted: true,
	}

	err := godirwalk.Walk(root, options)
	if err != nil {
		fmt.Fprintf(os.Stderr, "遍历目录出错: %v\n", err)
		os.Exit(1)
	}

	// 准备 fzf 输入
	cmd := exec.Command("fzf", "--multi", "--height=40%", "--reverse")
	cmd.Stderr = os.Stderr
	
	stdin, err := cmd.StdinPipe()
	if err != nil {
		fmt.Fprintf(os.Stderr, "创建管道出错: %v\n", err)
		os.Exit(1)
	}

	// 启动 fzf
	if err := cmd.Start(); err != nil {
		fmt.Fprintf(os.Stderr, "启动 fzf 出错: %v\n", err)
		os.Exit(1)
	}

	// 向 fzf 发送文件列表
	for _, file := range files {
		fmt.Fprintln(stdin, file)
	}
	stdin.Close()

	// 等待用户选择
	if err := cmd.Wait(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 130 {
			// 用户按 Ctrl-C 退出
			fmt.Println("取消选择")
			return
		}
		fmt.Fprintf(os.Stderr, "fzf 执行出错: %v\n", err)
		os.Exit(1)
	}
}

更高级的 fzf 集成

// 使用 fzf 预览功能
cmd := exec.Command("fzf", 
    "--multi",
    "--height=40%",
    "--reverse",
    "--preview", "bat --color=always {}",
    "--preview-window=right:60%")

// 使用自定义布局
cmd := exec.Command("fzf",
    "--multi",
    "--layout=reverse",
    "--border",
    "--height=90%",
    "--info=inline",
    "--color=light")

性能优化技巧

  1. 批量处理:

    var batch []string
    batchSize := 1000
    
    options := &godirwalk.Options{
        Callback: func(path string, de *godirwalk.Dirent) error {
            if !de.IsDir() {
                batch = append(batch, path)
                if len(batch) >= batchSize {
                    // 处理批量数据
                    processBatch(batch)
                    batch = batch[:0] // 清空批次
                }
            }
            return nil
        },
        PostChildrenCallback: func(path string, de *godirwalk.Dirent) error {
            if len(batch) > 0 {
                processBatch(batch)
                batch = batch[:0]
            }
            return nil
        },
    }
    
  2. 限制深度:

    maxDepth := 3
    options := &godirwalk.Options{
        Callback: func(path string, de *godirwalk.Dirent) error {
            depth := len(filepath.SplitList(path)) - len(filepath.SplitList(root))
            if depth > maxDepth {
                return filepath.SkipDir
            }
            // 处理逻辑
            return nil
        },
    }
    
  3. 忽略特定目录:

    ignoreDirs := map[string]bool{
        ".git": true,
        "node_modules": true,
    }
    
    options := &godirwalk.Options{
        Callback: func(path string, de *godirwalk.Dirent) error {
            if de.IsDir() && ignoreDirs[de.Name()] {
                return filepath.SkipDir
            }
            // 处理逻辑
            return nil
        },
    }
    

总结

fastwalk 和 fzf 的结合为 Go 开发者提供了强大的文件系统遍历和交互式选择能力。fastwalk 提供了高性能的并行目录遍历,而 fzf 则添加了用户友好的交互界面。这种组合特别适合需要处理大型代码库或文件系统的工具开发。

通过调整并行工作线程数、批量处理策略和适当的过滤条件,可以进一步优化性能。在实际应用中,可以根据具体需求调整 fzf 的界面和预览选项,以提供最佳的用户体验。

回到顶部