すべてのプロダクト
Search
ドキュメントセンター

Object Storage Service:ossfs 2.0 のパフォーマンステスト

最終更新日:May 01, 2025

このテストでは、オブジェクトの読み取り/書き込み速度や同時実行シナリオでのパフォーマンスなど、さまざまなシナリオで ossfs 2.0 のパフォーマンスを徹底的に評価します。このトピックを参照することで、ossfs 2.0 をより深く理解し、使用することができます。

テスト環境

  • ハードウェア

    • インスタンスタイプ: ecs.g7.32xlarge

    • vCPU: 128 vCPU

    • メモリ: 512 GiB

  • ソフトウェア

    • オペレーティングシステム: Alibaba Cloud Linux 3.2104 LTS 64 ビット

    • カーネルバージョン: 5.10.134-18.al8.x86_64

    • ossfs バージョン: ossfs 2.0.0beta、ossfs 1.91.4

マウント構成

このセクションでは、ossfs のパフォーマンステストでマウントオプションを指定する方法の例を示します。

Ossfs 2.0.0beta

  • ossfs2.conf 構成ファイルを使用する

    バケットをマウントするときに、アップロードするファイルのパートサイズを 33,554,432 バイトに設定するには、次のサンプルコードを使用します。

    # バケットが配置されているリージョンのエンドポイント。
    --oss_endpoint=https://oss-cn-hangzhou-internal.aliyuncs.com
    
    # バケットの名前。
    --oss_bucket=bucket-test
    
    # AccessKey ID と AccessKey シークレット。
    --oss_access_key_id=yourAccessKeyID
    --oss_access_key_secret=yourAccessKeySecret
    
    # アップロードされるファイルのパートサイズ。単位: バイト。
    --upload_buffer_size=33554432
  • mount コマンドを実行する

    ossfs2.conf 構成ファイルを指定し、bucket-test バケットを /mnt/ossfs2/ ローカルディレクトリにマウントするには、次のコマンドを実行します。

    ossfs2 mount /mnt/ossfs2/ -c /etc/ossfs2.conf 

Ossfs 1.91.4

bucket-test バケットを /mnt/ossfs ローカルディレクトリにマウントし、ダイレクトリードモードとキャッシュ最適化機能を有効にするには、次のコマンドを実行します。

ossfs bucket-test /mnt/ossfs -ourl=https://oss-cn-hangzhou-internal.aliyuncs.com -odirect_read -oreaddir_optimize

テストシナリオ

ossfs 2.0.0beta と ossfs 1.91.4 を使用してバケットをマウントした後、flexible I/O tester (FIO) を使用して、ossfs 2.0 と ossfs 1.0 の基本的な読み取りおよび書き込み機能をテストします。テストシナリオと結果は次のとおりです。

単一スレッドを使用して 100 GB のデータをシーケンシャルに直接書き込む

説明

ossfs 1.0 の書き込みパフォーマンスは、ディスクパフォーマンスによって制限されます。

  • テストコマンド

    FIO を使用して、file-100G という名前の単一スレッドテストタスクを実行し、1 MB のパートサイズで 100 GB のデータを /mnt/oss/fio_direct_write ディレクトリに直接書き込み、テスト結果を出力します。

    fio --name=file-100G --ioengine=libaio --rw=write --bs=1M --size=100G --numjobs=1 --direct=1 --directory=/mnt/oss/fio_direct_write --group_reporting
  • テスト結果

    ossfs バージョン

    帯域幅

    CPU コア使用率 (コアのフルキャパシティは 100%)

    ピークメモリ

    ossfs 2.0

    2.2 GB/s

    207%

    2,167 MB

    ossfs 1.0

    118 MB/s

    5%

    15 MB

単一スレッドを使用して 100 GB のデータをシーケンシャルに読み取る

  • テストコマンド

    ページキャッシュをクリアした後、FIO を使用して、/mnt/oss/fio_direct_write ディレクトリから 1 MB のパートサイズで 100 GB のデータを単一スレッドでシーケンシャルに読み取り、テスト結果を出力します。

    echo 1 > /proc/sys/vm/drop_caches
    fio --name=file-100G --ioengine=libaio --direct=1 --rw=read --bs=1M --directory=/mnt/oss/fio_direct_write --group_reporting --numjobs=1
  • テスト結果

    ossfs バージョン

    帯域幅

    CPU コア使用率 (コアのフルキャパシティは 100%)

    ピークメモリ

    ossfs 2.0

    3.0 GB/s

    378%

    1,617 MB

    ossfs 1.0

    355 MB/s

    50%

    400 MB

複数スレッドを使用して 100 GB のデータをシーケンシャルに読み取る

  • テストファイルの生成

    /mnt/oss/fio マウントディレクトリに、それぞれ 100 GB の 4 つのファイルを作成します。

    fio --name=file-100g --ioengine=libaio --direct=1 --iodepth=1 --numjobs=4 --nrfiles=1 --rw=write --bs=1M  --size=100G --group_reporting --thread --directory=/mnt/oss/fio
  • テストコマンド

    ページキャッシュをクリアした後、FIO を使用して、/mnt/oss/fio ディレクトリにある作成された 4 つのファイルを、4 つのスレッドを使用して 30 秒間、1 MB のパートサイズで同時に読み取り、結果を出力します。

    echo 1 > /proc/sys/vm/drop_caches
    fio --name=file-100g --ioengine=libaio --direct=1 --iodepth=1 --numjobs=4 --nrfiles=1 --rw=read --bs=1M  --size=100G --group_reporting --thread --directory=/mnt/oss/fio --time_based --runtime=30
  • テスト結果

    ossfs バージョン

    帯域幅

    CPU コア使用率 (コアのフルキャパシティは 100%)

    ピークメモリ

    ossfs 2.0

    7.1 GB/s

    1,187%

    6.2 GB

    ossfs 1.0

    1.4 GB/s

    210%

    1.6 GB

128 スレッドを使用して、それぞれ 128 KB の 100,000 ファイルを同時に読み取る

説明

Object Storage Service (OSS) は、Alibaba Cloud アカウントごとに最大 10,000 クエリ/秒 (QPS) を提供します。詳細については、「QPS」をご参照ください。テストで目的のパフォーマンスを達成するには、Alibaba Cloud アカウントの QPS が他のビジネスで使用されていないことを確認してください。

  • テスト手順

    1. rw-bench.go という名前の Go プログラムを作成します。

      このプログラムには、次のコア機能があります。1. 宛先ファイルディレクトリに同じサイズの複数のファイルを同時に作成できます。2. 宛先ファイルディレクトリ内のすべてのファイルを同時に読み取り、n 個のスレッドに読み取りを割り当て、帯域幅データを記録できます。

      サンプルコード

      package main
      
      import (
      	"flag"
      	"fmt"
      	"io"
      	"log"
      	"os"
      	"path/filepath"
      	"sync"
      	"time"
      )
      
      var dir = flag.String("dir", "", "作業ディレクトリ")
      var threads = flag.Int("threads", 128, "同時実行スレッド数")
      var isWrite = flag.Bool("write", false, "ファイル書き込みテスト")
      var fileSize = flag.Int64("file-size-KB", 128, "ファイルサイズ (KB)")
      var fileCount = flag.Int("file-count", 0, "ファイル数")
      
      type fileInfo struct {
      	Name string
      	Size int64
      }
      
      func getFileList(dir string, isWrite bool) []fileInfo {
      	var files []fileInfo
      
      	if isWrite {
      		for i := 0; i < *fileCount; i++ {
      			files = append(files, fileInfo{
      				Name: fmt.Sprintf("%v/%v.dat", dir, i),
      				Size: *fileSize * 1024,
      			})
      		}
      	} else {
      		err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
      			if err != nil {
      				return err
      			}
      			if !info.IsDir() {
      				files = append(files, fileInfo{
      					Name: path,
      					Size: info.Size(),
      				})
      			}
      			return nil
      		})
      
      		if err != nil {
      			log.Fatalf("パス %v の走査中にエラーが発生しました: %v\n", dir, err)
      		}
      	}
      
      	return files
      }
      
      func worker(taskChan <-chan fileInfo, wg *sync.WaitGroup, bytesChan chan<- int64, isWrite bool) {
      	defer wg.Done()
      	buffer := make([]byte, 1024*1024)
      
      	for fInfo := range taskChan {
      		var fd *os.File
      		var err error
      		if isWrite {
      			fd, err = os.OpenFile(fInfo.Name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
      			if err != nil {
      				fmt.Printf("%v の作成/オープンに失敗しました: %v\n", fInfo.Name, err)
      				continue
      			}
      		} else {
      			fd, err = os.OpenFile(fInfo.Name, os.O_RDONLY, 0)
      			if err != nil {
      				fmt.Printf("%v のオープンに失敗しました: %v\n", fInfo.Name, err)
      				continue
      			}
      		}
      
      		offset := int64(0)
      		var totalBytes int64
      		for offset < fInfo.Size {
      			var n int
      
      			if offset+int64(len(buffer)) > fInfo.Size {
      				buffer = buffer[:fInfo.Size-offset]
      			}
      
      			if isWrite {
      				n, err = fd.WriteAt(buffer, offset)
      				if err != nil {
      					fmt.Printf("%v への書き込みに失敗しました (オフセット: %v、エラー: %v)\n", fInfo.Name, offset, err)
      					break
      				}
      			} else {
      				n, err = fd.ReadAt(buffer, offset)
      				if err != nil && err != io.EOF {
      					fmt.Printf("%v からの読み取りに失敗しました (オフセット: %v、エラー: %v)\n", fInfo.Name, offset, err)
      					break
      				}
      			}
      
      			totalBytes += int64(n)
      			offset += int64(n)
      		}
      
      		fd.Close()
      		bytesChan <- totalBytes
      	}
      }
      
      func doBench(dir string, isWrite bool) {
      	files := getFileList(dir, isWrite)
      	var wg sync.WaitGroup
      
      	if isWrite {
      		fmt.Printf("%v ファイルで書き込みベンチマークを開始します\n", len(files))
      	} else {
      		fmt.Printf("%v ファイルで読み取りベンチマークを開始します\n", len(files))
      	}
      
      	taskChan := make(chan fileInfo, 1024)
      
      	go func(taskChan chan<- fileInfo) {
      		for _, fInfo := range files {
      			taskChan <- fInfo
      		}
      		close(taskChan)
      	}(taskChan)
      
      	bytesChan := make(chan int64, 1024)
      	for i := 0; i < *threads; i++ {
      		wg.Add(1)
      		go worker(taskChan, &wg, bytesChan, isWrite)
      	}
      
      	st := time.Now()
      	go func() {
      		wg.Wait()
      		close(bytesChan)
      	}()
      
      	var totalBytes int64
      	for bytes := range bytesChan {
      		totalBytes += bytes
      	}
      
      	ed := time.Now()
      	duration := ed.Sub(st)
      	throughput := float64(totalBytes) / (float64(duration.Nanoseconds()) / 1e9)
      
      	fmt.Printf("合計時間: %v\n", duration)
      	if isWrite {
      		fmt.Printf("書き込みスループット: %.2f MB/s\n", throughput/1000/1000)
      	} else {
      		fmt.Printf("読み取りスループット: %.2f MB/s\n", throughput/1000/1000)
      	}
      }
      
      func main() {
      	flag.Parse()
      
      	workdir := *dir
      	if workdir == "" {
      		flag.Usage()
      		os.Exit(1)
      	}
      
      	if _, err := os.Stat(workdir); err != nil {
      		fmt.Printf("%v へのアクセスに失敗しました: %v\n", workdir, err)
      		os.Exit(1)
      	}
      
      	doBench(workdir, *isWrite)
      }
      
    2. rw-bench.go プログラムファイルをコンパイルします。

      go build rw-bench.go
    3. ローカルファイルシステムにマウントされた OSS バケットディレクトリに、それぞれ 128 KB の 100,000 ファイルを作成します。

      mkdir -p <マウントされたテストファイルのパス> && ./rw-bench --dir <マウントされたテストファイルのパス> --file-size-KB 128 --file-count 100000 --write
    4. ページキャッシュをクリアし、プログラムを実行します。サーバーのレイテンシが安定したテストから得られたテストデータを使用して、テストを 5 回連続して実行した後、使用します。

      echo 1 > /proc/sys/vm/drop_caches
      ./rw-bench --dir <マウントされたテストファイルのパス> --threads 128
  • テスト結果

    ossfs バージョン

    帯域幅

    CPU コア使用率 (コアのフルキャパシティは 100%)

    ピークメモリ

    ossfs 2.0

    1 GB/s

    247%

    212 MB

    ossfs 1.0

    3.5 MB/s

    3%

    200 MB