테스트 환경

항목

설명

파일 압축 / Hash 구동 장비 (이하 PC)

l  192.168.30.195

l  11th Gen Intel(R) Core(TM) i7-11700 @ 2.50GHz   2.50 GHz

l  128GB

l  Samsung SSD 970 EVO Plus 1TB

NAS 장비 (이하 NAS)

l  192.168.30.86

l  시놀로지 DS1621+ (6베이)

l  마이크론 Crucial MX500 Series 2TB TLC 6

네트워크

l  HPE 1950-12XGT 스위치

l  10G 구성

테스트 파일

l  K3 1G 영상

l  2.13GB



1차 테스트 (단일 Thread, 10회 테스트 결과)

[테스트 결과]

압축 알고리즘 및 옵션 (압축 파일 용량)

10회 압축 평균 시간 (압축률)

10회 복사 (PC to NAS) 평균 시간

10 Hash 생성 평균 시간

.Net Framework 제공 압축 모듈 / 옵션 없음 (1.2GB)

52 초 (46 %)

2.12 초 (5.9 Gbps)

3.42 초

7zip / -mx=1 -mm=BZip2 (0.99GB)

14 초 (54 %)

1.57 초 (4.9 Gbps)

2.76 초

비압축

N/A

3.42 초 (5.0 Gbps)

5.92 초

  • 다른 작업을 수행하지 않는다고 했을 때, PC to NAS 복사 속도는 4Gbps 정도 나옴
  • 파일 복사 시간보다 Hash 생성 시간이 더 오래 걸림
  • CPU 사용량은 25% 정도였음


[테스트 결론]

  • .Net Framework 기본 제공 압축 모듈 대비 7zip 을 사용하는 것이 시간/용량 측면에서 효율적임
  • 파일 복사 시, 다른 수행 작업이 없음에도 10G 네트워크 환경의 대역폭을 모두 활용하지 못함


테스트를 위해 사용한 코드
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Threading;

namespace ZipHashPerformance
{
    class Program
    {
        static void Main(string[] args)
        {
            DoTest(CompressFileViaZipFileInDotNet, FileCopy, GenerateHashCodeForFile);
            DoTest(CompressFileVia7zip, FileCopy, GenerateHashCodeForFile);
            DoTest(NoComp, DirCopy, GenerateHashCodeForDir);
        }

        static void DoTest(Action<string, string> zipMethod, Action<string, string> copyMethod, Func<string, string> hashMethod)
        {
            string inputPath = @"..\PS";
            string zipFilePath = inputPath + ".zip";
            string nasZipFilePath = @"\\192.168.30.86\Project\JGS20A\PS.zip";
            string nasZipDirPath = @"\\192.168.30.86\Project\JGS20A\PS";

            double zippingTime = 0, copyTime = 0, hashingTime = 0;
            DateTime start, end;
            double maxCount = 10;

            for (int count = 0; count < maxCount; count++)
            {
                if (File.Exists(nasZipFilePath)) File.Delete(nasZipFilePath);
                if (File.Exists(zipFilePath)) File.Delete(zipFilePath);

                if(Directory.Exists(nasZipDirPath))
                {
                    foreach(string filePath in Directory.GetFiles(nasZipDirPath))
                    {
                        File.Delete(filePath);
                    }

                    Directory.Delete(nasZipDirPath);
                }

                start = DateTime.Now;
                zipMethod(inputPath, zipFilePath);
                end = DateTime.Now;
                zippingTime += (end - start).TotalMilliseconds;

                start = DateTime.Now;
                copyMethod(zipFilePath, nasZipFilePath);
                end = DateTime.Now;
                copyTime += (end - start).TotalMilliseconds;

                start = DateTime.Now;
                hashMethod(nasZipFilePath);
                end = DateTime.Now;
                hashingTime += (end - start).TotalMilliseconds;

                //Console.WriteLine($"Done {count}");
            }

            double inputLengh =
                File.Exists(zipFilePath) ? new FileInfo(zipFilePath).Length : GetDirSize(inputPath);

            Console.WriteLine($"zipping via {zipMethod.Method.Name} sec = {zippingTime / 1000.0}, avg. sec = {zippingTime / maxCount / 1000.0}");
            if(File.Exists(zipFilePath)) Console.WriteLine($"zip ratio via {zipMethod.Method.Name} = {inputLengh / GetDirSize(inputPath) * 100}");
            Console.WriteLine($"copyTime via {copyMethod.Method.Name} total  sec = {copyTime / 1000.0}, avg. sec = {copyTime / maxCount / 1000.0}, avg. speed = {inputLengh / (copyTime / maxCount / 1000.0) / 1024 / 1024 / 1024 * 8} Gbps");
            Console.WriteLine($"hashingTime via {hashMethod.Method.Name} total sec = {hashingTime / 1000.0}, avg. sec = {hashingTime / maxCount / 1000.0}, aavg. speed = {inputLengh / (hashingTime / maxCount / 1000.0) / 1024 / 1024 / 1024 * 8} Gbps");

            Console.WriteLine();
        }

        static void FileCopy(string sourceFilePath, string outputFilePath)
        {
            File.Copy(sourceFilePath, outputFilePath);
        }

        static void DirCopy(string sourceDirPath, string outputDirPath)
        {
            sourceDirPath = Path.Combine(Path.GetDirectoryName(sourceDirPath), Path.GetFileNameWithoutExtension(sourceDirPath));
            outputDirPath = Path.Combine(Path.GetDirectoryName(outputDirPath), Path.GetFileNameWithoutExtension(outputDirPath));

            Directory.CreateDirectory(outputDirPath);

            foreach(string filePath in Directory.GetFiles(sourceDirPath))
            {
                FileCopy(filePath, Path.Combine(outputDirPath, Path.GetFileName(filePath)));
            }
        }

        static double GetDirSize(string inputPath)
        {
            double totalSize = 0;
            foreach (string filePath in Directory.GetFiles(inputPath))
            {
                totalSize += (new FileInfo(filePath)).Length;
            }

            return totalSize;
        }

        static string GenerateHashCodeForFile(string zipFilePath)
        {
            using (FileStream fileStream = new FileStream(zipFilePath, FileMode.Open))
            {
                byte[] hashcode = MD5.Create().ComputeHash(fileStream);
                return BitConverter.ToString(hashcode).Replace("-", "").ToLower();
            }
        }

        static string GenerateHashCodeForDir(string dirPath)
        {
            foreach (string filePath in Directory.GetFiles(Path.Combine(Path.GetDirectoryName(dirPath), Path.GetFileNameWithoutExtension(dirPath))))
            {
                using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
                {
                    byte[] hashcode = MD5.Create().ComputeHash(fileStream);
                    BitConverter.ToString(hashcode).Replace("-", "").ToLower();
                }
            }

            return string.Empty;
        }

        static void CompressFileVia7zip(string input, string output)
        {
            try
            {
                ProcessStartInfo info = new ProcessStartInfo();
                info.FileName = @"C:\Program Files\7-Zip\7z.exe";
                //info.Arguments = "a -t7z \"" + output + "\" \"" + input;
                info.Arguments = "a \"" + output + "\" \"" + input + "\"" + " -mx=1 -mm=BZip2";

                //-mx=1 -mm=BZip2
                //Console.WriteLine(info.Arguments);

                info.WindowStyle = ProcessWindowStyle.Hidden;
                Process P = Process.Start(info);
                P.WaitForExit();

                int result = P.ExitCode;
                if (result != 0)
                {
                    Console.WriteLine("error!! code = " + result);
                }
            }
            catch { }
        }

        static void CompressFileViaZipFileInDotNet(string input, string output)
        {
            ZipFile.CreateFromDirectory(input, output);
        }

        static void NoComp(string input, string output)
        {

        }
    }
}

전갑호 선임 질문에 대한 답변


[전갑호 선임이 요청한 내용에 대한 답변]

  • 가상수신사용자(첨단망) 자료배포 단계별 업무절차 및 소요시간()
    • DAS 입력 폴더에 제품이 존재한다고 가정한 단계별 업무 절차
      • 등록 작업지시서 입력 및 처리 폴더로 이동 (Local to Local) à 1초
      • 폴더 압축 à 14초
      • Hash 값 생성 à 3초
      • NAS 로 복사 (Online 저장) à 2초
      • Hash 값 확인 à 3초
      • Tape Library 로 복사 (Nearline 저장) à 2초 (Tape Storage Cache 로 복사하는데 걸리는 시간만 고려)
      • 첨단망 배포 서버로 복사 à 3초 (10G 가정)
      • 배포물 생성 à TBD 초 (DSS 의존적)
      • 배포물 전송 à TBD 초 (가상수신사용자와의 네트워크 환경 의존적)
    • 전체 30 + Alpha + TBD 초 예상
      • 30초 à 필요 기능 수행에 필요한 시간
      • Alpha 초 à 기능 수행 간의 인터벌 시간 (최대 10분 이내일 것으로 예상, 정성적인 수치)
      • TBD 초 à 현재로서는 예상할 수 없는 시간
    • 압축/비압축 시 소요시간
      • 7zip 을 이용해13GB 을 압축한 파일 전송 시간 à 약 2
      • 비압축한13GB 폴더 전송 시간 à 약 4
    • 파일크기를 최소화 할 수 있는 압축방식
      • 아래 옵션을 이용하여 7 zip 압축 수행
        • -mx=1 -mm=BZip2
          • mx1: set compression level 로서 fastest 를 의미
          • BZip2: 압축 알고리즘으로서 Standard BWT algorithm 을 의미
2차 테스트 (20개 동시 수행 성능 측정)

[테스트 결과]

압축 알고리즘 및 옵션 (압축 파일 용량)

동시 수행 개수전체 소요 시간 (sec)CPU 최대 점유율 (%)

압축 평균 소요 시간 (sec)

복사 (PC to NAS) 평균 소요 시간 (sec)

Hash 생성 평균 소요 시간 (sec)

.Net Framework 제공 압축 모듈 / 옵션 없음 (1.2GB)

158.0852.32.03.4

5783359710

1011970751725

15182931063043

202341001273666

7zip / -mx=1 -mm=BZip2 (0.99GB)

118.89814.71.12.8

5871006978

101751001381520

152641002071935

203621002772854
  • CPU 사용률은 동시 실행 개수가 2개가 되면서 거의 100% 를 점유했음


테스트를 위해 사용한 코드
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Threading;

namespace ZipHashPerformance
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 3)
            {
                Console.WriteLine("[thread count] [max iteration count] [compress type]");
                Console.WriteLine("[thread count]: 동시 수행 개수");
                Console.WriteLine("[max iteration count]: 단일 Thread 최대 반복 횟수");
                Console.WriteLine("[compress type]: 0->dotnet, 1->7zip");
                return;
            }

            int threadCount = int.Parse(args[0]);
            double maxCount = double.Parse(args[1]);
            int compType = int.Parse(args[2]);

            List<Thread> thList = new List<Thread>();
            Thread thMonitor = new Thread(new ThreadStart(CpuMonitor));

            for (int i = 0; i < threadCount; i++)
            {
                thList.Add(new Thread(new ParameterizedThreadStart(DoTestForThead)));
                DoneThreadList.Add(i, false);
            }

            for (int i = 0; i < threadCount; i++)
            {
                object obj = new List<object> { i, maxCount, compType };
                thList[i].Start(obj);
            }

            thMonitor.Start();

            while (true)
            {
                Thread.Sleep(50);
                bool value = true;

                try
                {
                    for(int i = 0; i < threadCount; i++)
                    {
                        DoneThreadList.TryGetValue(i, out value);
                        if (!value) break;
                    }

                    if (!value) continue;
                }
                catch
                {
                    continue;
                }

                break;
            }

            MonitorThreadFlag = false;

            while (true)
            {
                Thread.Sleep(50);

                if (thMonitor.ThreadState == System.Threading.ThreadState.Running) continue;

                break;
            }

            EntireZippingTime /= (double)threadCount;
            EntireCopyTime /= (double)threadCount;
            EntireHashingTime /= (double)threadCount;

            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] zipping total sec = {EntireZippingTime / 1000.0}, avg. sec = {EntireZippingTime / maxCount / 1000.0}");
            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] copyTime total  sec = {EntireCopyTime / 1000.0}, avg. sec = {EntireCopyTime / maxCount / 1000.0}");
            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] hashingTime total sec = {EntireHashingTime / 1000.0}, avg. sec = {EntireHashingTime / maxCount / 1000.0}");

            //DoTest(1, 1, CompressFileViaZipFileInDotNet, FileCopy, GenerateHashCodeForFile);
            //DoTest(1, 10, CompressFileVia7zip, FileCopy, GenerateHashCodeForFile);
            //DoTest(1, 10, NoComp, DirCopy, GenerateHashCodeForDir);
        }

        static double EntireZippingTime = 0, EntireCopyTime = 0, EntireHashingTime = 0;

        static bool MonitorThreadFlag = true;
        static Dictionary<int, bool> DoneThreadList = new Dictionary<int, bool>();

        static void CpuMonitor()
        {
            PerformanceCounter cpuCounter;

            cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            while (MonitorThreadFlag)
            {
                Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}] CPU Usage: {cpuCounter.NextValue()} %");

                Thread.Sleep(1000);
            }
        }

        static void DoTestForThead(object obj)
        {
            List<object> objList = obj as List<object>;

            if ((int)objList[2] == 0) DoTest((int)objList[0], (double)objList[1], CompressFileViaZipFileInDotNet, FileCopy, GenerateHashCodeForFile);
            else DoTest((int)objList[0], (double)objList[1], CompressFileVia7zip, FileCopy, GenerateHashCodeForFile);

        }

        static void DoTest(int threadCount, double maxCount, Action<string, string> zipMethod, Action<string, string> copyMethod, Func<string, string> hashMethod)
        {
            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] Start!!!");

            string inputPath = @"..\PS";
            string zipFilePath = inputPath + $"_{threadCount}.zip";
            string nasZipFilePath = $"\\\\192.168.30.86\\Project\\JGS20A\\PS_{threadCount}.zip";
            string nasZipDirPath = $"\\\\192.168.30.86\\Project\\JGS20A\\PS_{threadCount}";

            double zippingTime = 0, copyTime = 0, hashingTime = 0;
            DateTime start, end;

            for (int count = 0; count < maxCount; count++)
            {
                if (File.Exists(nasZipFilePath)) File.Delete(nasZipFilePath);
                if (File.Exists(zipFilePath)) File.Delete(zipFilePath);

                if (Directory.Exists(nasZipDirPath))
                {
                    foreach (string filePath in Directory.GetFiles(nasZipDirPath))
                    {
                        File.Delete(filePath);
                    }

                    Directory.Delete(nasZipDirPath);
                }

                start = DateTime.Now;
                zipMethod(inputPath, zipFilePath);
                end = DateTime.Now;
                zippingTime += (end - start).TotalMilliseconds;
                EntireZippingTime += (end - start).TotalMilliseconds;

                start = DateTime.Now;
                copyMethod(zipFilePath, nasZipFilePath);
                end = DateTime.Now;
                copyTime += (end - start).TotalMilliseconds;
                EntireCopyTime += (end - start).TotalMilliseconds;

                start = DateTime.Now;
                hashMethod(nasZipFilePath);
                end = DateTime.Now;
                hashingTime += (end - start).TotalMilliseconds;
                EntireHashingTime += (end - start).TotalMilliseconds;

                //Console.WriteLine($"Done {count}");
            }

            DoneThreadList[threadCount] = true;

            double inputLengh =
                File.Exists(zipFilePath) ? new FileInfo(zipFilePath).Length : GetDirSize(inputPath);

            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] zipping via {zipMethod.Method.Name} sec = {zippingTime / 1000.0}, avg. sec = {zippingTime / maxCount / 1000.0}");
            if (File.Exists(zipFilePath)) Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] zip ratio via {zipMethod.Method.Name} = {inputLengh / GetDirSize(inputPath) * 100}");
            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] copyTime via {copyMethod.Method.Name} total  sec = {copyTime / 1000.0}, avg. sec = {copyTime / maxCount / 1000.0}, avg. speed = {inputLengh / (copyTime / maxCount / 1000.0) / 1024 / 1024 / 1024 * 8} Gbps");
            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] hashingTime via {hashMethod.Method.Name} total sec = {hashingTime / 1000.0}, avg. sec = {hashingTime / maxCount / 1000.0}, aavg. speed = {inputLengh / (hashingTime / maxCount / 1000.0) / 1024 / 1024 / 1024 * 8} Gbps");
            Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][{threadCount}] Done!!!");

            Console.WriteLine();
        }

        static void FileCopy(string sourceFilePath, string outputFilePath)
        {
            File.Copy(sourceFilePath, outputFilePath);
        }

        static void DirCopy(string sourceDirPath, string outputDirPath)
        {
            sourceDirPath = Path.Combine(Path.GetDirectoryName(sourceDirPath), Path.GetFileNameWithoutExtension(sourceDirPath));
            outputDirPath = Path.Combine(Path.GetDirectoryName(outputDirPath), Path.GetFileNameWithoutExtension(outputDirPath));

            Directory.CreateDirectory(outputDirPath);

            foreach (string filePath in Directory.GetFiles(sourceDirPath))
            {
                FileCopy(filePath, Path.Combine(outputDirPath, Path.GetFileName(filePath)));
            }
        }

        static double GetDirSize(string inputPath)
        {
            double totalSize = 0;
            foreach (string filePath in Directory.GetFiles(inputPath))
            {
                totalSize += (new FileInfo(filePath)).Length;
            }

            return totalSize;
        }

        static string GenerateHashCodeForFile(string zipFilePath)
        {
            using (FileStream fileStream = new FileStream(zipFilePath, FileMode.Open))
            {
                byte[] hashcode = MD5.Create().ComputeHash(fileStream);
                return BitConverter.ToString(hashcode).Replace("-", "").ToLower();
            }
        }

        static string GenerateHashCodeForDir(string dirPath)
        {
            foreach (string filePath in Directory.GetFiles(Path.Combine(Path.GetDirectoryName(dirPath), Path.GetFileNameWithoutExtension(dirPath))))
            {
                using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
                {
                    byte[] hashcode = MD5.Create().ComputeHash(fileStream);
                    BitConverter.ToString(hashcode).Replace("-", "").ToLower();
                }
            }

            return string.Empty;
        }

        static void CompressFileVia7zip(string input, string output)
        {
            try
            {
                ProcessStartInfo info = new ProcessStartInfo();
                info.FileName = @"C:\Program Files\7-Zip\7z.exe";
                //info.Arguments = "a -t7z \"" + output + "\" \"" + input;
                info.Arguments = "a \"" + output + "\" \"" + input + "\"" + " -mx=1 -mm=BZip2";

                //-mx=1 -mm=BZip2
                //Console.WriteLine(info.Arguments);

                info.WindowStyle = ProcessWindowStyle.Hidden;
                Process P = Process.Start(info);
                P.WaitForExit();

                int result = P.ExitCode;
                if (result != 0)
                {
                    Console.WriteLine("error!! code = " + result);
                }
            }
            catch { }
        }

        static void CompressFileViaZipFileInDotNet(string input, string output)
        {
            ZipFile.CreateFromDirectory(input, output);
        }

        static void NoComp(string input, string output)
        {

        }
    }
}