C#でSharpZipLibでzipファイルを展開するサンプルコード

コンピュータ

ZIPファイルをSharpZipLibというライブラリを使って展開してみます。

プロジェクトの作成

cd (mkdir SharpZipLibSample01)
dotnet new console -f net8.0
dotnet add package SharpZipLib --version 1.4.2

ソースコード

直列処理版
ファイル名:Program.cs

using System.Diagnostics;
using System.Security.AccessControl;
using ICSharpCode.SharpZipLib.Zip;

// zipEntries は List<string>(エントリ名のリスト)を想定
var stopwatch = Stopwatch.StartNew();

const string OUTPUT_DIR = @"./output";
const string INPUT_ACHIVE_FILE = @"G:\testdata\Archive.zip";
Directory.CreateDirectory(OUTPUT_DIR!);

List<string> zipEntries;
using (var fs = File.OpenRead(INPUT_ACHIVE_FILE))
using (var zip = new ZipFile(fs))
{
    zipEntries = zip.Cast<ZipEntry>()
        .Where(e => !e.IsDirectory)
        .Select(e => e.Name)
        .ToList();
}

using var zipStream = File.OpenRead(INPUT_ACHIVE_FILE);
using var zipFile = new ZipFile(zipStream);

foreach (var entryName in zipEntries)
{
    var entry = zipFile.GetEntry(entryName);
    if (entry == null || entry.IsDirectory) continue;

    using var entryStream = zipFile.GetInputStream(entry);
    var outPath = Path.Combine(OUTPUT_DIR, entry.Name);

    using var outFile = File.Create(outPath);
    entryStream.CopyTo(outFile);
}

stopwatch.Stop();
Console.WriteLine($"直列処理時間: {stopwatch.ElapsedMilliseconds} ms");

並列処理版

using System.Diagnostics;
using System.Security.AccessControl;
using ICSharpCode.SharpZipLib.Zip;

// zipEntries は List<string>(エントリ名のリスト)を想定
var stopwatch = Stopwatch.StartNew();

const string OUTPUT_DIR = @"./output";
const string INPUT_ACHIVE_FILE = @"G:\testdata\Archive.zip";
Directory.CreateDirectory(OUTPUT_DIR!);

List<string> zipEntries;
using (var fs = File.OpenRead(INPUT_ACHIVE_FILE))
using (var zip = new ZipFile(fs))
{
    zipEntries = zip.Cast<ZipEntry>()
        .Where(e => !e.IsDirectory)
        .Select(e => e.Name)
        .ToList();
}

using var zipStream = File.OpenRead(INPUT_ACHIVE_FILE);
using var zipFile = new ZipFile(zipStream);

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 1
};

// zipEntries は List<string>
Parallel.ForEach(zipEntries, options, entryName =>
{
    using var zipStream = File.OpenRead(INPUT_ACHIVE_FILE);           // 各スレッドで独立に開く
    using var zipFile = new ZipFile(zipStream);
    var entry = zipFile.GetEntry(entryName);
    if (entry == null || entry.IsDirectory) return;

    using var entryStream = zipFile.GetInputStream(entry);
    var outPath = Path.Combine("output", entry.Name);

    using var outFile = File.Create(outPath);
    entryStream.CopyTo(outFile);
});


stopwatch.Stop();
Console.WriteLine($"並列処理時間(MaxThreads={options.MaxDegreeOfParallelism}): {stopwatch.ElapsedMilliseconds} ms");

実行結果

実行環境
Ryzen7 5700X (8コア16スレッド)

・ZIPファイルはNVMeSSDから読み出し、出力先もNVMeSSD
直列処理時間: 499 ms

並列処理時間(MaxThreads=1): 529 ms
並列処理時間(MaxThreads=2): 338 ms
並列処理時間(MaxThreads=4): 247 ms
並列処理時間(MaxThreads=8): 212 ms
並列処理時間(MaxThreads=16): 228 ms

・ZIPファイルはNAS(HDD)から読み込み。出力先はNVMeSSD
直列処理時間: 2220 ms

並列処理時間(MaxThreads=1): 5545 ms
並列処理時間(MaxThreads=2): 2877 ms
並列処理時間(MaxThreads=4): 3193 ms
並列処理時間(MaxThreads=8): 4042 ms
並列処理時間(MaxThreads=16): 3940 ms

NVMeSSDの場合は並列処理で効率が上がるようだが、NAS(HDD)の場合直列処理のほうが高速に動作します。

コメント