C # में बड़ी फ़ाइलों के लिए चेकसम बनाने का सबसे तेज़ तरीका क्या है


128

मुझे कुछ मशीनों में बड़ी फ़ाइलों को सिंक करना होगा। फ़ाइलें आकार में 6GB तक हो सकती हैं। सिंक हर कुछ हफ्तों में मैन्युअल रूप से किया जाएगा। मैं फ़ाइल नाम को ध्यान में नहीं रख सकता क्योंकि वे कभी भी बदल सकते हैं।

मेरी योजना गंतव्य पीसी और स्रोत पीसी पर चेकसम बनाने की है और फिर सभी फाइलों को एक चेकसम के साथ कॉपी करें, जो गंतव्य में पहले से गंतव्य पर नहीं हैं। मेरा पहला प्रयास कुछ इस तरह था:

using System.IO;
using System.Security.Cryptography;

private static string GetChecksum(string file)
{
    using (FileStream stream = File.OpenRead(file))
    {
        SHA256Managed sha = new SHA256Managed();
        byte[] checksum = sha.ComputeHash(stream);
        return BitConverter.ToString(checksum).Replace("-", String.Empty);
    }
}

समस्या रनटाइम थी:
- SHA256 के साथ 1,6 GB फ़ाइल के साथ -> 20 मिनट
- MD5 के साथ 1,6 GB फ़ाइल के साथ -> 6.15 मिनट

क्या चेकसम प्राप्त करने का एक बेहतर - तेज़ - तरीका है (शायद एक बेहतर हैश फ़ंक्शन के साथ)?


2
क्या आपको वास्तव में चेकसम की जांच की आवश्यकता है? आप फ़ाइलों की प्रतिलिपि कैसे बना रहे हैं? अगर आपकी खिड़कियों पर मैं रोबोकॉपी के नवीनतम संस्करण का उपयोग करूंगा ...
मेष

6
यहाँ अच्छा टिप केवल हैशिंग को परेशान करने के लिए है, अगर फ़ाइल का आकार 2 उम्मीदवार फ़ाइलों के बीच अलग हैं stackoverflow.com/a/288756/74585
मैथ्यू लॉक

जवाबों:


117

यहां समस्या यह है कि SHA256Managedएक समय में 4096 बाइट्स पढ़ता है (इनहेरिट FileStreamऔर ओवरराइड Read(byte[], int, int)से यह देखने के लिए कि यह फिलस्ट्रीम से कितना पढ़ता है), जो कि डिस्क आईओ के लिए बहुत छोटा बफर है।

गति चीजों को रैप (SHA256 के साथ अपने मशीन, 1 MD5 के लिए मिनट पर 2 जीबी फ़ाइल hashing के लिए 2 मिनट) के लिए FileStreamमें BufferedStreamऔर सेट यथोचित आकार बफर आकार (मैं ~ 1 एमबी बफर के साथ की कोशिश की):

// Not sure if BufferedStream should be wrapped in using block
using(var stream = new BufferedStream(File.OpenRead(filePath), 1200000))
{
    // The rest remains the same
}

3
ठीक है - इसने अंतर बना दिया - MD5 के साथ 1.6GB फ़ाइल के हैशिंग ने मेरे बॉक्स पर 5.2 सेकेंड का समय लिया (क्वाडकोड @ 2.6 गीगाहर्ट्ज, 8 जीबी रैम) - मूल कार्यान्वयन के रूप में भी तेज ...
क्रोनो

4
मुझे नहीं मिला। मैं सिर्फ इस सुझाव की कोशिश की, लेकिन अंतर कुछ भी नहीं करने के लिए कम से कम है। 1024mb फ़ाइल w / o बफ़रिंग 12-14 सेकंड्स, बफ़रिंग के साथ भी 12-14 सेकंड्स - मैं समझता हूं कि सैकड़ों 4k ब्लॉक पढ़ने से अधिक IO का उत्पादन होगा, लेकिन मैं खुद से पूछता हूं कि क्या फ्रेमवर्क के नीचे या मूल APIs इसे पहले से हैंडल नहीं करते हैं ..
क्रिश्चियन कैसट

11
पार्टी के लिए थोड़ा देर से, लेकिन फाइलस्ट्रीम के लिए अब बफ़रड्रीम में स्ट्रीम को लपेटने की कोई आवश्यकता नहीं है क्योंकि यह आजकल फाइलस्ट्रीम में पहले से ही किया गया है। स्रोत
रेहान

मैं सिर्फ छोटी फ़ाइलों (<10 एमबी, लेकिन एमडी 5 पाने के लिए हमेशा के लिए ले रहा हूं) के साथ इस मुद्दे से गुजर रहा था। भले ही मैं .Net 4.5 का उपयोग करता हूं, लेकिन बफ़रडस्ट्रीम के साथ इस विधि पर स्विच करने से हैश का समय लगभग 8.6 सेकंड से घटकर 8.6
एमबी

मैंने 1024 kB के बजाय बफ़रड्रीम / w 512 kB का उपयोग किया। 1.8 जीबी फ़ाइल 30 सेकंड में हल हो गई थी।
ह्यूगो वोस्तहिस

61

संपूर्ण फ़ाइल को चेकसम न करें, प्रत्येक 100mb या उससे पहले चेकसम बनाएं, ताकि प्रत्येक फ़ाइल में चेकसम का संग्रह हो।

फिर जब आप चेकसम की तुलना करते हैं, तो आप पहले अलग-अलग चेकसम के बाद तुलना करना बंद कर सकते हैं, जल्दी निकल सकते हैं, और आपको पूरी फाइल को संसाधित करने से बचा सकते हैं।

यह समरूप फ़ाइलों के लिए अभी भी पूरा समय लेगा।


2
मुझे विचार पसंद है, लेकिन यह मेरे परिदृश्य में काम नहीं करेगा क्योंकि मैं समय के साथ बहुत सारी अपरिवर्तित फाइलों के साथ समाप्त हो जाऊंगा।
क्रोनो

1
आप हर 100mb फाइल को कैसे चेक करते हैं?
स्मिथ

1
सुरक्षा कारणों से चेकसम का उपयोग करते समय एक अच्छा विचार नहीं है, क्योंकि हमलावर सिर्फ उस बाइट को बदल सकता है जिसे आपने बाहर रखा है।
b.kiener

2
+1 यह एक उत्कृष्ट विचार है जब आप एक-से-एक तुलना कर रहे हैं। दुर्भाग्य से, मैं कई डुप्लिकेट (कई-से-कई चेक) के बीच अद्वितीय फ़ाइलों को देखने के लिए एक सूचकांक के रूप में एमडी 5 हैश का उपयोग कर रहा हूं।
नाथन गोइंग

1
@ b.kiener नहीं बाइट को बाहर रखा गया है। आपने उसे गलत समझा।
सोरूस फलाहती

47

जैसा कि एंटोन गोगोलेव ने कहा , फ़ाइलस्ट्रीम डिफ़ॉल्ट रूप से एक बार में 4096 बाइट्स पढ़ता है, लेकिन आप फाइलस्ट्रीम कंस्ट्रक्टर का उपयोग करके किसी अन्य मूल्य को निर्दिष्ट कर सकते हैं:

new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16 * 1024 * 1024)

ध्यान दें कि माइक्रोसॉफ्ट से ब्रैड अब्राम्स ने 2004 में लिखा था:

एक FileStream के आसपास एक बफ़रड्रीम को लपेटने से शून्य लाभ होता है। हमने बेहतर डिफ़ॉल्ट प्रदर्शन को प्रोत्साहित करने के लिए लगभग 4 साल पहले FileStream में BufferedStream के बफ़रिंग तर्क की नकल की

स्रोत


22

Md5sum.exe की विंडो पोर्ट इनवॉइस करें । यह .NET कार्यान्वयन के रूप में दो गुना तेज़ है (कम से कम मेरी मशीन पर 1.2 जीबी फ़ाइल का उपयोग करके)

public static string Md5SumByProcess(string file) {
    var p = new Process ();
    p.StartInfo.FileName = "md5sum.exe";
    p.StartInfo.Arguments = file;            
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.Start();
    p.WaitForExit();           
    string output = p.StandardOutput.ReadToEnd();
    return output.Split(' ')[0].Substring(1).ToUpper ();
}

3
WOW - PC-tools.net/win32/md5sums से md5sums.exe का उपयोग करना इसे वास्तव में तेज़ बनाता है। 1681457152 बाइट्स, 8672 एमएस = 184.91 एमबी / सेकंड -> 1,6 जीबी ~ 9 सेकंड यह मेरे उद्देश्य के लिए काफी तेज होगा।
क्रोनो

16

ठीक है - आप सभी को धन्यवाद - मुझे इसे लपेटने दें:

  1. हैशिंग करने के लिए एक "देशी" exe का उपयोग करते हुए 6 मिनट से 10 सेकंड तक का समय लगा जो बहुत बड़ा है।
  2. बफ़र बढ़ाना और भी तेज़ था - 1.6GB फ़ाइल में MD5 इन .Net का उपयोग करते हुए 5.2 सेकंड लगे, इसलिए मैं इस समाधान के साथ जाऊंगा - धन्यवाद फिर से

10

मैंने इस कोड को चलाते हुए बफर साइज़ के साथ टेस्ट किए

using (var stream = new BufferedStream(File.OpenRead(file), bufferSize))
{
    SHA256Managed sha = new SHA256Managed();
    byte[] checksum = sha.ComputeHash(stream);
    return BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
}

और मैंने आकार में 29 tested जीबी की फ़ाइल के साथ परीक्षण किया, परिणाम थे

  • 10.000: 369,24
  • 100.000: 362,55s
  • 1.000.000: 361,53 s
  • 10.000.000: 434,15s
  • 100.000.000: 435,15 s
  • 1.000.000.000: 434,31 s
  • और मूल का उपयोग करते समय 376,22, कोई भी बफर कोड नहीं।

मैं एक i5 2500 k CPU, 12 GB RAM और OCZ Vertex 4 256 GB SSD ड्राइव चला रहा हूं।

तो मैंने सोचा, एक मानक 2TB हार्डड्राइव के बारे में क्या। और परिणाम इस तरह थे

  • 10.000: 368,52 s
  • 100.000: 364,15s
  • 1.000.000: 363,06s
  • 10.000.000: 678,96s
  • 100.000.000: 617,89s
  • 1.000.000.000: 626,86s
  • और किसी के लिए भी 368,24 बफर नहीं है

तो मैं अधिकतम 1 मिल के बफर या बफर की सिफारिश नहीं करूंगा।


मुझे नहीं मिला। यह परीक्षण एंटोन गोगोलेव के स्वीकृत उत्तर के विपरीत कैसे हो सकता है?
दोस्तबुल

क्या आप अपने डेटा में प्रत्येक फ़ील्ड का विवरण जोड़ सकते हैं?
videoguy

2

आप कुछ गलत कर रहे हैं (शायद बहुत छोटा बफर पढ़ें)। असुरक्षित उम्र की एक मशीन पर (2002 से एथलॉन 2x1800MP) जिसमें डिस्क पर डीएमए संभवत: व्हेक से बाहर है (क्रमिक रीडिंग करते समय 6.6M / s बहुत धीमी है):

"यादृच्छिक" डेटा के साथ 1G फ़ाइल बनाएँ:

# dd if=/dev/sdb of=temp.dat bs=1M count=1024    
1073741824 bytes (1.1 GB) copied, 161.698 s, 6.6 MB/s

# time sha1sum -b temp.dat
abb88a0081f5db999d0701de2117d2cb21d192a2 *temp.dat

1m5.299s

# time md5sum -b temp.dat
9995e1c1a704f9c1eb6ca11e7ecb7276 *temp.dat

1m58.832s

यह भी अजीब है, मेरे लिए md5 sha1 की तुलना में लगातार धीमा है (कई बार रीरन)।


हाँ - मैं बफर बढ़ाने की कोशिश करूँगा - जैसे एंटोन गोगोलेव ने सुसाइड किया। मैंने इसे "देशी" MD5.exe के माध्यम से चलाया, जिसमें 9 सेकंड में 1,6 जीबी की फ़ाइल थी।
क्रोनो

2

मुझे पता है कि मुझे पार्टी करने में देर हो गई है लेकिन वास्तव में समाधान को लागू करने से पहले परीक्षण किया गया।

मैंने इनबिल्ट एमडी 5 क्लास और भी md5sum.exe के खिलाफ टेस्ट किया । मेरे मामले में इनबिल्ट क्लास ने 13 सेकंड का समय लिया जहाँ md5sum.exe भी हर रन में 16-18 सेकंड के आसपास था।

    DateTime current = DateTime.Now;
    string file = @"C:\text.iso";//It's 2.5 Gb file
    string output;
    using (var md5 = MD5.Create())
    {
        using (var stream = File.OpenRead(file))
        {
            byte[] checksum = md5.ComputeHash(stream);
            output = BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
            Console.WriteLine("Total seconds : " + (DateTime.Now - current).TotalSeconds.ToString() + " " + output);
        }
    }

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.