Tous les produits
Search
Centre de documentation

Container Service for Kubernetes:ossfs 2.0 stress test performance

Dernière mise à jour :Aug 25, 2026

Cette rubrique présente les performances d'ossfs 2,0 dans divers scénarios, notamment le débit en lecture/écriture et les charges de travail concurrentes. Elle fournit des données de référence pour vous aider à évaluer et à adopter ossfs 2,0.

Environnement de test

  • Environnement matériel

    • Type d'instance : ecs.g9i.48xlarge

    • vCPU : 192

    • Mémoire : 768 GiB

    • Bande passante réseau : 64 Gbit/s

  • Environnement logiciel

    • Système d'exploitation : Alibaba Cloud Linux 3.2104 LTS 64 bits

    • Version du noyau : 5.10.134-18.al8.x86_64

    • Versions des outils : ossfs 2.0.4, ossfs 1.91.8 et goofys 0.24.0

Configuration du montage

Pour tous les tests, le volume OSS est monté sur /mnt/oss dans le conteneur.

  • ossfs 2,0

    Ajoutez le paramètre otherOpts pour définir la taille des téléchargements multiparties à 33 554 432 octets.

    upload_buffer_size=33554432
  • ossfs 1,0

    Ajoutez le paramètre otherOpts pour activer le mode de lecture directe et l'optimisation du cache.

    -o direct_read -o readdir_optimize

Scénarios de test

Chaque outil monte le même bucket, puis FIO mesure les performances en lecture et en écriture. Voici les résultats.

Écriture séquentielle monothread (100 Go)

Remarque

Les performances en écriture d'ossfs 1,0 sont limitées par les E/S disque.

  • Commande de test

    Écriture directe monothread de 100 Go avec une taille de bloc de 1 Mo :

    fio --name=file-100G --ioengine=libaio --rw=write --bs=1M --size=100G --numjobs=1 --direct=1 --directory=/mnt/oss/fio_direct_write --group_reporting
  • Résultats du test

    Outil

    Débit

    Utilisation CPU (100 % pour un cœur entièrement chargé)

    Mémoire maximale

    ossfs 2,0

    2,2 Go/s

    207 %

    2 167 Mo

    ossfs 1,0

    118 Mo/s

    5 %

    15 Mo

    goofys

    450 Mo/s

    250 %

    7,5 Go

Lecture séquentielle monothread (100 Go)

  • Commande de test

    Videz le cache de pages, puis exécutez une lecture séquentielle monothread avec des blocs de 1 Mo :

    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
  • Résultats du test

    Outil de test

    Débit

    Utilisation CPU (100 % pour un cœur entièrement chargé)

    Mémoire maximale

    ossfs 2,0

    4,3 Go/s

    610 %

    1 629 Mo

    ossfs 1,0

    1,0 Go/s

    530 %

    260 Mo

    goofys

    1,3 Go/s

    270 %

    976 Mo

Lecture séquentielle multithread (4 × 100 Go)

  • Génération des fichiers de test

    Créez quatre fichiers de test de 100 Go dans /mnt/oss/fio :

    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
  • Commande de test

    Videz le cache de pages, puis lisez quatre fichiers de 100 Go avec 4 threads pendant 30 secondes (blocs de 1 Mo) dans /mnt/oss/fio :

    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
  • Résultats du test

    Outil

    Débit

    Utilisation CPU (100 % pour un cœur entièrement chargé)

    Mémoire maximale

    ossfs 2,0

    7,4 Go/s

    890 %

    6,2 Go

    ossfs 1,0

    1,8 Go/s

    739 %

    735 Mo

    goofys

    2,8 Go/s

    7 800 %

    2,7 Go

Lecture concurrente de petits fichiers (128 threads, 100 000 × 128 Ko)

Remarque

OSS impose par défaut une limite de 10 000 QPS. Pour reproduire ces résultats, assurez-vous qu'aucun autre service ne consomme le quota QPS du compte de test.

  • Étapes

    1. Créez un programme Go nommé rw-bench.go.

      Ce programme crée ou lit des fichiers de manière concurrente dans un répertoire cible et rapporte le débit.

      Exemple de code

      package main
      
      import (
      	"flag"
      	"fmt"
      	"io"
      	"log"
      	"os"
      	"path/filepath"
      	"sync"
      	"time"
      )
      
      var dir = flag.String("dir", "", "work dir")
      var threads = flag.Int("threads", 8, "concurrency threads count")
      var isWrite = flag.Bool("write", false, "test write files")
      var fileSize = flag.Int64("file-size-KB", 128, "file size in KBytes")
      var fileCount = flag.Int("file-count", 0, "file count")
      
      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("Error walking the path %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("Failed to create/open %v with %v\n", fInfo.Name, err)
      				continue
      			}
      		} else {
      			fd, err = os.OpenFile(fInfo.Name, os.O_RDONLY, 0)
      			if err != nil {
      				fmt.Printf("Failed to open %v with %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("Failed to write file %v at %v, with %v\n", fInfo.Name, offset, err)
      					break
      				}
      			} else {
      				n, err = fd.ReadAt(buffer, offset)
      				if err != nil && err != io.EOF {
      					fmt.Printf("Failed to read file %v at %v, with %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("start write bench with %v files\n", len(files))
      	} else {
      		fmt.Printf("start read bench with %v files\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("Total time: %v\n", duration)
      	if isWrite {
      		fmt.Printf("Write throughput: %.2f MBytes/s\n", throughput/1000/1000)
      	} else {
      		fmt.Printf("Read throughput: %.2f MBytes/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("Failed to access %v with %v\n", workdir, err)
      		os.Exit(1)
      	}
      
      	doBench(workdir, *isWrite)
      }
    2. Compilez le fichier de programme rw-bench.go.

      go build rw-bench.go
    3. Créez 100 000 fichiers (128 Ko chacun) dans le répertoire monté :

      mkdir -p <path_to_mounted_test_directory> && ./rw-bench --dir <path_to_mounted_test_directory> --file-size-KB 128 --file-count 100000 --write
    4. Videz le cache de pages et exécutez le test cinq fois. Enregistrez les résultats en régime permanent une fois la latence stabilisée.

      echo 1 > /proc/sys/vm/drop_caches
      ./rw-bench --dir <path_to_mounted_test_directory> --threads 128
  • Résultats du test

    Outil

    Débit

    Utilisation CPU (100 % pour un cœur entièrement chargé)

    Mémoire maximale

    ossfs 2,0

    1 Go/s

    247 %

    176 Mo

    ossfs 1,0

    45 Mo/s

    25 %

    412 Mo

    goofys

    1 Go/s

    750 %

    1,3 Go