golang快速并行目录遍历插件库fastwalk的使用

Golang快速并行目录遍历插件库fastwalk的使用

简介

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

  • filepath.WalkDir快约2.5倍(macOS)、4倍(Linux)、6倍(Windows)
  • 内存分配减少50%
  • 内存分配次数减少25%
  • godirwalk快4-5倍(跨平台)

特性

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

使用示例

以下是一个类似于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)

              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%

Linux

              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%

Windows

              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%

fastwalk在大多数情况下都显著提高了性能,特别是在Windows平台上时间性能提升高达83%。


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

1 回复

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


Golang快速并行目录遍历插件库fastwalk使用指南

fastwalk是一个高效的并行目录遍历库,相比标准库的filepath.Walk能显著提高遍历速度,特别适合处理大型目录结构。

安装fastwalk

go get github.com/karrick/godirwalk

基本使用方法

1. 简单遍历

package main

import (
	"fmt"
	"github.com/karrick/godirwalk"
	"os"
)

func main() {
	root := "/path/to/directory"
	
	err := godirwalk.Walk(root, &godirwalk.Options{
		Callback: func(path string, de *godirwalk.Dirent) error {
			fmt.Printf("%s %s\n", de.ModeType(), path)
			return nil
		},
		ErrorCallback: func(path string, err error) godirwalk.ErrorAction {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			return godirwalk.SkipNode
		},
		Unsorted: true, // 设置为true可提高性能
	})
	
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error walking directory: %v\n", err)
	}
}

2. 并行遍历

fastwalk的并行版本可以显著提高性能:

package main

import (
	"fmt"
	"github.com/karrick/godirwalk"
	"os"
	"runtime"
)

func main() {
	root := "/path/to/directory"
	
	// 根据CPU核心数设置并发度
	concurrency := runtime.NumCPU()
	
	err := godirwalk.Walk(root, &godirwalk.Options{
		Callback: func(path string, de *godirwalk.Dirent) error {
			fmt.Printf("%s %s\n", de.ModeType(), path)
			return nil
		},
		ErrorCallback: func(path string, err error) godirwalk.ErrorAction {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			return godirwalk.SkipNode
		},
		Unsorted: true,
		Parallel: true, // 启用并行
		Concurrency: concurrency, // 设置并发数
	})
	
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error walking directory: %v\n", err)
	}
}

高级功能

1. 跳过特定目录

err := godirwalk.Walk(root, &godirwalk.Options{
	Callback: func(path string, de *godirwalk.Dirent) error {
		if de.IsDir() && path == "/path/to/skip" {
			return godirwalk.SkipThis
		}
		fmt.Println(path)
		return nil
	},
})

2. 获取文件信息

err := godirwalk.Walk(root, &godirwalk.Options{
	Callback: func(path string, de *godirwalk.Dirent) error {
		info, err := de.Info()
		if err != nil {
			return err
		}
		fmt.Printf("%s: %d bytes\n", path, info.Size())
		return nil
	},
})

3. 仅处理特定类型文件

err := godirwalk.Walk(root, &godirwalk.Options{
	Callback: func(path string, de *godirwalk.Dirent) error {
		if de.IsRegular() && strings.HasSuffix(path, ".go") {
			fmt.Println("Go file:", path)
		}
		return nil
	},
})

性能优化建议

  1. 启用Unsorted选项:设置为true可以避免排序,提高性能
  2. 合理设置并发度:通常设置为CPU核心数
  3. 避免在Callback中做耗时操作:如果需要对文件进行复杂处理,考虑收集路径后批量处理
  4. 使用ScratchBuffer:对于非常大的目录,可以重用缓冲区
buffer := make([]byte, 64*1024) // 64KB buffer
err := godirwalk.Walk(root, &godirwalk.Options{
	ScratchBuffer: buffer,
	// ...其他选项
})

与标准库对比

标准库的filepath.Walk是单线程的,而fastwalk通过以下方式提高性能:

  • 并行处理目录项
  • 减少内存分配
  • 避免不必要的排序
  • 使用更高效的系统调用

对于包含数百万文件的目录,fastwalk可以比标准库快5-10倍。

注意事项

  1. 并行遍历时文件顺序是不确定的
  2. 错误处理需要特别注意,某些错误可能需要跳过而非终止遍历
  3. 符号链接默认不会跟随,需要额外处理

fastwalk是一个强大而高效的目录遍历工具,特别适合需要处理大量文件的场景。根据具体需求选择合适的配置选项,可以显著提高程序性能。

回到顶部