.NET में दो बाइट सरणियों की तुलना करें


541

मैं यह व्रत कैसे कर सकता हूं?

यकीन है कि मैं यह कर सकता हूँ:

static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length)
        return false;

    for (int i=0; i<a1.Length; i++)
        if (a1[i]!=a2[i])
            return false;

    return true;
}

लेकिन मैं या तो बीसीएल फ़ंक्शन या ऐसा करने के लिए कुछ अत्यधिक अनुकूलित साबित तरीका ढूंढ रहा हूं ।

java.util.Arrays.equals((sbyte[])(Array)a1, (sbyte[])(Array)a2);

अच्छी तरह से काम करता है, लेकिन ऐसा नहीं लगता है कि x64 के लिए काम करेगा।

मेरे सुपर-फास्ट उत्तर पर यहां ध्यान दें ।


1
"यह थोथा इस तथ्य पर निर्भर करता है कि सरणियाँ संरेखित शुरू होती हैं।" यह एक बड़ी बात है। आपको यह दर्शाने के लिए कोड को ठीक करना चाहिए।
जो चंग

4
a1.Length == a2.Length &&! a1.Where ((t, i) => t! = a2 [i])। कोई भी ()
alerya

मुझे @OhadSchneider का जवाब पसंद आयाIStructuralEquatable
LCJ

जवाबों:


613

आप Enumerable.SequenceEqual विधि का उपयोग कर सकते हैं ।

using System;
using System.Linq;
...
var a1 = new int[] { 1, 2, 3};
var a2 = new int[] { 1, 2, 3};
var a3 = new int[] { 1, 2, 4};
var x = a1.SequenceEqual(a2); // true
var y = a1.SequenceEqual(a3); // false

यदि आप किसी कारण से .NET 3.5 का उपयोग नहीं कर सकते हैं, तो आपकी विधि ठीक है।
कंपाइलर का रन-टाइम वातावरण आपके लूप का अनुकूलन करेगा ताकि आपको प्रदर्शन के बारे में चिंता करने की आवश्यकता न हो।


4
लेकिन क्या SequenceEqual को एक असुरक्षित तुलना की तुलना में अधिक समय नहीं लगता है? खासकर जब आपकी तुलना 1000 कर रही हो?
18

90
हां, यह असुरक्षित तुलना की तुलना में लगभग 50 गुना धीमा है।
हफ्थोर

27
यह वास्तव में यहाँ मृत को बढ़ा रहा है, लेकिन धीमी गति से यहाँ उपयोग करने के लिए एक बुरा शब्द है। 50x धीमा बुरा लगता है , लेकिन यह अक्सर आप अंतर करने के लिए इसके लिए पर्याप्त डेटा की तुलना नहीं कर रहे हैं, और यदि आप कर रहे हैं, तो आपको वास्तव में अपने स्वयं के मामले के लिए, कारणों की असंख्य के लिए यह निर्धारित करना होगा। उदाहरण के लिए, असुरक्षित उत्तर के निर्माता पर ध्यान दें, 7x की धीमी गति का अंतर, जैसा कि 50x धीमे के विपरीत है (असुरक्षित विधि की गति भी डेटा के संरेखण पर निर्भर करती है)। दुर्लभ मामलों में जहां ये संख्या मायने रखती है, पी / इनवोक और भी तेज है।
सेलाली अडोबोर

4
तो धीमी गति से कार्यान्वयन 300 से अधिक पसंद करता है? मेरा सुझाव है कि msvcrt.dll को हुक करना होगा क्योंकि यह काम करने का सबसे तेज़ तरीका होगा।
तगरेट

69
सबसे तेज़ व्यवसाय के लिए सबसे महत्वपूर्ण बात नहीं है। इस कोड पर बचत की तुलना में 99% मामलों में स्थिरता बहुत "तेज" है। मैं SequenceEqual का उपयोग कर रहा हूं और मेरा पूरा कोड <1ms है। आप जो बचत कर रहे हैं वह पी / इनवोक की पठनीयता की कमी के 5 मिनट तक कभी नहीं जोड़ेंगे।
23

236

पी / आह्वान शक्तियों को सक्रिय!

[DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int memcmp(byte[] b1, byte[] b2, long count);

static bool ByteArrayCompare(byte[] b1, byte[] b2)
{
    // Validate buffers are the same length.
    // This also ensures that the count does not exceed the length of either buffer.  
    return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;
}

48
: - पी / आह्वान Yaay इस कम से कम अब तक बिटमैप्स पर से सबसे तेजी से साबित हुई stackoverflow.com/questions/2031217/...
एरिक फोर्ब्स

25
इस मामले में पिनिंग आवश्यक नहीं है। मार्शल कोड PInvoke के साथ मूल कोड को कॉल करते समय स्वचालित पिनिंग करता है। संदर्भ: stackoverflow.com/questions/2218444/…
मार्क ग्लासगो

14
पी / इनवॉइस से एलोसिट हो सकता है, लेकिन यह अब तक प्रस्तुत किए गए सभी समाधानों में सबसे तेज है, जिसमें एक कार्यान्वयन भी शामिल है जो कि असुरक्षित पॉइंटर-आकार की तुलना का उपयोग करता है। कुछ समानताएँ हैं जो आप संदर्भ समानता सहित मूल कोड को कॉल करने और पहले और अंतिम तत्वों की तुलना करने से पहले कर सकते हैं।
जोश

38
बू क्यों? पोस्टर एक तेजी से कार्यान्वयन और एक अनुकूलित विधानसभा भाषा की तुलना में हराया नहीं जा सकता चाहता था। मुझे नहीं पता कि P / INVOKE के बिना .NET से "REPE CMPSD" कैसे प्राप्त किया जा सकता है।
जेसन गोएमाट

14
Nitpick: MSVCR.dll को उपयोगकर्ता कोड द्वारा उपयोग नहीं किया जाना चाहिए। MSVCR का उपयोग करने के लिए, आपको अपने द्वारा वितरित संस्करण का उपयोग करके रनटाइम वितरित करना होगा। ( msdn.microsoft.com/en-us/library/… और blogs.msdn.com/b/oldnewthing/archive/2014/04/11/10516280.aspx )
मिच

160

.NET 4 में इसके लिए एक नया अंतर्निहित समाधान है - IStructuralEquatable

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
    return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2);
}

17
इस ब्लॉग पोस्ट के अनुसार यह वास्तव में बहुत धीमा है।
मैट जॉनसन-पिंट

48
पागल धीमी गति से। लूप के लिए साधारण से लगभग 180x धीमा।
हफथोर

यह काम करता है, लेकिन मुझे समझ नहीं आता कि क्यों। एक बाइट [] एक आदिम प्रकार है जो IStructuralEquatable को लागू नहीं करता है, इसलिए आप इसे क्यों डाल सकते हैं - और उस पर एक निहित डाली! और फिर इंटरफ़ेस "बराबर" विधि जादुई रूप से उपलब्ध हो जाती है ... उस पद्धति का कार्यान्वयन कहां से हो रहा है? क्या कोई मुझे अंदर कर सकता है?
जोश

1
क्यों नहीं बस StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)NullReferenceExceptionयहाँ कोई नहीं है।
ta.speot.is

1
@ ta.speot.is धन्यवाद, एक लाइनर के साथ बहस नहीं कर सकते! पिछला समाधान थोड़ा और अधिक कुशल था क्योंकि इससे कलाकारों को IStructuralEquatable (एक सरणी को सांख्यिकीय रूप से IStructuralEquatable के रूप में जाना जाता है) को बचाया गया था, लेकिन वास्तव में आपके सुझाव अशक्त तर्कों के लिए विधि का काम भी करते हैं।
ओहद श्नाइडर

76

उपयोगकर्ता जीआईएल ने असुरक्षित कोड का सुझाव दिया जिसने इस समाधान को जन्म दिया:

// Copyright (c) 2008-2013 Hafthor Stefansson
// Distributed under the MIT/X11 software license
// Ref: http://www.opensource.org/licenses/mit-license.php.
static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) {
  if(a1==a2) return true;
  if(a1==null || a2==null || a1.Length!=a2.Length)
    return false;
  fixed (byte* p1=a1, p2=a2) {
    byte* x1=p1, x2=p2;
    int l = a1.Length;
    for (int i=0; i < l/8; i++, x1+=8, x2+=8)
      if (*((long*)x1) != *((long*)x2)) return false;
    if ((l & 4)!=0) { if (*((int*)x1)!=*((int*)x2)) return false; x1+=4; x2+=4; }
    if ((l & 2)!=0) { if (*((short*)x1)!=*((short*)x2)) return false; x1+=2; x2+=2; }
    if ((l & 1)!=0) if (*((byte*)x1) != *((byte*)x2)) return false;
    return true;
  }
}

जितना संभव हो उतना सरणी के लिए 64-बिट आधारित तुलना करता है। इस तरह की गणना इस तथ्य पर निर्भर करती है कि सरणियाँ संरेखित शुरू होती हैं। यह काम करेगा अगर नहीं qword संरेखित, बस के रूप में उपवास के रूप में अगर यह नहीं थे।

यह साधारण forलूप की तुलना में लगभग सात टाइमर अधिक तेजी से प्रदर्शन करता है । J # लाइब्रेरी का उपयोग करके मूल forलूप के बराबर प्रदर्शन किया गया । का उपयोग करते हुए। पर्याप्तता लगभग सात गुना धीमी गति से चलती है; मुझे लगता है कि सिर्फ इसलिए कि यह IEnumerator.MoveNext का उपयोग कर रहा है। मुझे लगता है कि LINQ- आधारित समाधान कम से कम धीमा या बदतर है।


3
अच्छा समाधान। लेकिन एक (छोटा) संकेत: यदि A1 और a2 के संदर्भ समान हैं तो तुलनाएं चीजों को गति दे सकती हैं यदि कोई a1 और b1 के लिए समान सरणी देता है।
mmmmmmmm

12
.NET 4 x64 रिलीज़ पर नया परीक्षण डेटा: IStructualEquatable.equals ~ 180x धीमे, SequenceEqual 15x धीमे, SHA1 हैश की तुलना 11x धीमी, बिटकॉइनर ~ समान, असुरक्षितxx तेज़, pinvoke 11x तेज़। बहुत अच्छा है कि असुरक्षित केवल पी / चालान की तुलना में थोड़ा कम धीमी है।
हफथोर

3
यह लिंक इस बात के बारे में अच्छी जानकारी देता है कि मेमोरी संरेखण मायने रखता है ibm.com/developerworks/library/pa-dalign - इसलिए, एक अनुकूलन संरेखण की जांच करने के लिए हो सकता है और यदि दोनों सरणियाँ एक ही राशि से संरेखण बंद हैं, तो बाइट की तुलना करें क्योंकि वे दोनों हैं एक सीमा सीमा पर।
हफथोर

5
जब यह a1 और a2 दोनों अशक्त हो तो यह गलत नहीं होगा?
नवफाल

2
@CristiDiaconescu मैंने केविनड्रेगर के जवाब को विफल कर दिया। मुझे शायद क्या करना चाहिए टेस्ट सूट और मेरे परिणाम जीथब पर उपलब्ध हैं और मेरे उत्तर पर इसे लिंक करें।
हफ्थोर

73

Span<T> अपने स्वयं के अनुप्रयोग के कोड आधार में भ्रामक और / या गैर-पोर्टेबल फ़्लॉफ़ फेंकने के बिना एक अत्यंत प्रतिस्पर्धी विकल्प प्रदान करता है:

// byte[] is implicitly convertible to ReadOnlySpan<byte>
static bool ByteArrayCompare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)
{
    return a1.SequenceEqual(a2);
}

.NET कोर 3.1.0 के रूप में कार्यान्वयन का (हिम्मत) यहाँ पाया जा सकता है

मैंने इस विधि को जोड़ने के लिए @ EliArbel के जिस्ट को संशोधितSpansEqual किया है, दूसरों के मानदंड में सबसे कम दिलचस्प कलाकारों को छोड़ें, इसे अलग-अलग सरणी आकार, आउटपुट ग्राफ़ के साथ चलाएं, और SpansEqualआधार रेखा के रूप में चिह्नित करें ताकि यह रिपोर्ट करें कि विभिन्न वीडियो कैसे तुलना करते हैंSpansEqual

नीचे की संख्या परिणामों से होती है, "त्रुटि" कॉलम को हटाने के लिए हल्के से संपादित किया जाता है।

|        Method |  ByteCount |               Mean |            StdDev | Ratio |
|-------------- |----------- |-------------------:|------------------:|------:|
|    SpansEqual |         15 |           3.562 ns |         0.0035 ns |  1.00 |
|  LongPointers |         15 |           4.611 ns |         0.0028 ns |  1.29 |
|      Unrolled |         15 |          18.035 ns |         0.0195 ns |  5.06 |
| PInvokeMemcmp |         15 |          11.210 ns |         0.0353 ns |  3.15 |
|               |            |                    |                   |       |
|    SpansEqual |       1026 |          20.048 ns |         0.0286 ns |  1.00 |
|  LongPointers |       1026 |          63.347 ns |         0.1062 ns |  3.16 |
|      Unrolled |       1026 |          39.175 ns |         0.0304 ns |  1.95 |
| PInvokeMemcmp |       1026 |          40.830 ns |         0.0350 ns |  2.04 |
|               |            |                    |                   |       |
|    SpansEqual |    1048585 |      44,070.526 ns |        35.3348 ns |  1.00 |
|  LongPointers |    1048585 |      59,973.407 ns |        80.4145 ns |  1.36 |
|      Unrolled |    1048585 |      55,032.945 ns |        24.4745 ns |  1.25 |
| PInvokeMemcmp |    1048585 |      55,593.719 ns |        22.4301 ns |  1.26 |
|               |            |                    |                   |       |
|    SpansEqual | 2147483591 | 253,648,180.000 ns | 1,112,524.3074 ns |  1.00 |
|  LongPointers | 2147483591 | 249,412,064.286 ns | 1,079,409.5670 ns |  0.98 |
|      Unrolled | 2147483591 | 246,329,091.667 ns |   852,021.7992 ns |  0.97 |
| PInvokeMemcmp | 2147483591 | 247,795,940.000 ns | 3,390,676.3644 ns |  0.98 |

मैं देख कर हैरान था SpansEqual अधिकतम-सरणी-आकार के तरीकों के लिए शीर्ष पर बाहर नहीं आया , लेकिन अंतर इतना मामूली है कि मुझे नहीं लगता कि यह कोई बात नहीं होगी।

मेरी प्रणाली की जानकारी:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i7-6850K CPU 3.60GHz (Skylake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.1.100
  [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
  DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT

मैंने कभी नहीं सोचा था कि मैं स्पैन <टी> का उपयोग करूंगा या मेरे द्वारा किए जाने वाले सभी सामानों में इसके करीब होगा। आपके लिए धन्यवाद मैं अब अपने सहकर्मियों के बारे में अपनी बड़ाई कर सकता हूं।
जोक

क्या SequenceEqual को विशेष रूप से एक स्पैन विधि के रूप में लागू किया गया है? सोचा था कि यह IEnumerable विस्तार विधियों में से एक था।
ज़स्तई

1
@Zastai हाँ, {ReadOnly,}Span<T>का अपना संस्करण है SequenceEqual(एक ही नाम क्योंकि यह उसी IEnumerable<T>एक्सटेंशन विधि के समान अनुबंध है , यह अभी तेज है)। ध्यान दें कि प्रकारों पर प्रतिबंध के कारण एक्सटेंशन विधियों का {ReadOnly,}Span<T>उपयोग नहीं किया जा सकता है । IEnumerable<T>ref struct
जो Amenta

1
@Sentinel System.Memory पैकेज में "पोर्टेबल" / "धीमी" के लिए Span<T>कार्यान्वयन है netstandard1.1और ऊपर (इसलिए इस इंटरेक्टिव चार्ट के साथ खेलने के लिए देखें कि वे कौन से हैं)। "फास्ट" Span<T>इस समय केवल .NET कोर 2.1 में उपलब्ध है, लेकिन ध्यान दें कि SequenceEqual<T>विशेष रूप से, "फास्ट" और "धीमी" / "पोर्टेबल" के बीच बहुत कम अंतर netstandard2.0होना चाहिए (हालांकि लक्ष्य में थोड़ा सुधार देखना चाहिए क्योंकि वे सदिश कोड पथ है)।
जो अम्ना

1
install-package system.memory
क्रिस मोशचीनी

30

यदि आप इसे करने के विरोध में नहीं हैं, तो आप J # असेंबली "vjslib.dll" आयात कर सकते हैं और इसके Arrays.equals (बाइट [], बाइट []) विधि का उपयोग कर सकते हैं ...

अगर कोई आप पर हँसता है तो मुझे दोष मत दो ...


संपादित करें: इसके लायक क्या है, इसके लिए मैंने कोड को अलग करने के लिए परावर्तक का उपयोग किया है, और यह वही है जो दिखता है:

public static bool equals(sbyte[] a1, sbyte[] a2)
{
  if (a1 == a2)
  {
    return true;
  }
  if ((a1 != null) && (a2 != null))
  {
    if (a1.Length != a2.Length)
    {
      return false;
    }
    for (int i = 0; i < a1.Length; i++)
    {
      if (a1[i] != a2[i])
      {
        return false;
      }
    }
    return true;
  }
  return false;
}

25

.NET 3.5 और नए में एक नया सार्वजनिक प्रकार है, System.Data.Linq.Binaryजो एनकैप्सुलेट करता है byte[]। यह लागू होता है IEquatable<Binary>कि (प्रभाव में) दो बाइट सरणियों की तुलना करता है। ध्यान दें कि System.Data.Linq.Binaryइसमें निहित रूपांतरण ऑपरेटर भी है byte[]

MSDN प्रलेखन: System.Data.Linq.Binary

समान विधि का परावर्तक अपघटन:

private bool EqualsTo(Binary binary)
{
    if (this != binary)
    {
        if (binary == null)
        {
            return false;
        }
        if (this.bytes.Length != binary.bytes.Length)
        {
            return false;
        }
        if (this.hashCode != binary.hashCode)
        {
            return false;
        }
        int index = 0;
        int length = this.bytes.Length;
        while (index < length)
        {
            if (this.bytes[index] != binary.bytes[index])
            {
                return false;
            }
            index++;
        }
    }
    return true;
}

दिलचस्प मोड़ यह है कि वे केवल बाइट-बाय-बाइट तुलना लूप के लिए आगे बढ़ते हैं यदि दो बाइनरी ऑब्जेक्ट का हैश एक ही है। यह, हालांकि, Binaryवस्तुओं के निर्माण में हैश की गणना करने की लागत पर आता है ( forलूप :-) के साथ सरणी का पता लगाने के द्वारा )।

उपरोक्त कार्यान्वयन का मतलब है कि सबसे खराब स्थिति में आपको सरणियों को तीन बार पार करना पड़ सकता है: पहले array1 के हैश की गणना करने के लिए, फिर array2 के हैश की गणना करने के लिए और अंत में (क्योंकि यह सबसे खराब स्थिति है, लंबाई और हैश के बराबर) की तुलना करने के लिए सरणी 2 में बाइट्स के साथ सरणी 2 में बाइट्स।

कुल मिलाकर, भले ही System.Data.Linq.Binaryबीसीएल में बनाया गया है, मुझे नहीं लगता कि यह दो बाइट सरणियों की तुलना करने का सबसे तेज़ तरीका है: - |


20

मैंने जाँच के बारे में एक समान प्रश्न पोस्ट किया कि क्या बाइट [] शून्य से भरा है। (SIMD कोड को पीटा गया था इसलिए मैंने इसे इस उत्तर से हटा दिया।) यहाँ मेरी तुलनाओं से सबसे तेज़ कोड है:

static unsafe bool EqualBytesLongUnrolled (byte[] data1, byte[] data2)
{
    if (data1 == data2)
        return true;
    if (data1.Length != data2.Length)
        return false;

    fixed (byte* bytes1 = data1, bytes2 = data2) {
        int len = data1.Length;
        int rem = len % (sizeof(long) * 16);
        long* b1 = (long*)bytes1;
        long* b2 = (long*)bytes2;
        long* e1 = (long*)(bytes1 + len - rem);

        while (b1 < e1) {
            if (*(b1) != *(b2) || *(b1 + 1) != *(b2 + 1) || 
                *(b1 + 2) != *(b2 + 2) || *(b1 + 3) != *(b2 + 3) ||
                *(b1 + 4) != *(b2 + 4) || *(b1 + 5) != *(b2 + 5) || 
                *(b1 + 6) != *(b2 + 6) || *(b1 + 7) != *(b2 + 7) ||
                *(b1 + 8) != *(b2 + 8) || *(b1 + 9) != *(b2 + 9) || 
                *(b1 + 10) != *(b2 + 10) || *(b1 + 11) != *(b2 + 11) ||
                *(b1 + 12) != *(b2 + 12) || *(b1 + 13) != *(b2 + 13) || 
                *(b1 + 14) != *(b2 + 14) || *(b1 + 15) != *(b2 + 15))
                return false;
            b1 += 16;
            b2 += 16;
        }

        for (int i = 0; i < rem; i++)
            if (data1 [len - 1 - i] != data2 [len - 1 - i])
                return false;

        return true;
    }
}

दो 256MB बाइट सरणियों पर मापा:

UnsafeCompare                           : 86,8784 ms
EqualBytesSimd                          : 71,5125 ms
EqualBytesSimdUnrolled                  : 73,1917 ms
EqualBytesLongUnrolled                  : 39,8623 ms

1
मैं इस बात की पुष्टि करता हूँ। मैंने टेस्ट भी चलाए। यह उस उत्तर की तुलना में तेज़ है जो मेम्कैंप असुरक्षित कॉल का उपयोग करता है।
ujeenator

1
@AmberdeBlack क्या आप सुनिश्चित हैं? क्या आपने छोटे सरणियों के साथ परीक्षण किया?
झार शारदान

@ArekBulski क्या आपको यकीन है कि यह मेमकैंप से तेज है, मेरे परीक्षण से पता चलता है?
ज़ार शारदान

मुझे इसके और मेमकैंप के बीच लगभग समान प्रदर्शन मिला, इसलिए पूरी तरह से प्रबंधित समाधान के लिए +1।
माइक मेरीनोस्की

10
 using System.Linq; //SequenceEqual

 byte[] ByteArray1 = null;
 byte[] ByteArray2 = null;

 ByteArray1 = MyFunct1();
 ByteArray2 = MyFunct2();

 if (ByteArray1.SequenceEqual<byte>(ByteArray2) == true)
 {
    MessageBox.Show("Match");
 }
 else
 {
   MessageBox.Show("Don't match");
 }

1
यही मैं उपयोग कर रहा हूं। लेकिन यह उम ... एक अनुक्रमिक तुलना की तरह लगता है जो आप अन्यथा एक साधारण लूप का उपयोग करते हैं, इसलिए बहुत तेज नहीं है। इसे प्रतिबिंबित करना और वास्तव में क्या कर रहे हैं यह देखना अच्छा होगा। नाम से देखते हुए, यह कुछ भी नहीं फैंसी है।
सेर्गेई अकोपोव

1
हां, लेकिन पहले से ही स्वीकृत उत्तर में उल्लेख किया गया है। btw, आप वहाँ प्रकार विनिर्देशन निकाल सकते हैं।
नवफ़ल

10

चलो एक और जोड़ो!

हाल ही में Microsoft ने एक विशेष NuGet पैकेज, System.Runtime.CompilerServices.Unsafe जारी किया । यह विशेष है क्योंकि यह IL में लिखा गया है , और निम्न स्तर की कार्यक्षमता प्रदान करता है जो सीधे C # में उपलब्ध नहीं है।

इसके तरीकों में Unsafe.As<T>(object)से एक, किसी भी सुरक्षा प्रकार को छोड़ते हुए, किसी भी संदर्भ प्रकार को किसी अन्य संदर्भ प्रकार में कास्टिंग करने की अनुमति देता है। यह आमतौर पर एक बहुत बुरा विचार है, लेकिन अगर दोनों प्रकार की संरचना समान है, तो यह काम कर सकता है। तो हम एक कास्ट करने के लिए इसका उपयोग कर सकते byte[]एक करने के लिए long[]:

bool CompareWithUnsafeLibrary(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length) return false;

    var longSize = (int)Math.Floor(a1.Length / 8.0);
    var long1 = Unsafe.As<long[]>(a1);
    var long2 = Unsafe.As<long[]>(a2);

    for (var i = 0; i < longSize; i++)
    {
        if (long1[i] != long2[i]) return false;
    }

    for (var i = longSize * 8; i < a1.Length; i++)
    {
        if (a1[i] != a2[i]) return false;
    }

    return true;
}

ध्यान दें कि long1.Lengthयह अभी भी मूल सरणी की लंबाई लौटाएगा, क्योंकि यह सरणी की मेमोरी संरचना में एक फ़ील्ड में संग्रहीत है।

यह विधि इतनी तेज़ नहीं है जितनी अन्य विधियों ने यहाँ प्रदर्शित की है, लेकिन यह भोली विधि की तुलना में बहुत तेज़ है, असुरक्षित कोड या पी / चालान या पिनिंग का उपयोग नहीं करती है, और कार्यान्वयन काफी सीधा है (आईएमओ)। यहाँ मेरी मशीन से कुछ बेंचमार्कडॉटनेट परिणाम हैं:

BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4870HQ CPU 2.50GHz, ProcessorCount=8
Frequency=2435775 Hz, Resolution=410.5470 ns, Timer=TSC
  [Host]     : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  DefaultJob : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0

                 Method |          Mean |    StdDev |
----------------------- |-------------- |---------- |
          UnsafeLibrary |   125.8229 ns | 0.3588 ns |
          UnsafeCompare |    89.9036 ns | 0.8243 ns |
           JSharpEquals | 1,432.1717 ns | 1.3161 ns |
 EqualBytesLongUnrolled |    43.7863 ns | 0.8923 ns |
              NewMemCmp |    65.4108 ns | 0.2202 ns |
            ArraysEqual |   910.8372 ns | 2.6082 ns |
          PInvokeMemcmp |    52.7201 ns | 0.1105 ns |

मैंने सभी परीक्षणों के साथ एक जिस्ट भी बनाया है ।


यह असुरक्षित कीवर्ड का उपयोग नहीं करता है, फिर भी यह System.Runtime.CompilerServices.Unsafe
Paulo Zemek

मैंने NewMemCmp
Mr Anderson

8

मैंने एक तरीका विकसित किया जो मेरे पीसी पर थोड़ा धड़कता है memcmp()( EqualBytesLongUnrolled()प्लिंथ का जवाब) और बहुत ही धीमी गति से धड़कता है । मूल रूप से, यह 8 के बजाय लूप को 4 से नियंत्रित करता है।

अपडेट 30 मार्च 2019 :

.NET कोर 3.0 में शुरू, हमारे पास SIMD सपोर्ट है!

यह समाधान मेरे पीसी पर काफी मार्जिन से सबसे तेज है:

#if NETCOREAPP3_0
using System.Runtime.Intrinsics.X86;
#endif


public static unsafe bool Compare(byte[] arr0, byte[] arr1)
{
    if (arr0 == arr1)
    {
        return true;
    }
    if (arr0 == null || arr1 == null)
    {
        return false;
    }
    if (arr0.Length != arr1.Length)
    {
        return false;
    }
    if (arr0.Length == 0)
    {
        return true;
    }
    fixed (byte* b0 = arr0, b1 = arr1)
    {
#if NETCOREAPP3_0
        if (Avx2.IsSupported)
        {
            return Compare256(b0, b1, arr0.Length);
        }
        else if (Sse2.IsSupported)
        {
            return Compare128(b0, b1, arr0.Length);
        }
        else
#endif
        {
            return Compare64(b0, b1, arr0.Length);
        }
    }
}
#if NETCOREAPP3_0
public static unsafe bool Compare256(byte* b0, byte* b1, int length)
{
    byte* lastAddr = b0 + length;
    byte* lastAddrMinus128 = lastAddr - 128;
    const int mask = -1;
    while (b0 < lastAddrMinus128) // unroll the loop so that we are comparing 128 bytes at a time.
    {
        if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0), Avx.LoadVector256(b1))) != mask)
        {
            return false;
        }
        if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0 + 32), Avx.LoadVector256(b1 + 32))) != mask)
        {
            return false;
        }
        if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0 + 64), Avx.LoadVector256(b1 + 64))) != mask)
        {
            return false;
        }
        if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0 + 96), Avx.LoadVector256(b1 + 96))) != mask)
        {
            return false;
        }
        b0 += 128;
        b1 += 128;
    }
    while (b0 < lastAddr)
    {
        if (*b0 != *b1) return false;
        b0++;
        b1++;
    }
    return true;
}
public static unsafe bool Compare128(byte* b0, byte* b1, int length)
{
    byte* lastAddr = b0 + length;
    byte* lastAddrMinus64 = lastAddr - 64;
    const int mask = 0xFFFF;
    while (b0 < lastAddrMinus64) // unroll the loop so that we are comparing 64 bytes at a time.
    {
        if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0), Sse2.LoadVector128(b1))) != mask)
        {
            return false;
        }
        if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0 + 16), Sse2.LoadVector128(b1 + 16))) != mask)
        {
            return false;
        }
        if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0 + 32), Sse2.LoadVector128(b1 + 32))) != mask)
        {
            return false;
        }
        if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0 + 48), Sse2.LoadVector128(b1 + 48))) != mask)
        {
            return false;
        }
        b0 += 64;
        b1 += 64;
    }
    while (b0 < lastAddr)
    {
        if (*b0 != *b1) return false;
        b0++;
        b1++;
    }
    return true;
}
#endif
public static unsafe bool Compare64(byte* b0, byte* b1, int length)
{
    byte* lastAddr = b0 + length;
    byte* lastAddrMinus32 = lastAddr - 32;
    while (b0 < lastAddrMinus32) // unroll the loop so that we are comparing 32 bytes at a time.
    {
        if (*(ulong*)b0 != *(ulong*)b1) return false;
        if (*(ulong*)(b0 + 8) != *(ulong*)(b1 + 8)) return false;
        if (*(ulong*)(b0 + 16) != *(ulong*)(b1 + 16)) return false;
        if (*(ulong*)(b0 + 24) != *(ulong*)(b1 + 24)) return false;
        b0 += 32;
        b1 += 32;
    }
    while (b0 < lastAddr)
    {
        if (*b0 != *b1) return false;
        b0++;
        b1++;
    }
    return true;
}

मेरी माप .NET 462 के लिए भिन्न होती है। NETCORE:
Motlicek Petr

दो 0-लंबाई सरणियों की तुलना करते समय कोड क्रैश हो जाता है, क्योंकि रिटर्निंग पिनिंग null
ग्लेन स्लेडेन

memcmp केवल एक इक्विटी तुलनित्र नहीं है। यह जानकारी प्रदान करता है कि कौन सी वस्तु बड़ी या छोटी है। क्या आप इस उद्देश्य के लिए अपने एल्गोरिथ्म को अपना सकते हैं और प्रदर्शन की जांच कर सकते हैं?
निकोले.न्यकीन्को

क्या इससे तेज है Spanऔर memcpy?
रेशम

। नेट कोर 3 और आधुनिक सीपीयू पर @silkfire, यह बड़े सरणियों के लिए 2-3 गुना तेज होना चाहिए।
श्री एंडरसन

6

मैं असुरक्षित कोड का उपयोग करता हूं और forInt32 पॉइंटर्स की तुलना करते हुए लूप चलाता हूं ।

हो सकता है कि आपको सरणियों की जाँच करने के लिए गैर-अशक्त होना चाहिए।


5

यदि आप देखते हैं कि .NET कैसे स्ट्रिंग करता है। तो आप देखते हैं कि यह एक निजी विधि का उपयोग करता है जिसे EqualsHelper कहा जाता है जिसमें "असुरक्षित" सूचक कार्यान्वयन होता है। .NET परावर्तक यह देखने के लिए आपका मित्र है कि चीजें आंतरिक रूप से कैसे की जाती हैं।

यह बाइट सरणी तुलना के लिए एक टेम्पलेट के रूप में इस्तेमाल किया जा सकता है जो मैंने ब्लॉग पोस्ट में एक कार्यान्वयन किया था फास्ट बाइट सरणी तुलना सी # में । मैंने यह देखने के लिए कुछ अल्पविकसित मानक भी किए कि असुरक्षित से अधिक सुरक्षित कार्यान्वयन कब हो।

जब तक आपको वास्तव में हत्यारे के प्रदर्शन की आवश्यकता नहीं है, मैंने कहा, मैं एक साधारण fr लूप तुलना के लिए जाऊंगा।


3

एक ऐसा समाधान नहीं मिल रहा है जिससे मैं पूरी तरह से खुश हूं (उचित प्रदर्शन, लेकिन कोई असुरक्षित कोड / पिनवोक) नहीं है इसलिए मैं इसके साथ आया हूं, वास्तव में मूल कुछ भी नहीं है, लेकिन काम करता है:

    /// <summary>
    /// 
    /// </summary>
    /// <param name="array1"></param>
    /// <param name="array2"></param>
    /// <param name="bytesToCompare"> 0 means compare entire arrays</param>
    /// <returns></returns>
    public static bool ArraysEqual(byte[] array1, byte[] array2, int bytesToCompare = 0)
    {
        if (array1.Length != array2.Length) return false;

        var length = (bytesToCompare == 0) ? array1.Length : bytesToCompare;
        var tailIdx = length - length % sizeof(Int64);

        //check in 8 byte chunks
        for (var i = 0; i < tailIdx; i += sizeof(Int64))
        {
            if (BitConverter.ToInt64(array1, i) != BitConverter.ToInt64(array2, i)) return false;
        }

        //check the remainder of the array, always shorter than 8 bytes
        for (var i = tailIdx; i < length; i++)
        {
            if (array1[i] != array2[i]) return false;
        }

        return true;
    }

इस पृष्ठ पर कुछ अन्य समाधानों की तुलना में प्रदर्शन:

सिंपल लूप: 19837 टिक्स, 1.00

* बिटकॉर्टर: 4886 टिक, 4.06

असुरक्षित श्रेणी: 1636 टिक, 12.12

इक्वलबाइट्सलॉन्ग नियंत्रित: 637 टिक, 31.09

पी / आह्वान ज्ञापन: 369 टिक, 53.67

लिंक्डपैड में परीक्षण किया गया, 1000000 बाइट्स समान सरणियों (सबसे खराब स्थिति), 500 पुनरावृत्तियों में से प्रत्येक।


हाँ, मैंने नोट किया कि stackoverflow.com/a/1445280/4489 की टिप्पणी में कि मेरे परीक्षण से पता चलता है कि यह वास्तव में लूप के लिए साधारण से थोड़ा धीमा है, जो कि मूल प्रश्न में था।
हाफथोर

क्या आपको यकीन है? मेरे परीक्षण में यह 4 गुना तेज है? कुछ भी अच्छा पुराने देशी कोड धड़कता है, भले ही दलदली भूमि के ऊपर।
ज़ार शारदान

3

ऐसा लगता है कि इक्वलबाइट्सलॉन्ग ट्रोल ऊपर दिए गए सुझाव से सबसे अच्छा है।

स्किप की गई विधियाँ (Enumerable.SequenceEqual, StructComComparisons.StructuralEqualityComparer.Equals), धीर-धीर नहीं थीं। 265MB सरणियों पर मैंने इसे मापा है:

Host Process Environment Information:
BenchmarkDotNet.Core=v0.9.9.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-3770 CPU 3.40GHz, ProcessorCount=8
Frequency=3323582 ticks, Resolution=300.8802 ns, Timer=TSC
CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
GC=Concurrent Workstation
JitModules=clrjit-v4.6.1590.0

Type=CompareMemoriesBenchmarks  Mode=Throughput  

                 Method |      Median |    StdDev | Scaled | Scaled-SD |
----------------------- |------------ |---------- |------- |---------- |
             NewMemCopy |  30.0443 ms | 1.1880 ms |   1.00 |      0.00 |
 EqualBytesLongUnrolled |  29.9917 ms | 0.7480 ms |   0.99 |      0.04 |
          msvcrt_memcmp |  30.0930 ms | 0.2964 ms |   1.00 |      0.03 |
          UnsafeCompare |  31.0520 ms | 0.7072 ms |   1.03 |      0.04 |
       ByteArrayCompare | 212.9980 ms | 2.0776 ms |   7.06 |      0.25 |

OS=Windows
Processor=?, ProcessorCount=8
Frequency=3323582 ticks, Resolution=300.8802 ns, Timer=TSC
CLR=CORE, Arch=64-bit ? [RyuJIT]
GC=Concurrent Workstation
dotnet cli version: 1.0.0-preview2-003131

Type=CompareMemoriesBenchmarks  Mode=Throughput  

                 Method |      Median |    StdDev | Scaled | Scaled-SD |
----------------------- |------------ |---------- |------- |---------- |
             NewMemCopy |  30.1789 ms | 0.0437 ms |   1.00 |      0.00 |
 EqualBytesLongUnrolled |  30.1985 ms | 0.1782 ms |   1.00 |      0.01 |
          msvcrt_memcmp |  30.1084 ms | 0.0660 ms |   1.00 |      0.00 |
          UnsafeCompare |  31.1845 ms | 0.4051 ms |   1.03 |      0.01 |
       ByteArrayCompare | 212.0213 ms | 0.1694 ms |   7.03 |      0.01 |

मैंने NewMemCmp
Mr Anderson

3

मैंने यहाँ कई linq solution नहीं देखे हैं।

मुझे प्रदर्शन के निहितार्थों पर यकीन नहीं है, हालांकि मैं आमतौर पर linqअंगूठे के नियम के रूप में रहता हूं और बाद में यदि आवश्यक हो तो अनुकूलन करता हूं ।

public bool CompareTwoArrays(byte[] array1, byte[] array2)
 {
   return !array1.Where((t, i) => t != array2[i]).Any();
 }

कृपया ध्यान दें कि यह केवल तभी काम करता है जब वे समान आकार के सरणियाँ हों। एक एक्सटेंशन ऐसा लग सकता है

public bool CompareTwoArrays(byte[] array1, byte[] array2)
 {
   if (array1.Length != array2.Length) return false;
   return !array1.Where((t, i) => t != array2[i]).Any();
 }

प्रश्न का पूरा बिंदु एक तेज़ समाधान है जिसे फ़ंक्शन प्रश्न में पोस्ट करता है।
कोडइन्चोस 15

3

मैंने संलग्न प्रोग्राम का उपयोग करके कुछ माप किए। नेट डिबगर संलग्न के बिना .net 4.7 रिलीज़ बिल्ड। मुझे लगता है कि लोग गलत मीट्रिक का उपयोग कर रहे हैं क्योंकि आप क्या कर रहे हैं यदि आप यहां गति के बारे में परवाह करते हैं तो यह पता लगाने में कितना समय लगता है कि दो बाइट सरणियों के बराबर हैं। बाइट्स में थ्रूपुट।

StructuralComparison :              4.6 MiB/s
for                  :            274.5 MiB/s
ToUInt32             :            263.6 MiB/s
ToUInt64             :            474.9 MiB/s
memcmp               :           8500.8 MiB/s

जैसा कि आप देख सकते हैं, इससे बेहतर कोई memcmpऔर तरीका नहीं है और यह तेजी के परिमाण का आदेश है। एक साधारण forलूप दूसरा सबसे अच्छा विकल्प है। और यह अभी भी मेरे मन को कचोटता है कि Microsoft केवल एक Buffer.Compareविधि को शामिल क्यों नहीं कर सकता ।

[Program.cs]:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace memcmp
{
    class Program
    {
        static byte[] TestVector(int size)
        {
            var data = new byte[size];
            using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
            {
                rng.GetBytes(data);
            }
            return data;
        }

        static TimeSpan Measure(string testCase, TimeSpan offset, Action action, bool ignore = false)
        {
            var t = Stopwatch.StartNew();
            var n = 0L;
            while (t.Elapsed < TimeSpan.FromSeconds(10))
            {
                action();
                n++;
            }
            var elapsed = t.Elapsed - offset;
            if (!ignore)
            {
                Console.WriteLine($"{testCase,-16} : {n / elapsed.TotalSeconds,16:0.0} MiB/s");
            }
            return elapsed;
        }

        [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern int memcmp(byte[] b1, byte[] b2, long count);

        static void Main(string[] args)
        {
            // how quickly can we establish if two sequences of bytes are equal?

            // note that we are testing the speed of different comparsion methods

            var a = TestVector(1024 * 1024); // 1 MiB
            var b = (byte[])a.Clone();

            // was meant to offset the overhead of everything but copying but my attempt was a horrible mistake... should have reacted sooner due to the initially ridiculous throughput values...
            // Measure("offset", new TimeSpan(), () => { return; }, ignore: true);
            var offset = TimeZone.Zero

            Measure("StructuralComparison", offset, () =>
            {
                StructuralComparisons.StructuralEqualityComparer.Equals(a, b);
            });

            Measure("for", offset, () =>
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (a[i] != b[i]) break;
                }
            });

            Measure("ToUInt32", offset, () =>
            {
                for (int i = 0; i < a.Length; i += 4)
                {
                    if (BitConverter.ToUInt32(a, i) != BitConverter.ToUInt32(b, i)) break;
                }
            });

            Measure("ToUInt64", offset, () =>
            {
                for (int i = 0; i < a.Length; i += 8)
                {
                    if (BitConverter.ToUInt64(a, i) != BitConverter.ToUInt64(b, i)) break;
                }
            });

            Measure("memcmp", offset, () =>
            {
                memcmp(a, b, a.Length);
            });
        }
    }
}

2

लघु बाइट सरणियों की तुलना करने के लिए निम्नलिखित एक दिलचस्प हैक है:

if(myByteArray1.Length != myByteArray2.Length) return false;
if(myByteArray1.Length == 8)
   return BitConverter.ToInt64(myByteArray1, 0) == BitConverter.ToInt64(myByteArray2, 0); 
else if(myByteArray.Length == 4)
   return BitConverter.ToInt32(myByteArray2, 0) == BitConverter.ToInt32(myByteArray2, 0); 

तब मैं शायद सवाल में सूचीबद्ध समाधान के लिए बाहर गिर जाएगा।

इस कोड का प्रदर्शन विश्लेषण करना दिलचस्प होगा।


int i = 0; के लिए (? i <a1.Length-7; i + = 8) अगर (BitConverter.ToInt64 (a1, i)! = BitConverter.ToInt64 (a2, i)) गलत है; के लिए (; i <a1.Length; i ++) अगर (a1 [i]! = a2 [i]) झूठा लौटें; सच लौटना; // लूप के लिए साधारण से थोड़ा धीमा।
हफ़थोर

2

आप में से जो लोग आदेश के बारे में परवाह करते हैं (यानी चाहते हैं कि आपकी memcmpवापसी ऐसी intहोनी चाहिए जैसे कि कुछ भी नहीं होना चाहिए), .NET कोर 3.0 (और संभवतः .NET मानक 2.1 उर्फ ​​.NET 5.0) में एक Span.SequenceCompareTo(...)विस्तार विधि (प्लस Span.SequenceEqualTo) शामिल होगी जो कर सकती है दो ReadOnlySpan<T>उदाहरणों ( where T: IComparable<T>) की तुलना करने के लिए उपयोग किया जाता है ।

में मूल GitHub प्रस्ताव , चर्चा, कूद तालिका गणना के साथ दृष्टिकोण की तुलना शामिल एक पढ़ने byte[]के रूप में long[], SIMD उपयोग, और पी / CLR कार्यान्वयन के लिए आह्वान memcmp

आगे बढ़ते हुए, यह बाइट सरणियों या बाइट श्रेणियों की तुलना करने के लिए आपका गो-टू तरीका होना चाहिए (जैसा कि आपके .NET मानक 2.1 एपीआई Span<byte>के byte[]लिए उपयोग करना चाहिए ), और यह पर्याप्त रूप से इतना तेज़ है कि अब आपको इसे अनुकूलित करने के बारे में परवाह नहीं करनी चाहिए (और नहीं, नाम में समानता होने के बावजूद यह भयानक रूप से भयावह प्रदर्शन नहीं करता है Enumerable.SequenceEqual)।

#if NETCOREAPP3_0
// Using the platform-native Span<T>.SequenceEqual<T>(..)
public static int Compare(byte[] range1, int offset1, byte[] range2, int offset2, int count)
{
    var span1 = range1.AsSpan(offset1, count);
    var span2 = range2.AsSpan(offset2, count);

    return span1.SequenceCompareTo(span2);
    // or, if you don't care about ordering
    // return span1.SequenceEqual(span2);
}
#else
// The most basic implementation, in platform-agnostic, safe C#
public static bool Compare(byte[] range1, int offset1, byte[] range2, int offset2, int count)
{
    // Working backwards lets the compiler optimize away bound checking after the first loop
    for (int i = count - 1; i >= 0; --i)
    {
        if (range1[offset1 + i] != range2[offset2 + i])
        {
            return false;
        }
    }

    return true;
}
#endif

1

मैंने कई ग्राफिक्स कार्ड में निर्मित ब्लॉक-ट्रांसफर त्वरण विधियों के बारे में सोचा। लेकिन तब आपको सभी डेटा को बाइट-वार पर कॉपी करना होगा, इसलिए यह आपकी बहुत मदद नहीं करता है यदि आप अपने तर्क के पूरे हिस्से को अनवांटेड और हार्डवेयर-निर्भर कोड में लागू नहीं करना चाहते हैं ...

ऊपर दिखाए गए दृष्टिकोण के समान अनुकूलन का एक अन्य तरीका यह होगा कि आप बाइट के बजाय [[] शुरू से ही सही [] अपने डेटा का अधिक से अधिक संग्रह करें, उदाहरण के लिए यदि आप इसे बाइनरी फ़ाइल से क्रमिक रूप से पढ़ रहे हैं, या यदि आप एक मेमोरी मैप की गई फ़ाइल का उपयोग करते हैं, तो डेटा को लंबे [] या एकल लंबे मानों में पढ़ें। फिर, आपके तुलना लूप को केवल बाइट की संख्या के 1/8 वें भाग की आवश्यकता होगी जो कि बाइट के लिए करना होगा [] जिसमें समान डेटा हो। यह कब और कितनी बार आपको तुलना करने की आवश्यकता है, कब और कितनी बार आपको बाइट-बाय-बाय तरीके से डेटा तक पहुंचने की आवश्यकता है, उदाहरण के लिए एपीआई कॉल में एक पैरामीटर के रूप में इसका उपयोग करने की आवश्यकता होती है। एक बाइट []। अंत में, आप केवल यह बता सकते हैं कि क्या आप वास्तव में उपयोग के मामले को जानते हैं ...


स्वीकृत उत्तर बाइट बफर को एक लंबे बफर के रूप में पुन: प्रस्तुत करता है और जैसा आप वर्णन करते हैं, उसकी तुलना करता है।
हफथोर

1

यह यहाँ दिए गए किसी भी अन्य संस्करण की तुलना में लगभग निश्चित रूप से बहुत धीमा है, लेकिन यह लिखने में मजेदार था।

static bool ByteArrayEquals(byte[] a1, byte[] a2) 
{
    return a1.Zip(a2, (l, r) => l == r).All(x => x);
}

1

मैं एक अतिरिक्त अनुकूलन के साथ ArekBulski द्वारा पोस्ट EqualBytesLongUnrolled विधि से प्रेरित एक समाधान पर बस गया। मेरे उदाहरण में, सरणियों में सरणी अंतर सरणियों की पूंछ के पास होता है। परीक्षण में, मैंने पाया कि जब यह बड़े सरणियों के लिए मामला है, तो सरणी तत्वों की उल्टे क्रम में तुलना करने में सक्षम होने से यह समाधान मेम्कैंप आधारित समाधान पर एक बड़ा प्रदर्शन लाभ देता है। यहाँ वह समाधान है:

public enum CompareDirection { Forward, Backward }

private static unsafe bool UnsafeEquals(byte[] a, byte[] b, CompareDirection direction = CompareDirection.Forward)
{
    // returns when a and b are same array or both null
    if (a == b) return true;

    // if either is null or different lengths, can't be equal
    if (a == null || b == null || a.Length != b.Length)
        return false;

    const int UNROLLED = 16;                // count of longs 'unrolled' in optimization
    int size = sizeof(long) * UNROLLED;     // 128 bytes (min size for 'unrolled' optimization)
    int len = a.Length;
    int n = len / size;         // count of full 128 byte segments
    int r = len % size;         // count of remaining 'unoptimized' bytes

    // pin the arrays and access them via pointers
    fixed (byte* pb_a = a, pb_b = b)
    {
        if (r > 0 && direction == CompareDirection.Backward)
        {
            byte* pa = pb_a + len - 1;
            byte* pb = pb_b + len - 1;
            byte* phead = pb_a + len - r;
            while(pa >= phead)
            {
                if (*pa != *pb) return false;
                pa--;
                pb--;
            }
        }

        if (n > 0)
        {
            int nOffset = n * size;
            if (direction == CompareDirection.Forward)
            {
                long* pa = (long*)pb_a;
                long* pb = (long*)pb_b;
                long* ptail = (long*)(pb_a + nOffset);
                while (pa < ptail)
                {
                    if (*(pa + 0) != *(pb + 0) || *(pa + 1) != *(pb + 1) ||
                        *(pa + 2) != *(pb + 2) || *(pa + 3) != *(pb + 3) ||
                        *(pa + 4) != *(pb + 4) || *(pa + 5) != *(pb + 5) ||
                        *(pa + 6) != *(pb + 6) || *(pa + 7) != *(pb + 7) ||
                        *(pa + 8) != *(pb + 8) || *(pa + 9) != *(pb + 9) ||
                        *(pa + 10) != *(pb + 10) || *(pa + 11) != *(pb + 11) ||
                        *(pa + 12) != *(pb + 12) || *(pa + 13) != *(pb + 13) ||
                        *(pa + 14) != *(pb + 14) || *(pa + 15) != *(pb + 15)
                    )
                    {
                        return false;
                    }
                    pa += UNROLLED;
                    pb += UNROLLED;
                }
            }
            else
            {
                long* pa = (long*)(pb_a + nOffset);
                long* pb = (long*)(pb_b + nOffset);
                long* phead = (long*)pb_a;
                while (phead < pa)
                {
                    if (*(pa - 1) != *(pb - 1) || *(pa - 2) != *(pb - 2) ||
                        *(pa - 3) != *(pb - 3) || *(pa - 4) != *(pb - 4) ||
                        *(pa - 5) != *(pb - 5) || *(pa - 6) != *(pb - 6) ||
                        *(pa - 7) != *(pb - 7) || *(pa - 8) != *(pb - 8) ||
                        *(pa - 9) != *(pb - 9) || *(pa - 10) != *(pb - 10) ||
                        *(pa - 11) != *(pb - 11) || *(pa - 12) != *(pb - 12) ||
                        *(pa - 13) != *(pb - 13) || *(pa - 14) != *(pb - 14) ||
                        *(pa - 15) != *(pb - 15) || *(pa - 16) != *(pb - 16)
                    )
                    {
                        return false;
                    }
                    pa -= UNROLLED;
                    pb -= UNROLLED;
                }
            }
        }

        if (r > 0 && direction == CompareDirection.Forward)
        {
            byte* pa = pb_a + len - r;
            byte* pb = pb_b + len - r;
            byte* ptail = pb_a + len;
            while(pa < ptail)
            {
                if (*pa != *pb) return false;
                pa++;
                pb++;
            }
        }
    }

    return true;
}

0

क्षमा करें, यदि आप एक ऐसे प्रबंधित तरीके की तलाश कर रहे हैं जो आप पहले से ही सही तरीके से कर रहे हैं और मेरे ज्ञान में ऐसा करने के लिए BCL में कोई विधि नहीं है।

आपको कुछ प्रारंभिक अशक्त जांचों को जोड़ना चाहिए और फिर इसे पुन: उपयोग करना चाहिए जैसे कि जहां बीसीएल में है।


जब आपने लिखा था कि आप सही थे, हालांकि 2010 में (.NET 4.0) एक BCL विधि आई थी, ओहद श्नाइडर का उत्तर देखें। प्रश्न के समय, .NET 3.5 में Linq था (aku का उत्तर देखें)।
जेपी स्टिग नील्सन

-1

SequenceEqualsतुलना करने के लिए इस का उपयोग करें ।


-2

यदि आप एक बहुत तेजी से बाइट सरणी समानता तुलनकर्ता की तलाश कर रहे हैं, तो मेरा सुझाव है कि आप इस STSdb ​​लैब्स लेख पर एक नज़र डालें: बाइट सरणी समानता तुलनित्र।यह बाइट [] सरणी समानता की तुलना में सबसे तेज़ कार्यान्वयन में से कुछ को प्रस्तुत करता है, जिन्हें प्रस्तुत किया जाता है, प्रदर्शन का परीक्षण किया जाता है और संक्षेप में प्रस्तुत किया जाता है।

आप इन कार्यान्वयनों पर भी ध्यान केंद्रित कर सकते हैं:

BigEndianByteArrayComparer - तेजी से बाइट [] सरणी तुलनित्र बाएं से दाएं (BigEndian) BigEndianByteArrayEqualityComparer - - फास्ट बाइट [] बराबरी दाएं बाएं से दाएं (BigEndian) LittleEndianByteArrayCompasser.com - फास्ट बाइट्स -। [] समानता की तुलना दाएं से बाएं (LittleEndian)


-2

संक्षिप्त उत्तर यह है:

    public bool Compare(byte[] b1, byte[] b2)
    {
        return Encoding.ASCII.GetString(b1) == Encoding.ASCII.GetString(b2);
    }

इस तरह से आप असुरक्षित कोड लिखने की आवश्यकता के बिना बाइट सरणी की तुलना करने के लिए अनुकूलित .NET स्ट्रिंग तुलना का उपयोग कर सकते हैं। यह पृष्ठभूमि में कैसे किया जाता है :

private unsafe static bool EqualsHelper(String strA, String strB)
{
    Contract.Requires(strA != null);
    Contract.Requires(strB != null);
    Contract.Requires(strA.Length == strB.Length);

    int length = strA.Length;

    fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
    {
        char* a = ap;
        char* b = bp;

        // Unroll the loop

        #if AMD64
            // For the AMD64 bit platform we unroll by 12 and
            // check three qwords at a time. This is less code
            // than the 32 bit case and is shorter
            // pathlength.

            while (length >= 12)
            {
                if (*(long*)a     != *(long*)b)     return false;
                if (*(long*)(a+4) != *(long*)(b+4)) return false;
                if (*(long*)(a+8) != *(long*)(b+8)) return false;
                a += 12; b += 12; length -= 12;
            }
       #else
           while (length >= 10)
           {
               if (*(int*)a != *(int*)b) return false;
               if (*(int*)(a+2) != *(int*)(b+2)) return false;
               if (*(int*)(a+4) != *(int*)(b+4)) return false;
               if (*(int*)(a+6) != *(int*)(b+6)) return false;
               if (*(int*)(a+8) != *(int*)(b+8)) return false;
               a += 10; b += 10; length -= 10;
           }
       #endif

        // This depends on the fact that the String objects are
        // always zero terminated and that the terminating zero is not included
        // in the length. For odd string sizes, the last compare will include
        // the zero terminator.
        while (length > 0)
        {
            if (*(int*)a != *(int*)b) break;
            a += 2; b += 2; length -= 2;
        }

        return (length <= 0);
    }
}

मेरे परीक्षणों में, एक स्ट्रिंग में रूपांतरण तेजी से तुलना के लाभ को नष्ट कर देता है। यह लूप के लिए एक साधारण से लगभग 2.5 गुना धीमा था।
डग क्लटर

जब मैंने वही किया तो वह लगभग 8 गुना धीमा था। क्या आप अपना कोड यहाँ लिख सकते हैं?
Alon

1
यदि एक बाइट में अशक्त (0) मान है तो क्या यह टूट जाएगा?
जोसेफ लेनॉक्स

-1 के साथ-साथ @DougClutter द्वारा इंगित स्ट्रिंग में रूपांतरण के कारण धीमा होने के कारण, यह विफल हो जाएगा यदि बाइट सरणी में गैर-एएससीआईआई डेटा है। सही परिणाम प्राप्त करने के लिए इसे iso-8859-1 का उपयोग करना होगा।
जो

2
Compare(new byte[]{128}, new byte[]{ 255 }) == trueछोटी गाड़ी नहीं ...
15

-2

चूंकि ऊपर दिए गए कई फैंसी समाधान UWP के साथ काम नहीं करते हैं और क्योंकि मैं Linq से प्यार करता हूं और कार्यात्मक दृष्टिकोण मैं इस समस्या के लिए आपको मेरा संस्करण दबाता हूं। पहला अंतर होने पर तुलना से बचने के लिए, मैंने चुना। .FirstOrDefault ()

public static bool CompareByteArrays(byte[] ba0, byte[] ba1) =>
    !(ba0.Length != ba1.Length || Enumerable.Range(1,ba0.Length)
        .FirstOrDefault(n => ba0[n] != ba1[n]) > 0);

-1 क्योंकि यह कोड टूटा हुआ है और स्पष्ट रूप से अप्रयुक्त है। यह एक फेंकता है IndexOutOfRangeExceptionजब गैर खाली सरणियों की तुलना क्योंकि आप तत्वों पहुँच बना रहे हैं 1के माध्यम से ba0.Lengthजब यह होना चाहिए 0के माध्यम से ba0.Length - 1। यदि आप इसे ठीक करते हैं, Enumerable.Range(0, ba0.Length)तब भी यह गलत तरीके trueसे समान लंबाई के सरणियों के लिए लौटता है जहां केवल पहले तत्व भिन्न होते हैं क्योंकि आप पहले तत्वों के बीच अंतर नहीं कर सकते हैं संतोषजनक predicateऔर कोई तत्व संतोषजनक नहीं predicate; दोनों मामलों में FirstOrDefault<int>()रिटर्न 0
BACON

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