benchmarks/read_within_file/main.go (135 lines of code) (raw):
// Copyright 2015 Google LLC
//
// 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 main
import (
"errors"
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"time"
"github.com/googlecloudplatform/gcsfuse/v2/benchmarks/internal/format"
)
var fFile = flag.String("file", "", "Path to file to read.")
var fRandom = flag.Bool("random", false, "Read randomly? Otherwise sequentially.")
var fDuration = flag.Duration("duration", 10*time.Second, "How long to run.")
var fReadSize = flag.Int("read_size", 1<<20, "Size of each call to read(2).")
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
func readRandom(
r io.ReaderAt,
fileSize int64,
readSize int,
desiredDuration time.Duration) (err error) {
// Make sure the logic below for choosing offsets works.
if fileSize < int64(readSize) {
err = fmt.Errorf(
"file size of %d bytes not large enough for reads of %d bytes",
fileSize,
readSize)
return
}
buf := make([]byte, readSize)
start := time.Now()
var readCount int64
var bytesRead int64
for time.Since(start) < desiredDuration {
// Choose a random offset at which to read.
off := rand.Int63n(fileSize - int64(readSize))
// Read, ignoring io.EOF which io.ReaderAt is allowed to return for reads
// that abut the end of the file.
var n int
n, err = r.ReadAt(buf, off)
switch {
case err == io.EOF && n == readSize:
err = nil
case err != nil:
err = fmt.Errorf("ReadAt: %w", err)
return
}
readCount++
bytesRead += int64(n)
}
d := time.Since(start)
// Report.
seconds := float64(d) / float64(time.Second)
readsPerSec := float64(readCount) / seconds
fmt.Printf(
"Read %d times (%s) in %v (%.1f Hz)\n",
readCount,
format.Bytes(float64(bytesRead)),
d,
readsPerSec)
fmt.Println()
return
}
func readSequential(
r io.ReadSeeker,
readSize int,
desiredDuration time.Duration) (err error) {
buf := make([]byte, readSize)
start := time.Now()
var readCount int64
var bytesRead int64
for time.Since(start) < desiredDuration {
var n int
n, err = r.Read(buf)
switch {
case err == io.EOF:
_, err = r.Seek(0, 0)
if err != nil {
err = fmt.Errorf("seek: %w", err)
return
}
case err != nil:
err = fmt.Errorf("read: %w", err)
}
bytesRead += int64(n)
readCount++
}
d := time.Since(start)
// Report.
seconds := float64(d) / float64(time.Second)
readsPerSec := float64(readCount) / seconds
bytesPerSec := float64(bytesRead) / seconds
fmt.Printf(
"Read %d times (%s) in %v (%.1f Hz, %s/s)\n",
readCount,
format.Bytes(float64(bytesRead)),
d,
readsPerSec,
format.Bytes(bytesPerSec))
fmt.Println()
return
}
////////////////////////////////////////////////////////////////////////
// main logic
////////////////////////////////////////////////////////////////////////
func run() (err error) {
if *fFile == "" {
err = errors.New("you must set --file")
return
}
// Open the file for reading.
f, err := os.Open(*fFile)
if err != nil {
return
}
// Find its size.
size, err := f.Seek(0, 2)
if err != nil {
err = fmt.Errorf("seek: %w", err)
return
}
log.Printf("%s has size %s.", f.Name(), format.Bytes(float64(size)))
// Perform reads.
if *fRandom {
err = readRandom(f, size, *fReadSize, *fDuration)
if err != nil {
err = fmt.Errorf("readRandom: %w", err)
return
}
} else {
err = readSequential(f, *fReadSize, *fDuration)
if err != nil {
err = fmt.Errorf("readSequential: %w", err)
return
}
}
return
}
func main() {
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
flag.Parse()
err := run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}