// Copyright 2021 Vitaliy Filippov // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package readbenchfs import ( "golang.org/x/net/context" "os" "io" "math/rand" "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseutil" ) type readBenchFS struct { fuseutil.NotImplementedFileSystem buf []byte } const FILE_SIZE = 1024*1024*1024*1024 var _ fuseutil.FileSystem = &readBenchFS{} func NewReadBenchServer() (server fuse.Server, err error) { // 1 GB of random data to exceed CPU cache buf := make([]byte, 1024*1024*1024) rand.Read(buf) server = fuseutil.NewFileSystemServer(&readBenchFS{ buf: buf, }) return } func (fs *readBenchFS) StatFS( ctx context.Context, op *fuseops.StatFSOp) error { return nil } func (fs *readBenchFS) LookUpInode( ctx context.Context, op *fuseops.LookUpInodeOp) error { if op.Name == "test" { op.Entry = fuseops.ChildInodeEntry{ Child: 2, Attributes: fuseops.InodeAttributes{ Size: FILE_SIZE, Nlink: 1, Mode: 0444, }, } return nil } return fuse.ENOENT } func (fs *readBenchFS) GetInodeAttributes( ctx context.Context, op *fuseops.GetInodeAttributesOp) error { if op.Inode == 1 { op.Attributes = fuseops.InodeAttributes{ Nlink: 1, Mode: 0755 | os.ModeDir, } return nil } else if op.Inode == 2 { op.Attributes = fuseops.InodeAttributes{ Size: FILE_SIZE, Nlink: 1, Mode: 0444, } return nil } return fuse.ENOENT } func (fs *readBenchFS) OpenDir( ctx context.Context, op *fuseops.OpenDirOp) error { // Allow opening any directory. return nil } func (fs *readBenchFS) ReadDir( ctx context.Context, op *fuseops.ReadDirOp) error { if op.Inode != 1 { return fuse.ENOENT } if op.Offset > 0 { return nil } entries := []fuseutil.Dirent{ fuseutil.Dirent{ Offset: 1, Inode: 2, Name: "test", Type: fuseutil.DT_File, }, } for _, e := range entries[op.Offset:] { n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e) if n == 0 { break } op.BytesRead += n } return nil } func (fs *readBenchFS) OpenFile( ctx context.Context, op *fuseops.OpenFileOp) error { // Allow opening any file. return nil } func (fs *readBenchFS) ReadFile( ctx context.Context, op *fuseops.ReadFileOp) error { if op.Offset > FILE_SIZE { return io.EOF } end := op.Offset+int64(len(op.Dst)) if end > FILE_SIZE { end = FILE_SIZE } buflen := int64(len(fs.buf)) for pos := op.Offset; pos < end; { s := pos % buflen e := buflen if e-s > end-pos { e = s+end-pos } copy(op.Dst[pos-op.Offset : ], fs.buf[s : ]) pos = op.Offset+e } op.BytesRead = int(end-op.Offset) return nil } func (fs *readBenchFS) VectoredRead( ctx context.Context, op *fuseops.VectoredReadOp) error { if op.Offset > FILE_SIZE { return io.EOF } end := op.Offset+op.Size if end > FILE_SIZE { end = FILE_SIZE } buflen := int64(len(fs.buf)) for pos := op.Offset; pos < end; { s := pos % buflen e := buflen if e-s > end-pos { e = s+end-pos } op.Data = append(op.Data, fs.buf[s : e]) pos = op.Offset+e } op.BytesRead = int(end-op.Offset) return nil } func (fs *readBenchFS) ReleaseDirHandle( ctx context.Context, op *fuseops.ReleaseDirHandleOp) error { return nil } func (fs *readBenchFS) GetXattr( ctx context.Context, op *fuseops.GetXattrOp) error { return nil } func (fs *readBenchFS) ListXattr( ctx context.Context, op *fuseops.ListXattrOp) error { return nil } func (fs *readBenchFS) ForgetInode( ctx context.Context, op *fuseops.ForgetInodeOp) error { return nil } func (fs *readBenchFS) ReleaseFileHandle( ctx context.Context, op *fuseops.ReleaseFileHandleOp) error { return nil } func (fs *readBenchFS) FlushFile( ctx context.Context, op *fuseops.FlushFileOp) error { return nil }