항목 | 설명 |
|---|---|
파일 압축 / 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 |
[테스트 결과]
압축 알고리즘 및 옵션 (압축 파일 용량) | 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 을 의미
- -mx=1 -mm=BZip2
- 아래 옵션을 이용하여 7 zip 압축 수행
- DAS 입력 폴더에 제품이 존재한다고 가정한 단계별 업무 절차
[테스트 결과]
압축 알고리즘 및 옵션 (압축 파일 용량) | 동시 수행 개수 | 전체 소요 시간 (sec) | CPU 최대 점유율 (%) | 압축 평균 소요 시간 (sec) | 복사 (PC to NAS) 평균 소요 시간 (sec) | Hash 생성 평균 소요 시간 (sec) |
|---|---|---|---|---|---|---|
.Net Framework 제공 압축 모듈 / 옵션 없음 (1.2GB) | 1 | 58.0 | 8 | 52.3 | 2.0 | 3.4 |
| 5 | 78 | 33 | 59 | 7 | 10 | |
| 10 | 119 | 70 | 75 | 17 | 25 | |
| 15 | 182 | 93 | 106 | 30 | 43 | |
| 20 | 234 | 100 | 127 | 36 | 66 | |
7zip / -mx=1 -mm=BZip2 (0.99GB) | 1 | 18.8 | 98 | 14.7 | 1.1 | 2.8 |
| 5 | 87 | 100 | 69 | 7 | 8 | |
| 10 | 175 | 100 | 138 | 15 | 20 | |
| 15 | 264 | 100 | 207 | 19 | 35 | |
| 20 | 362 | 100 | 277 | 28 | 54 |
- 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)
{
}
}
}