Skip to content

Instantly share code, notes, and snippets.

@kamijin-fanta
Last active March 31, 2022 09:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kamijin-fanta/b29df6b5db154f860767478badc14064 to your computer and use it in GitHub Desktop.
Save kamijin-fanta/b29df6b5db154f860767478badc14064 to your computer and use it in GitHub Desktop.

stat benchmark

$ df -T /tmp
Filesystem     Type 1K-blocks     Used Available Use% Mounted on
/dev/sdc       ext4 263174212 72152352 177583704  29% /
$ go test -test.bench  . -benchmem
goos: linux
goarch: amd64
pkg: b29df6b5db154f860767478badc14064
cpu: AMD Ryzen 5 PRO 3400GE w/ Radeon Vega Graphics
BenchmarkStat1k/syscall.Stat-8              1868            648497 ns/op           48000 B/op       1000 allocs/op
BenchmarkStat1k/os.Stat-8                   1324            883063 ns/op          256002 B/op       2000 allocs/op
BenchmarkStat10k/syscall.Stat-8              142           8423880 ns/op          480003 B/op      10000 allocs/op
BenchmarkStat10k/os.Stat-8                   100          10550826 ns/op         2560015 B/op      20000 allocs/op
PASS
ok      b29df6b5db154f860767478badc14064        6.897s
module b29df6b5db154f860767478badc14064
go 1.17
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.1.0 // indirect
github.com/stretchr/testify v1.7.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package main
import (
"fmt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
)
func TestSpaseFileSize(t *testing.T) {
dataDir, err := ioutil.TempDir("", "go-spase-test")
require.Nil(t, err)
t.Logf("dataDir %s", dataDir)
defer os.RemoveAll(dataDir)
err = os.MkdirAll(dataDir, os.ModePerm)
if err != nil {
panic(err)
}
f, err := os.OpenFile(dataDir+"/test", os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm)
require.Nil(t, err)
defer f.Close()
stat, err := f.Stat()
require.Nil(t, err)
if syscallStat, ok := stat.Sys().(*syscall.Stat_t); ok {
t.Logf("size=%d, blocks=%d, blksize=%d", syscallStat.Size, syscallStat.Blocks, syscallStat.Blksize)
assert.Equal(t, int64(0), syscallStat.Size)
assert.Equal(t, int64(0), syscallStat.Blocks)
}
_, err = f.WriteAt([]byte{1}, 10000)
require.Nil(t, err)
_ = f.Sync()
stat, err = f.Stat()
require.Nil(t, err)
if syscallStat, ok := stat.Sys().(*syscall.Stat_t); ok {
t.Logf("size=%d, blocks=%d, blksize=%d", syscallStat.Size, syscallStat.Blocks, syscallStat.Blksize)
assert.Equal(t, int64(10001), syscallStat.Size)
assert.Equal(t, int64(8), syscallStat.Blocks)
}
buff := make([]byte, 4096)
_, err = f.WriteAt(buff, 0)
require.Nil(t, err)
_ = f.Sync()
stat, err = f.Stat()
require.Nil(t, err)
if syscallStat, ok := stat.Sys().(*syscall.Stat_t); ok {
t.Logf("size=%d, blocks=%d, blksize=%d", syscallStat.Size, syscallStat.Blocks, syscallStat.Blksize)
assert.Equal(t, int64(10001), syscallStat.Size)
assert.Equal(t, int64(16), syscallStat.Blocks)
}
}
func BenchmarkStat1k(b *testing.B) {
names, closer := initFiles(1000)
defer closer()
b.Run("syscall.Stat", benchSyscallStat(names))
b.Run("os.Stat", benchOsStat(names))
}
func BenchmarkStat10k(b *testing.B) {
names, closer := initFiles(10000)
defer closer()
b.Run("syscall.Stat", benchSyscallStat(names))
b.Run("os.Stat", benchOsStat(names))
}
func benchSyscallStat(names []string) func(b *testing.B) {
return func(b *testing.B) {
var totalBlocks int64
for i := 0; i < b.N; i++ {
var stat syscall.Stat_t
for _, name := range names {
err := syscall.Stat(name, &stat)
if err != nil {
panic(err)
}
totalBlocks += stat.Blocks
}
}
}
}
func benchOsStat(names []string) func(b *testing.B) {
return func(b *testing.B) {
var totalBlocks int64
for i := 0; i < b.N; i++ {
for _, name := range names {
stat, err := os.Stat(name)
if err != nil {
panic(err)
}
syscallStat := stat.Sys().(*syscall.Stat_t)
totalBlocks += syscallStat.Blocks
}
}
}
}
func initFiles(numFiles int) ([]string, func() error) {
dataDir, err := ioutil.TempDir("", "go-stat-bench")
if err != nil {
panic(err)
}
names := make([]string, numFiles)
for i := 0; i < numFiles; i++ {
name := filepath.Join(dataDir, fmt.Sprintf("%5d", i))
names[i] = name
f, err := os.Create(name)
if err != nil {
panic(err)
}
err = f.Close()
if err != nil {
panic(err)
}
}
return names, func() error {
return os.RemoveAll(dataDir)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment