Runtime: الاقتراح: أضف System.HashCode لتسهيل إنشاء أكواد تجزئة جيدة.

تم إنشاؤها على ٩ ديسمبر ٢٠١٦  ·  182تعليقات  ·  مصدر: dotnet/runtime

تحديث 6/16/17: البحث عن متطوعين

تم الانتهاء من شكل API. ومع ذلك ، ما زلنا نتخذ قرارًا بشأن أفضل خوارزمية تجزئة من قائمة المرشحين لاستخدامها في التنفيذ ، ونحتاج إلى شخص ما لمساعدتنا في قياس الإنتاجية / التوزيع لكل خوارزمية. إذا كنت ترغب في تولي هذا الدور ، فيرجى ترك تعليق أدناه وسيقوم @ Karelz بتعيين هذه المشكلة لك.

تحديث 6/13/17: تم قبول الاقتراح!

إليك واجهة برمجة التطبيقات التي تمت الموافقة عليها من قبل terrajobst على https://github.com/dotnet/corefx/issues/14354#issuecomment -308190321:

// Will live in the core assembly
// .NET Framework : mscorlib
// .NET Core      : System.Runtime / System.Private.CoreLib
namespace System
{
    public struct HashCode
    {
        public static int Combine<T1>(T1 value1);
        public static int Combine<T1, T2>(T1 value1, T2 value2);
        public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3);
        public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4);
        public static int Combine<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5);
        public static int Combine<T1, T2, T3, T4, T5, T6>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6);
        public static int Combine<T1, T2, T3, T4, T5, T6, T7>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7);
        public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8);

        public void Add<T>(T value);
        public void Add<T>(T value, IEqualityComparer<T> comparer);

        [Obsolete("Use ToHashCode to retrieve the computed hash code.", error: true)]
        [EditorBrowsable(Never)]
        public override int GetHashCode();

        public int ToHashCode();
    }
}

النص الأصلي لهذا الاقتراح كما يلي.

المنطق

يجب ألا يتطلب إنشاء رمز تجزئة جيد استخدام الثوابت السحرية القبيحة والتلاعب في التعليمات البرمجية الخاصة بنا. يجب أن يكون الأمر أقل إغراءً لكتابة تنفيذ سيء ولكن موجز GetHashCode مثل

class Person
{
    public override int GetHashCode() => FirstName.GetHashCode() + LastName.GetHashCode();
}

عرض

يجب أن نضيف نوع HashCode لإنشاء كود التجزئة وتجنب إجبار المطورين على الاختلاط بالتفاصيل الفوضوية. هذا هو اقتراحي ، الذي يستند إلى https://github.com/dotnet/corefx/issues/14354#issuecomment -305019329 ، مع بعض التنقيحات الطفيفة.

// Will live in the core assembly
// .NET Framework : mscorlib
// .NET Core      : System.Runtime / System.Private.CoreLib
namespace System
{
    public struct HashCode
    {
        public static int Combine<T1>(T1 value1);
        public static int Combine<T1, T2>(T1 value1, T2 value2);
        public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3);
        public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4);
        public static int Combine<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5);
        public static int Combine<T1, T2, T3, T4, T5, T6>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6);
        public static int Combine<T1, T2, T3, T4, T5, T6, T7>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7);
        public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8);

        public void Add<T>(T value);
        public void Add<T>(T value, IEqualityComparer<T> comparer);
        public void AddRange<T>(T[] values);
        public void AddRange<T>(T[] values, int index, int count);
        public void AddRange<T>(T[] values, int index, int count, IEqualityComparer<T> comparer);

        [Obsolete("Use ToHashCode to retrieve the computed hash code.", error: true)]
        public override int GetHashCode();

        public int ToHashCode();
    }
}

ملاحظات

انظرterrajobst تعليق الصورة في https://github.com/dotnet/corefx/issues/14354#issuecomment -305019329 لأهداف هذا API. كل ملاحظاته صحيحة. أود أن أشير إلى هؤلاء على وجه الخصوص ، ومع ذلك:

  • لا تحتاج واجهة برمجة التطبيقات إلى إنتاج تجزئة تشفير قوية
  • ستوفر واجهة برمجة التطبيقات كود تجزئة "a" ، ولكنها لا تضمن خوارزمية كود تجزئة معينة. يتيح لنا ذلك استخدام خوارزمية مختلفة لاحقًا أو استخدام خوارزميات مختلفة في بنيات مختلفة.
  • ستضمن واجهة برمجة التطبيقات (API) أنه خلال عملية معينة ، ستنتج نفس القيم نفس رمز التجزئة. من المحتمل أن ينتج عن مثيلات مختلفة لنفس التطبيق رموز تجزئة مختلفة بسبب التوزيع العشوائي. يتيح لنا ذلك ضمان عدم تمكن المستهلكين من الاستمرار في قيم التجزئة والاعتماد بشكل عرضي على استقرارها عبر عمليات التشغيل (أو الأسوأ من ذلك ، إصدارات النظام الأساسي).
api-approved area-System.Numerics up-for-grabs

التعليق الأكثر فائدة

قرارات

  • يجب علينا إزالة جميع طرق AddRange لأن السيناريو غير واضح. من غير المرجح إلى حد ما أن تظهر Array's كثيرًا. وبمجرد تضمين المصفوفات الكبيرة ، فإن السؤال المطروح هو ما إذا كان يجب تخزين الحساب مؤقتًا. توضح رؤية حلقة for على جانب الاتصال أنك بحاجة إلى التفكير في ذلك.
  • كما أننا لا نريد إضافة حمولات زائدة من IEnumerable إلى AddRange لأنها ستخصص.
  • لا نعتقد أننا بحاجة إلى التحميل الزائد إلى Add الذي يأخذ string و StringComparison . نعم ، من المحتمل أن تكون هذه أكثر فاعلية من الاتصال عبر IEqualityComparer ، لكن يمكننا إصلاح ذلك لاحقًا.
  • نعتقد أن وضع علامة على GetHashCode عفا عليه الزمن مع وجود خطأ فكرة جيدة ، لكننا سنذهب إلى أبعد من ذلك ونخفي أيضًا من IntelliSense.

هذا يترك لنا:

ج #
// سيعيش في التجميع الأساسي
// .NET Framework: mscorlib
// NET Core: System.Runtime / System.Private.CoreLib
نظام مساحة الاسم
{
HashCode الهيكل العام
{
الجمع بين كثافة العمليات العامة العامة(T1 value1) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2) ؛
الجمع بين كثافة العمليات العامة العامة(قيمة T1 ، قيمة T2 ، قيمة T3) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6، T7 value7) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6، T7 value7، T8 value8) ؛

    public void Add<T>(T value);
    public void Add<T>(T value, IEqualityComparer<T> comparer);

    [Obsolete("Use ToHashCode to retrieve the computed hash code.", error: true)]
    [EditorBrowsable(Never)]
    public override int GetHashCode();

    public int ToHashCode();
}

}
""

ال 182 كومينتر

اقتراح: إضافة دعم عشوائية التجزئة

public static HashCode Randomized<T> { get; } // or CreateRandomized<T>
or 
public static HashCode Randomized(Type type); // or CreateRandomized(Type type)

مطلوب T أو Type type للحصول على نفس التجزئة العشوائية لنفس النوع.

اقتراح: إضافة دعم للمجموعات

public HashCode Combine<T>(T[] values);
public HashCode Combine<T>(T[] values, IEqualityComparer<T> comparer);
public HashCode Combine<T>(Span<T> values);
public HashCode Combine<T>(Span<T> values, IEqualityComparer<T> comparer);
public HashCode Combine<T>(IEnumerable<T> values);
public HashCode Combine<T>(IEnumerable<T> IEqualityComparer<T> comparer);

أعتقد أنه ليست هناك حاجة للحمل الزائد Combine(_field1, _field2, _field3, _field4, _field5) لأن الكود التالي HashCode.Empty.Combine(_field1).Combine(_field2).Combine(_field3).Combine(_field4).Combine(_field5); يجب أن يكون مضمنًا محسنًا بدون الجمع بين المكالمات.

تضمين التغريدة

اقتراح: إضافة دعم للمجموعات

نعم ، كان ذلك جزءًا من خطتي النهائية لهذا الاقتراح. أعتقد أنه من المهم التركيز على الكيفية التي نريد أن تبدو بها واجهة برمجة التطبيقات قبل أن نبدأ في إضافة هذه الأساليب.

لقد أراد استخدام خوارزمية مختلفة ، مثل تجزئة Marvin32 التي تُستخدم للسلاسل في coreclr. سيتطلب هذا توسيع حجم HashCode إلى 8 بايت.

ماذا عن وجود أنواع Hash32 و Hash64 التي من شأنها تخزين 4 أو 8 بايت من البيانات داخليًا؟ وثق إيجابيات / سلبيات كل منها. Hash64 مفيد لـ X ، ولكن من المحتمل أن يكون أبطأ. Hash32 أسرع ، ولكن من المحتمل ألا تكون موزعة (أو أيًا كانت المقايضة في الواقع).

لقد أراد توزيع بذور التجزئة بشكل عشوائي ، لذلك لن تكون التجزئة حتمية.

يبدو هذا كأنه سلوك مفيد. لكن يمكنني رؤية الناس يريدون السيطرة على هذا. لذلك ربما يجب أن تكون هناك طريقتان لإنشاء التجزئة ، واحدة لا تأخذ بذرة (وتستخدم بذرة عشوائية) والأخرى تسمح بتوفير البذور.

ملاحظة: ستحب Roslyn إذا كان من الممكن توفير هذا في الفوركس. نحن نضيف ميزة لإخراج GetHashCode للمستخدم. حاليًا ، يقوم بإنشاء رمز مثل:

c# public override int GetHashCode() { var hashCode = -1923861349; hashCode = hashCode * -1521134295 + this.b.GetHashCode(); hashCode = hashCode * -1521134295 + this.i.GetHashCode(); hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(this.s); return hashCode; }

هذه ليست تجربة رائعة ، وهي تكشف الكثير من المفاهيم القبيحة. سيكون من دواعي سرورنا أن يكون لدينا Hash. مهما كانت واجهة برمجة التطبيقات التي يمكننا الاتصال بها بدلاً من ذلك.

شكرا!

ماذا عن MurmurHash؟ إنه سريع بشكل معقول وله خصائص تجزئة جيدة جدًا. هناك أيضًا تطبيقان مختلفان ، أحدهما يبث تجزئة 32 بت والآخر يبصق تجزئة 128 بت.

هناك أيضًا تطبيقات موجهة لكل من تنسيقات 32 بت و 128 بت.

tannergooding MurmurHash سريع ، لكنه غير آمن ، من أصوات منشور المدونة هذا .

jkotas ، هل كان هناك أي عمل في JIT حول إنشاء كود أفضل لـ> CyrusNajmabadi :

ماذا عن وجود أنواع Hash32 و Hash64 التي من شأنها تخزين 4 أو 8 بايت من البيانات داخليًا؟ وثق إيجابيات / سلبيات كل منها. Hash64 مفيد لـ X ، ولكن من المحتمل أن يكون أبطأ. Hash32 أسرع ، ولكن من المحتمل ألا تكون موزعة (أو أيًا كانت المقايضة في الواقع).

ما زلت أعتقد أن هذا النوع سيكون ذا قيمة كبيرة لتقديمه للمطورين وسيكون من الرائع الحصول عليه في الإصدار 2.0.

jamesqo ، لا أعتقد أن هذا التنفيذ يجب أن يكون آمنًا من الناحية المشفرة (هذا هو الغرض من وظائف التجزئة المشفرة الصريحة).

أيضًا ، تنطبق هذه المقالة على Murmur2. تم حل المشكلة في خوارزمية Murmur3.

JIT حول إنشاء رمز أفضل للبُنى ذات 4 بايت على 32 بت منذ مناقشاتنا العام الماضي

أنا لست على علم بأي.

ما رأيك في اقتراح CyrusNajmabadi

يجب أن تكون أنواع الأطر اختيارات بسيطة تعمل بشكل جيد مع 95٪ + من الحالات. قد لا يكونون الأسرع ، لكن هذا جيد. إن اختيارك بين Hash32 و Hash64 ليس خيارًا بسيطًا.

هذا جيد بالنسبة لي. لكن هل يمكننا على الأقل الحصول على حل جيد بما فيه الكفاية لتلك الحالات التي تبلغ 95٪؟ الآن لا يوجد شيء ...: - /

hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode (this.s) ؛

CyrusNajmabadi لماذا تتصل بـ EqualityComparer هنا ، وليس فقط this.s.GetHashCode ()؟

لغير البنيات: حتى لا نحتاج إلى التحقق من وجود قيمة خالية.

هذا قريب مما نقوم بإنشائه للأنواع المجهولة خلف الكواليس أيضًا. أقوم بتحسين حالة القيم المعروفة غير الفارغة لإنشاء رمز يكون أكثر إرضاءً للمستخدمين. ولكن سيكون من الجيد أن يكون لديك فقط واجهة برمجة تطبيقات مدمجة لهذا الغرض.

استدعاء EqualityComparer.Default.GetHashCode يشبه 10x + أكثر تكلفة من التحقق من القيمة null ...

استدعاء EqualityComparer.Default.GetHashCode مثل 10x + أغلى من البحث عن null ..

يبدو وكأنه مشكلة. إذا كان هناك فقط واجهة برمجة تطبيقات جيدة لرمز التجزئة ، فيمكننا استدعاء الفوركس الذي يمكنني التأجيل إليه :)

(أيضًا ، لدينا هذه المشكلة في الأنواع المجهولة لدينا لأن هذا ما نولده هناك أيضًا).

لست متأكدًا مما نفعله مع tuples ، لكنني أعتقد أنه مشابه.

لست متأكدًا مما نفعله مع tuples ، لكنني أعتقد أنه مشابه.

يمر System.Tuple عبر EqualityComparer<Object>.Default لأسباب تاريخية. يستدعي System.ValueTuple Object.GetHashCode مع فحص فارغ - https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/ValueTuple.cs#L809.

أوه لا. يبدو أن tuple يمكنه فقط استخدام "HashHelpers". هل يمكن الكشف عن ذلك حتى يتمكن المستخدمون من الحصول على نفس الفائدة؟

رائعة. يسعدني أن أفعل شيئًا مشابهًا. لقد بدأت من الأنواع المجهولة لأنني اعتقدت أنها أفضل الممارسات المعقولة. إذا لم يكن كذلك ، فلا بأس بذلك. :)

لكن هذا ليس سبب وجودي هنا. أنا هنا لأحصل على نظام ما يجمع بين التجزئات بفعالية. إذا / متى يمكن توفير ذلك ، فسننتقل بكل سرور إلى الاتصال بذلك بدلاً من الترميز الثابت بأرقام عشوائية والجمع بين قيم التجزئة بأنفسنا.

ما هو شكل واجهة برمجة التطبيقات الذي تعتقد أنه سيعمل بشكل أفضل مع الكود الذي تم إنشاؤه بواسطة المترجم؟

حرفيا أي من حلول 32 بت التي تم تقديمها في وقت سابق ستكون مناسبة لي. هيك ، حلول 64 بت جيدة معي. مجرد نوع من واجهة برمجة التطبيقات (API) التي يمكنك الحصول عليها والتي تقول "يمكنني دمج التجزئة بطريقة ما بطريقة معقولة وإنتاج نتيجة موزعة بشكل معقول".

لا يمكنني التوفيق بين هذه العبارات:

كان لدينا بنية HashCode غير قابلة للتغيير بحجم 4 بايت. كان لديه طريقة Combine (int) ، والتي تمزج في كود التجزئة المقدم مع كود التجزئة الخاص بها عبر خوارزمية تشبه DJBX33X ، وأرجع HashCode جديد.

jkotas لم يعتقد أن الخوارزمية الشبيهة بـ DJBX33X كانت قوية بما يكفي.

و

يجب أن تكون أنواع الأطر اختيارات بسيطة تعمل بشكل جيد مع 95٪ + من الحالات.

ألا يمكننا التوصل إلى تجزئة تراكمية بسيطة 32 بت تعمل بشكل جيد بما يكفي لـ 95٪ من الحالات؟ ما هي الحالات التي لم يتم التعامل معها بشكل جيد هنا ، ولماذا نعتقد أنها في حالة 95٪؟

jkotas ، هل الأداء حقًا بالغ الأهمية لهذا النوع؟ أعتقد في المتوسط ​​أشياء مثل عمليات البحث القابلة للتجزئة ، وهذا سيستغرق وقتًا أطول من بضع نسخ هيكلية. إذا تبين أنه يمثل عنق زجاجة ، فهل سيكون من المعقول أن نطلب من فريق JIT تحسين نسخ بنية 32 بت بعد إصدار API بحيث يكون لديهم بعض الحوافز ، بدلاً من حظر واجهة برمجة التطبيقات هذه على ذلك عندما لا يعمل أحد على التحسين نسخ؟

ألا يمكننا التوصل إلى تجزئة تراكمية بسيطة 32 بت تعمل بشكل جيد بما يكفي لـ 95٪ من الحالات؟

لقد تم حرقنا بشكل سيء جدًا افتراضيًا 32 بت تراكم تجزئة للسلاسل ، ولهذا السبب تجزئة مارفن للسلاسل في .NET Core - https://github.com/dotnet/corert/blob/87e58839d6629b5f90777f886a2f52d7a99c076f/src/System.Private.CoreLib/ src / System / Marvin.cs # L25. لا أعتقد أننا نريد تكرار نفس الخطأ هنا.

jkotas ، هل الأداء حقًا بالغ الأهمية لهذا النوع؟

لا أعتقد أن الأداء حرج. نظرًا لأنه يبدو أن واجهة برمجة التطبيقات هذه ستُستخدم بواسطة كود مترجم مُنشأ تلقائيًا ، أعتقد أننا يجب أن نفضل الكود الأصغر الذي تم إنشاؤه على شكله. النمط غير الطلاقة هو رمز أصغر.

لقد تم حرقنا بشكل سيء جدًا افتراضيًا 32 بت تراكم تجزئة لسلسلة

هذا لا يبدو مثل حالة 95٪. نحن نتحدث عن المطورين العاديين الذين يريدون فقط تجزئة "جيدة بما فيه الكفاية" لجميع تلك الأنواع حيث يقومون بالأشياء يدويًا اليوم.

نظرًا لأنه يبدو أن واجهة برمجة التطبيقات هذه ستُستخدم بواسطة كود مترجم مُنشأ تلقائيًا ، أعتقد أننا يجب أن نفضل الكود الأصغر الذي تم إنشاؤه على شكله. النمط غير الطلاقة هو رمز أصغر.

هذا ليس للاستخدام من قبل مترجم Roslyn. هذا للاستخدام بواسطة Roslyn IDE عندما نساعد المستخدمين على إنشاء رموز GetHashCodes لأنواعهم. هذا هو الكود الذي سيراه المستخدم وسيتعين عليه الحفاظ عليه ، ولديه شيء معقول مثل:

ج #
إرجاع Hash.Combine (this.A؟ .GetHashCode () ؟؟ 0،
this.B؟ .GetHashCode () ؟؟ 0 ،
this.C؟ .GetHashCode () ؟؟ 0)؛

is a lot nicer than a user seeing and having to maintain:

```c#
            var hashCode = -1923861349;
            hashCode = hashCode * -1521134295 + this.b.GetHashCode();
            hashCode = hashCode * -1521134295 + this.i.GetHashCode();
            hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(this.s);
            return hashCode;

أعني ، لدينا بالفعل هذا الرمز في الفوركس:

https://github.com/dotnet/roslyn/blob/master/src/Compilers/Test/Resources/Core/NetFX/ValueTuple/ValueTuple.cs#L5

نعتقد أنه جيد بما يكفي لـ tuple. ليس من الواضح بالنسبة لي سبب وجود مثل هذه المشكلة في إتاحتها للمستخدمين الذين يريدون ذلك لأنواعهم الخاصة.

ملاحظة: لقد فكرنا في القيام بذلك في روزلين:

c# return (this.A, this.B, this.C).GetHashCode();

لكنك الآن تجبر الأشخاص على إنشاء بنية (يحتمل أن تكون كبيرة) لمجرد الحصول على نوع من سلوك التجزئة الافتراضي المعقول.

نحن نتحدث عن المطورين العاديين الذين يريدون فقط تجزئة "جيدة بما فيه الكفاية" لجميع تلك الأنواع حيث يقومون بالأشياء يدويًا اليوم.

كانت تجزئة السلسلة الأصلية عبارة عن تجزئة "جيدة بدرجة كافية" تعمل بشكل جيد للمطورين العاديين. ولكن بعد ذلك تم اكتشاف أن خوادم الويب ASP.NET كانت عرضة لهجمات DoS لأنها تميل إلى تخزين الأشياء المستلمة في علامات التجزئة. لذلك تحولت التجزئة "جيدة بما فيه الكفاية" بشكل أساسي إلى مشكلة أمنية سيئة.

نعتقد أنه جيد بما يكفي لـ tuple

لا بالضرورة. لقد قمنا بإجراء قياس إيقاف خلفي لـ tuples لجعل رمز التجزئة عشوائيًا والذي يمنحنا خيارًا لتعديل الخوارزمية لاحقًا.

     return Hash.Combine(this.A?.GetHashCode() ?? 0,
                         this.B?.GetHashCode() ?? 0,
                         this.C?.GetHashCode() ?? 0);

هذا يبدو معقولا بالنسبة لي.

أنا لا أحصل على البوزيتوين الخاص بك. يبدو أنك تقول شيئين:

كانت تجزئة السلسلة الأصلية عبارة عن تجزئة "جيدة بدرجة كافية" تعمل بشكل جيد للمطورين العاديين. ولكن بعد ذلك تم اكتشاف أن خوادم الويب ASP.NET كانت عرضة لهجمات DoS لأنها تميل إلى تخزين الأشياء المستلمة في علامات التجزئة. لذلك تحولت التجزئة "جيدة بما فيه الكفاية" بشكل أساسي إلى مشكلة أمنية سيئة.

حسنًا ، إذا كان الأمر كذلك ، فلنقدم رمز تجزئة مفيد للأشخاص الذين لديهم مخاوف تتعلق بالأمان / DoS.

يجب أن تكون أنواع الأطر اختيارات بسيطة تعمل بشكل جيد مع 95٪ + من الحالات.

حسنًا ، إذا كان الأمر كذلك ، فلنقدم رمز تجزئة جيد بما يكفي لـ 95٪ من الحالات. يمكن للأشخاص الذين لديهم مخاوف أمنية / DoS استخدام النماذج المتخصصة الموثقة لهذا الغرض.

لا بالضرورة. لقد قمنا بإجراء قياس إيقاف خلفي لـ tuples لجعل رمز التجزئة عشوائيًا والذي يمنحنا خيارًا لتعديل الخوارزمية لاحقًا.

نعم. هل يمكننا الكشف عن ذلك حتى يتمكن المستخدمون من استخدام نفس الآلية.

-
أنا أعاني هنا حقًا لأنه يبدو أننا نقول "لأننا لا نستطيع التوصل إلى حل عالمي ، على كل شخص أن يطرح حلًا خاصًا به". يبدو هذا كواحد من أسوأ الأماكن التي يمكن التواجد فيها. لأن معظم عملائنا بالتأكيد لا يفكرون في طرح "تجزئة مارفن" الخاصة بهم لمخاوف DoS. إنهم يقومون فقط بإضافة أو xoring أو دمج تجزئات الحقل بشكل سيء في تجزئة نهائية واحدة.

إذا كنا نهتم بحالة 95٪ ، فعلينا فقط عمل تجزئة جيدة بشكل عام. إذا كنا نهتم بحالة 5٪ ، فيمكننا توفير حل متخصص لذلك.

هذا يبدو معقولا بالنسبة لي.

عظيم :) هل يمكننا بعد ذلك الكشف عن:

ج #
System.Numerics.Hashing. مساحة الاسم
{
فئة HashHelpers ثابتة داخلية
{
عام ثابت للقراءة فقط int RandomSeed = new Random (). التالي (Int32.MinValue، Int32.MaxValue) ؛

    public static int Combine(int h1, int h2)
    {
        // RyuJIT optimizes this to use the ROL instruction
        // Related GitHub pull request: dotnet/coreclr#1830
        uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
        return ((int)rol5 + h1) ^ h2;
    }
}
Roslyn could then generate:

```c#
     return Hash.Combine(Hash.RandomSeed,
                         this.A?.GetHashCode() ?? 0,
                         this.B?.GetHashCode() ?? 0,
                         this.C?.GetHashCode() ?? 0);

قد يكون لهذا ميزة كونه "جيدًا بدرجة كافية" للغالبية العظمى من الحالات ، بينما يقود الأشخاص أيضًا إلى المسار الصحيح للتهيئة بقيم عشوائية حتى لا يأخذوا التبعيات على التجزئات غير العشوائية.

يمكن للأشخاص الذين لديهم مخاوف أمنية / DoS استخدام النماذج المتخصصة الموثقة لهذا الغرض.

كل تطبيق ASP.NET له مخاوف تتعلق بالأمان / DoS.

عظيم :) هل يمكننا بعد ذلك الكشف عن:

هذا يختلف عما قلته إنه معقول.

ما رأيك في https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.HashCodeCombiner.Sources/HashCodeCombiner.cs . إنه ما يتم استخدامه في ASP.NET داخليًا في عدد من الأماكن اليوم ، وهو ما سأكون سعيدًا جدًا به (باستثناء أن وظيفة الدمج يجب أن تكون أقوى - تفاصيل التنفيذ التي يمكننا الاستمرار في التغيير والتبديل فيها).

jkotas سمعت أن: p

لذا فإن المشكلة هنا هي أن المطورين لا يعرفون متى يكونون عرضة لهجمات DoS ، لأنه ليس شيئًا يهمهم فيه ، ولهذا السبب قمنا بتبديل السلاسل لاستخدام Marvin32.

لا يجب أن نتجه إلى طريق قول "95٪ من الحالات لا تهم" ، لأنه لا يوجد لدينا وسيلة لإثبات ذلك ، ويجب أن نخطئ في جانب الحذر حتى عندما يكون لذلك تكلفة الأداء. إذا كنت ستبتعد عن ذلك ، فإن تنفيذ كود التجزئة يحتاج إلى مراجعة Crypto Board ، وليس فقط أن نقرر "هذا يبدو جيدًا بما فيه الكفاية".

كل تطبيق ASP.NET له مخاوف تتعلق بالأمان / DoS.

نعم. إذن كيف تتعامل مع مشكلة تيه اليوم التي لا يوجد بها أي شخص أي مساعدة في رموز التجزئة ، وبالتالي من المحتمل أن تفعل الأشياء بشكل سيء؟ من الواضح أنه من المقبول أن يكون لديك تلك الحالة من العالم. إذن ما الذي يتضرر من توفير نظام تجزئة معقول يؤدي على الأرجح أداء أفضل مما يتداوله الأشخاص اليوم؟

لأنه ليس لدينا طريقة لإثبات ذلك ، ويجب أن نخطئ في جانب الحذر حتى عندما يكون له تكلفة أداء

إذا لم تقدم شيئًا ، فسيستمر الناس في فعل الأشياء بشكل سيء. إن رفض "الخير الكافي" لأنه لا يوجد شيء مثالي يعني فقط الوضع السيئ الذي نعيشه اليوم.

كل تطبيق ASP.NET له مخاوف تتعلق بالأمان / DoS.

هل يمكنك شرح هذا؟ كما أفهمها ، لديك مخاوف من DoS إذا كنت تقبل إدخالًا تعسفيًا ثم تخزنها في بعض هياكل البيانات التي تعمل بشكل سيئ إذا كان من الممكن صياغة المدخلات بشكل خاص. حسنًا ، لقد فهمت كيف أن هذا مصدر قلق للسلاسل التي يحصل عليها المرء في سيناريوهات الويب التي تأتي من المستخدم.

إذن كيف ينطبق ذلك على الأنواع المتبقية الموجودة التي لا يتم استخدامها في هذا السيناريو؟

لدينا هذه المجموعات من الأنواع:

  1. أنواع المستخدمين التي يجب أن تكون آمنة DoS. في الوقت الحالي ، لا نوفر أي شيء للمساعدة ، لذلك نحن بالفعل في مكان سيئ حيث من المحتمل أن الناس لا يفعلون الشيء الصحيح.
  2. أنواع المستخدمين التي لا تحتاج إلى أن تكون آمنة في DoS. في الوقت الحالي ، لا نوفر أي شيء للمساعدة ، لذلك نحن بالفعل في مكان سيئ حيث من المحتمل أن الناس لا يفعلون الشيء الصحيح.
  3. أنواع الأطر التي يجب أن تكون آمنة DoS. في الوقت الحالي ، جعلناهم من DoS آمنين ، لكننا لا نكشف عن ذلك من خلال واجهات برمجة التطبيقات.
  4. نماذج الإطار التي لا يجب أن تكون آمنة. لقد قدمنا ​​لهم الآن تجزئات ، لكن من خلال واجهات برمجة التطبيقات لا نكشف.

في الأساس ، نعتقد أن هذه الحالات مهمة ، ولكنها ليست مهمة بما يكفي لتوفير حل فعلي للمستخدمين للتعامل مع "1" أو "2". نظرًا لأننا قلقون من أن حل الرقم "2" لن يكون جيدًا بالنسبة إلى الرقم "1" ، فلن نقدمه في المقام الأول. وإذا لم نكن مستعدين حتى لتقديم حل لـ "1" ، فيبدو أننا في وضع غريب بشكل لا يصدق. نحن قلقون بشأن DoSing و ASP ، لكننا لسنا قلقين من مساعدة الناس فعليًا. ولأننا لن نساعد الناس في ذلك ، فنحن لسنا مستعدين حتى للمساعدة في الحالات غير المتعلقة بوزارة الخارجية.

-

إذا كانت هاتان الحالتان مهمتان (وأنا على استعداد لقبولهما) فلماذا لا نقدم فقط واجهتي برمجة تطبيقات؟ وثقهم. وضح لهم الغرض من وراء ذلك. إذا استخدمها الناس بشكل صحيح ، عظيم . إذا لم يستخدمها الناس بشكل صحيح فلا يزال ذلك جيدًا. بعد كل شيء ، من المحتمل أنهم لا يقومون بالأشياء بشكل صحيح اليوم على أي حال ، فكيف تكون الأمور أسوأ؟

ماذا تعتقد عن

ليس لدي رأي بطريقة أو بأخرى. إذا كانت واجهة برمجة تطبيقات يمكن للعملاء استخدامها والتي تعمل بشكل مقبول وتوفر واجهة برمجة تطبيقات بسيطة مع رمز واضح من نهايتها ، فأعتقد أن هذا جيد.

أعتقد أنه سيكون من الجيد أن يكون لديك نموذج ثابت بسيط يتعامل مع حالة 99٪ من الرغبة في دمج مجموعة من الحقول / الخصائص بطريقة مرتبة. يبدو أنه يمكن إضافة شيء من هذا القبيل إلى هذا النوع بكل بساطة.

أعتقد أنه سيكون من الجيد الحصول على شكل ثابت بسيط

يوافق على.

أعتقد أنه سيكون من الجيد أن يكون لديك نموذج ثابت بسيط يتعامل مع حالة 99٪ من الرغبة في دمج مجموعة من الحقول / الخصائص بطريقة مرتبة. يبدو أنه يمكن إضافة شيء من هذا القبيل إلى هذا النوع بكل بساطة.

يوافق على.

أنا على استعداد لمقابلتكما في منتصف الطريق في هذا لأنني أريد حقًا أن أرى نوعًا من واجهة برمجة التطبيقات. jkotas ما زلت لا أفهم أنك تعارض إضافة واجهة برمجة تطبيقات قائمة على المثيلات غير قابلة للتغيير ؛ قلت أولاً أن السبب هو أن النسخ ذات 32 بت ستكون بطيئة ، ثم لأن واجهة برمجة التطبيقات القابلة للتغيير ستكون أكثر إيجازًا (وهذا ليس صحيحًا ؛ h.Combine(a).Combine(b) (نسخة غير قابلة للتغيير) أقصر من h.Combine(a); h.Combine(b); (قابل للتغيير إصدار)).

بعد قولي هذا ، أنا على استعداد للعودة إلى:

public static class HashCode
{
    public static int Combine<T>(T value1, Tvalue2);
    public static int Combine<T>(T value1, Tvalue2, IEqualityComparer<T> comparer);
    public static int Combine<T>(T value1, Tvalue2, T value3);
    public static int Combine<T>(T value1, Tvalue2, T value3, IEqualityComparer<T> comparer);
    public static int Combine<T>(T value1, Tvalue2, T value3, T value4);
    public static int Combine<T>(T value1, Tvalue2, T value3, T value4, IEqualityComparer<T> comparer);
    // ... All the way until value8
}

هل هذا يبدو معقولا؟

لا يمكنني تعديل رسالتي الآن ، لكنني أدركت للتو أنه لا يمكن لجميع الطرق قبول T. في هذه الحالة ، يمكننا فقط الحصول على 8 أحمال زائدة تقبل جميع ints وإجبار المستخدم على الاتصال بـ GetHashCode.

إذا كانت هاتان الحالتان مهمتان (وأنا على استعداد لقبولهما) فلماذا لا نقدم فقط واجهتي برمجة تطبيقات؟ وثقهم. وضح لهم الغرض من وراء ذلك. إذا استخدمها الناس بشكل صحيح ، عظيم. إذا لم يستخدمها الناس بشكل صحيح فلا يزال ذلك جيدًا. بعد كل شيء ، من المحتمل أنهم لا يقومون بالأشياء بشكل صحيح اليوم على أي حال ، فكيف تكون الأمور أسوأ؟

لأن الناس لا يستخدمون الأشياء بشكل صحيح عندما يكونون هناك. لنأخذ مثالًا بسيطًا ، XSS. منذ البداية ، حتى نماذج الويب لديها القدرة على ترميز المخرجات بتنسيق HTML. على الرغم من أن المطورين لم يعرفوا المخاطر ، ولم يعرفوا كيفية القيام بذلك بشكل صحيح ، ولم يكتشفوا إلا بعد فوات الأوان ، تم نشر تطبيقهم ، وعفوًا ، تم الآن رفع ملف تعريف الارتباط الخاص بهم.

إعطاء الناس خيار الأمان يفترض أنهم

  1. تعرف على المشكلة.
  2. افهم ما هي المخاطر.
  3. يمكن تقييم تلك المخاطر.
  4. يمكن بسهولة اكتشاف الشيء الصحيح الذي يجب القيام به.

لا تنطبق هذه الافتراضات عمومًا على غالبية المطورين ، فهم لا يكتشفون المشكلة إلا بعد فوات الأوان. لا يذهب المطورون إلى المؤتمرات الأمنية ولا يقرأون الأوراق البيضاء ولا يفهمون الحلول. لذلك في سيناريو ASP.NET HashDoS ، قمنا بالاختيار لهم ، وقمنا بحمايتهم افتراضيًا ، لأن هذا كان الشيء الصحيح الذي يجب القيام به ، وكان له التأثير الأكبر. ومع ذلك ، قمنا بتطبيقه فقط على السلاسل ، وهذا ترك الأشخاص الذين كانوا ينشئون فئات مخصصة من مدخلات المستخدم في مكان سيء. يجب أن نفعل الشيء الصحيح ، ونساعد في حماية هؤلاء العملاء الآن ، وجعله الافتراضي ، مع وجود حفرة من النجاح ، وليس الفشل. أحيانًا لا يتعلق تصميم واجهة برمجة التطبيقات للأمان بالاختيار ، بل يتعلق بمساعدة المستخدم سواء كان يعرف ذلك أم لا.

يمكن للمستخدم دائمًا إنشاء تجزئة تركز على عدم الأمان ؛ لذلك بالنظر إلى الخيارين

  1. أداة التجزئة الافتراضية غير مدركة للأمان ؛ يمكن للمستخدم إنشاء وظيفة تجزئة مدركة للأمان
  2. أداة التجزئة الافتراضية تدرك الأمان ؛ يمكن للمستخدم إنشاء وظيفة تجزئة مخصصة غير متعلقة بالأمان

ثم الثاني ربما يكون أفضل ؛ وما هو مقترح لن يكون له تأثير الأداء الكامل على تجزئة التشفير ؛ لذلك يقدم حل وسط جيد؟

كان أحد الأسئلة الجارية في هذه المواضيع هو أي الخوارزمية مثالية للجميع. أعتقد أنه من الآمن القول أنه لا توجد خوارزمية واحدة مثالية. ومع ذلك ، لا أعتقد أن هذا يجب أن يمنعنا من تقديم شيء أفضل من الكود مثل ما أظهرهCyrusNajmabadi ، والذي يميل إلى ضعف إنتروبيا لمدخلات .NET الشائعة بالإضافة إلى أخطاء التجزئة الشائعة الأخرى (مثل فقدان بيانات الإدخال أو سهولة

أود اقتراح خيارين للتغلب على مشكلة "أفضل خوارزمية":

  1. الخيارات الصريحة: أخطط لإرسال اقتراح API قريبًا لمجموعة من التجزئات غير المشفرة (ربما xxHash و Marvin32 و SpookyHash على سبيل المثال). واجهة برمجة التطبيقات هذه لها استخدام مختلف قليلاً عن نوع HashCode أو HashCodeHelper ، ولكن من أجل المناقشة ، نفترض أنه يمكننا حل هذه الاختلافات. إذا استخدمنا واجهة برمجة التطبيقات هذه لـ GetHashCode:

    • الكود الذي تم إنشاؤه واضح حول ما يفعله - إذا قام Roslyn بإنشاء Marvin32.Create(); ، فإنه يتيح للمستخدمين المحترفين معرفة ما قرروا القيام به ويمكنهم بسهولة تغييره إلى خوارزمية أخرى في المجموعة إذا رغبوا في ذلك.

    • هذا يعني أنه لا داعي للقلق بشأن تغيير التغييرات. إذا بدأنا بخوارزمية غير عشوائية / سيئة الإنتروبيا / بطيئة ، فيمكننا ببساطة تحديث Roslyn لبدء إنشاء شيء آخر في كود جديد. سيستمر الرمز القديم في استخدام التجزئة القديمة وسيستخدم الرمز الجديد التجزئة الجديدة. يمكن للمطورين (أو إصلاح رمز Roslyn) تغيير الكود القديم إذا أرادوا ذلك.

    • أكبر جانب سلبي يمكنني التفكير فيه هو أن بعض التحسينات التي قد نريدها لـ GetHashCode قد تكون ضارة للخوارزميات الأخرى. على سبيل المثال ، بينما تعمل الحالة الداخلية 32 بت بشكل جيد مع الهياكل الثابتة ، فإن الحالة الداخلية 256 بت في (على سبيل المثال) CityHash قد تضيع الكثير من الوقت في النسخ.

  1. التوزيع العشوائي: ابدأ بخوارزمية عشوائية بشكل صحيح (الرمز CyrusNajmabadi الذي يظهر بقيمة أولية عشوائية لا يحسب لأنه من المحتمل أن يغسل العشوائية). هذا يضمن أنه يمكننا تغيير التنفيذ بدون مشاكل التوافق. سنظل بحاجة إلى أن نكون حساسين للغاية بشأن تغييرات الأداء إذا قمنا بتغيير الخوارزمية. ومع ذلك ، سيكون هذا أيضًا جانبًا إيجابيًا محتملًا حيث يمكننا اتخاذ خيارات لكل بنية (أو حتى لكل جهاز). على سبيل المثال ، يوضح هذا الموقع أن xxHash هو الأسرع على x64 Mac بينما SpookyHash هو الأسرع على Xbox و iPhone. إذا سلكنا هذا الطريق بقصد تغيير الخوارزميات في مرحلة ما ، فقد نحتاج إلى التفكير في تصميم واجهة برمجة تطبيقات لا تزال تتمتع بأداء معقول إذا كانت هناك حالة داخلية 64+ بت.

CCbartonjs ، terrajobst

morganbr لا توجد خوارزمية واحدة مثالية ، لكني أعتقد أن وجود بعض الخوارزمية ، التي تعمل بشكل جيد إلى حد ما معظم الوقت ، معروضة باستخدام واجهة برمجة تطبيقات بسيطة وسهلة الفهم هو الشيء الأكثر فائدة الذي يمكن القيام به. وجود مجموعة من الخوارزميات بالإضافة إلى ذلك ، للاستخدامات المتقدمة أمر جيد. لكن لا ينبغي أن يكون هذا هو الخيار الوحيد ، فلا يجب أن أعرف من هو مارفن فقط حتى أتمكن من وضع أغراضي في Dictionary .

لا يجب أن أتعلم من هو مارفن فقط حتى أتمكن من وضع أغراضي في قاموس.

أنا أحب الطريقة التي وضعت بها ذلك. يعجبني أيضًا أنك ذكرت القاموس نفسه. IDictionary هو شيء يمكن أن يحتوي على الكثير من الضمانات المختلفة مع جميع أنواع الصفات المختلفة (انظر مجموعات واجهات برمجة التطبيقات في العديد من الأنظمة الأساسية). ومع ذلك ، ما زلنا نقدم فقط "قاموسًا" أساسيًا يقوم بعمل لائق بشكل عام ، على الرغم من أنه قد لا يتفوق في كل فئة.

أعتقد أن هذا ما يبحثون طن من الناس لفي مكتبة التجزئة. شيء ينجز المهمة ، حتى لو لم يكن مثاليًا لجميع الأغراض.

morganbr أعتقد أن الناس ببساطة يريدون طريقة لكتابة GetHashCode أفضل مما يفعلونه اليوم (عادةً ما تكون مجموعة من عمليات الرياضيات التي قاموا بنسخها من شيء ما على الويب). إذا كان بإمكانك فقط تقديم إشارة أساسية لذلك الأحرف الرونية بشكل جيد ، فسيكون الناس سعداء. يمكنك بعد ذلك الحصول على واجهة برمجة تطبيقات تعمل من وراء الكواليس للمستخدمين المتقدمين إذا كانت لديهم حاجة قوية لوظائف تجزئة محددة .

بعبارة أخرى ، لن يعرف الأشخاص الذين يكتبون أكواد التجزئة اليوم أو يهتمون لماذا يريدون Spooky vs Marvin vs Murmur. فقط الشخص الذي لديه حاجة معينة لواحد من أكواد التجزئة المحددة سيبحث. ولكن الكثير من الناس يحتاجون إلى القول "هذه هي حالة غرضي ، زودني بطريقة لإنتاج تجزئة موزعة جيدًا وسريعة يمكنني استخدامها بعد ذلك مع القواميس ، والتي أعتقد أنها تمنعني من أن أكون مغرورًا إذا حدث ذلك لأخذ مدخلات غير موثوقة وتجزئتها وتخزينها ".

CyrusNajmabadi المشكلة هي أنه إذا قمنا بتوسيع مفاهيمنا الحالية عن التوافق في المستقبل ، نجد أنه بمجرد

مرة واحدة يمكن أن يجادل أنه إذا بدأ بطريقة عشوائية مستقرة ، فإنه يصبح من السهل تغيير التنفيذ ، حيث لا يمكنك الاعتماد على القيمة من التشغيل إلى التشغيل على أي حال. ولكن إذا وجدنا بعد عامين أن هناك خوارزمية توفر توازنًا جيدًا إن لم يكن أفضل لمجموعات التجزئة مع أداء أفضل في الحالة العامة ، ولكنها تصنع بنية تتضمن قائمة \

وفقًا لاقتراح مورغان ، فإن الكود الذي تكتبه اليوم سيكون له نفس خصائص الأداء بشكل فعال إلى الأبد. بالنسبة للتطبيقات التي كان من الممكن أن تتحسن ، فهذا أمر مؤسف. بالنسبة للتطبيقات التي كانت ستزداد سوءًا ، فهذا أمر رائع. ولكن عندما نعثر على الخوارزمية الجديدة ، فإننا نتحقق منها ، ونغير Roslyn (ونقترح تغييرًا على ReSharper / إلخ) لبدء إنشاء أشياء باستخدام NewAwesomeThing2019 بدلاً من SomeThingThatWasConsoredAwesomeIn2018.

أي شيء مثل هذا الصندوق الأسود الفائق لا يمكن القيام به إلا مرة واحدة. ومن ثم نحن عالقون معها إلى الأبد. ثم يكتب شخص ما التالي ، الذي يتمتع بأداء متوسط ​​أفضل ، لذلك هناك نوعان من تطبيقات الصندوق الأسود لا تعرف سبب اختيارك بينهما. وبعد ذلك ... وبعد ذلك ....

لذلك ، بالتأكيد ، قد لا تعرف سبب قيام Roslyn / ReSharper / etc بكتابة GetHashCode تلقائيًا لك باستخدام Marvin32 ، أو Murmur ، أو FastHash ، أو مجموعة / شرطية على أساس IntPtr.Size. لكن لديك القدرة على النظر في الأمر. ولديك القدرة على تغييرها على الأنواع الخاصة بك لاحقًا ، حيث يتم الكشف عن معلومات جديدة ... ولكننا قدمنا ​​لك أيضًا القدرة على الاحتفاظ بها كما هي. (سيكون من المحزن أن نكتب هذا ، وفي غضون 3 سنوات تتجنب Roslyn / ReSharper / إلخ صراحة تسميتها ، لأن الخوارزمية الجديدة أفضل كثيرًا ... عادةً).

bartonjs ما الذي يجعل التجزئة مختلفة عن جميع الأماكن حيث يوفر لك Net مع خوارزمية الصندوق الأسود أو بنية البيانات؟ على سبيل المثال ، الفرز (introsort) ، Dictionary (تسلسل منفصل قائم على المصفوفة) ، StringBuilder (قائمة مرتبطة من 8k أجزاء) ، معظم LINQ.

لقد ألقينا نظرة أعمق على هذا اليوم. نعتذر عن التأخير والتأخير في هذه القضية.

متطلبات

  • لمن هو API؟

    • لا تحتاج واجهة برمجة التطبيقات إلى إنتاج تجزئة تشفير قوية

    • لكن: يجب أن تكون واجهة برمجة التطبيقات

    • ومع ذلك ، هذا لا يعني أنه يتعين علينا استخدام API في كل مكان. لا بأس إذا كانت هناك أجزاء من FX حيث نريد استخدام جزء مخصص إما لمخاطر الأمان / DOS أو بسبب الأداء. ستكون الاستثناءات موجودة دائمًا .

  • ما هي الخصائص المطلوبة لهذه التجزئة؟

    • يتم استخدام جميع وحدات البت في الإدخال

    • النتيجة موزعة بشكل جيد

    • ستوفر واجهة برمجة التطبيقات كود تجزئة "a" ، ولكنها لا تضمن خوارزمية كود تجزئة معينة. يتيح لنا ذلك استخدام خوارزمية مختلفة لاحقًا أو استخدام خوارزميات مختلفة في بنيات مختلفة.

    • ستضمن واجهة برمجة التطبيقات (API) أنه خلال عملية معينة ، ستنتج نفس القيم نفس رمز التجزئة. من المحتمل أن ينتج عن مثيلات مختلفة لنفس التطبيق رموز تجزئة مختلفة بسبب التوزيع العشوائي. يتيح لنا ذلك ضمان عدم تمكن المستهلكين من الاستمرار في قيم التجزئة والاعتماد بشكل عرضي على استقرارها عبر عمليات التشغيل (أو الأسوأ من ذلك ، إصدارات النظام الأساسي).

شكل API

ج #
// سيعيش في التجميع الأساسي
// .NET Framework: mscorlib
// NET Core: System.Runtime / System.Private.CoreLib
نظام مساحة الاسم
{
HashCode الهيكل العام
{
الجمع العام الثابت int(T1 value1) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2) ؛
الجمع بين كثافة العمليات العامة العامة(قيمة T1 ، قيمة T2 ، قيمة T3) ؛
الجمع العام الثابت int(T1 value1، T2 value2، T3 value3، T4 value4) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5) ؛
الجمع العام الثابت int(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6) ؛
الجمع العام الثابت int(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6، T7 value7) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6، T7 value7، T8 value8) ؛

    public void Add<T>(T value);
    public void Add<T>(T value, IEqualityComparer<T> comparer);
    public void Add<T>(T[] value);
    public void Add<T>(T[] value, int index, int length);
    public void Add(byte[] value);
    public void Add(byte[] value, int index, int length);
    public void Add(string value);
    public void Add(string value, StringComparison comparisonType);

    public int ToHashCode();
}

}

Notes:

* We decided to not override `GetHashCode()` to produce the hash code as this would be weird, both naming-wise as well as from a behavioral standpoint (`GetHashCode()` should return the object's hash code, not the one being computed).
* We decided to use `Add` for the builder patter and `Combine` for the static construction
* We decided to use not provide a static initialization method. Instead, `Add` will do this on first use.
* The struct is mutable, which is unfortunate but we feel the best compromise between making `GetHashCode()` very cheap & not cause any allocations while allowing the structure to be bigger than 32-bit so that the hash code algorithm can use more bits during accumulation.
* `Combine` will just call `<value>.GetHashCode()`, so it has the behavior of the value's type `GetHashCode()` implementation
    - For strings that means different casing will produce different hash codes
    - For arrays, that means the hash code doesn't look at the contents but uses reference semantics for the hash code
    - If that behavior is undesired, the developer needs to use the builder-style approach

### Usage

The simple case is when someone just wants to produce a good hash code for a given type, like so:

```C#
public class Customer
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override int GetHashCode() => HashCode.Combine(Id, FirstName, LastName);
}

الحالة الأكثر تعقيدًا هي عندما يحتاج المطور إلى تعديل كيفية حساب التجزئة. الفكرة هي أن موقع الاستدعاء يمرر التجزئة المطلوبة بدلاً من الكائن / القيمة ، مثل:

ج #
زبون من فئة جزئية عامة
{
تجاوز العامة int GetHashCode () =>
HashCode.Combine (
هوية شخصية،
StringComparer.OrdinalIgnoreCase.GetHashCode (الاسم الأول) ،
StringComparer.OrdinalIgnoreCase.GetHashCode (اسم العائلة) ،
) ؛
}

And lastly, if the developer needs more flexibility, such as producing a hash code for more than eight values, we also provide a builder-style approach:

```C#
public partial class Customer
{
    public override int GetHashCode()
    {
        var hashCode = new HashCode();
        hashCode.Add(Id);
        hashCode.Add(FirstName, StringComparison.OrdinalIgnoreCase);
        hashCode.Add(LastName, StringComparison.OrdinalIgnoreCase);
        return hashCode.ToHashCode();
    }
}

الخطوات التالية

ستبقى هذه القضية في متناول اليد. من أجل تنفيذ API ، نحتاج إلى تحديد الخوارزمية التي يجب استخدامها.

morganbr سيقدم اقتراحًا لمرشحين جيدين. بشكل عام ، لا نريد كتابة خوارزمية تجزئة من الصفر - نريد استخدام خوارزمية معروفة جيدًا خصائصها مفهومة جيدًا.

ومع ذلك ، يجب علينا قياس التنفيذ لأحمال عمل .NET النموذجية ومعرفة الخوارزمية التي تحقق نتائج جيدة (الإنتاجية والتوزيع). من المحتمل أن تختلف الإجابات باختلاف بنية وحدة المعالجة المركزية ، لذلك يجب أن نأخذ ذلك في الاعتبار عند القياس.

jamesqo ، هل ما زلت مهتمًا بالعمل في هذا المجال؟ في هذه الحالة ، يرجى تحديث الاقتراح وفقًا لذلك.

terrajobst ، قد نرغب أيضًا في public static int Combine<T1>(T1 value); . أعلم أنه يبدو مضحكا بعض الشيء ، لكنه سيوفر طريقة لنشر البتات من شيء ذي مساحة تجزئة إدخال محدودة. على سبيل المثال ، تحتوي العديد من عمليات التعداد على عدد قليل من علامات التجزئة الممكنة ، فقط باستخدام الأجزاء القليلة السفلية من الكود. بعض المجموعات مبنية على افتراض أن التجزئة موزعة على مساحة أكبر ، لذا فإن نشر البتات قد يساعد المجموعة على العمل بكفاءة أكبر.

public void Add(string value, StrinComparison comparison);

Nit: يجب تسمية المعلمة StringComparison comparisonType لمطابقة التسمية المستخدمة في أي مكان آخر يتم استخدام StringComparison كمعامل.

المعايير التي ستساعدنا في اختيار الخوارزميات ستكون:

  1. هل الخوارزمية لها تأثير انهيار جليدي جيد؟ أي ، هل كل جزء من المدخلات لديه فرصة بنسبة 50٪ لتقليب كل جزء من المخرجات؟ يحتوي هذا الموقع على دراسة للعديد من الخوارزميات الشائعة.
  2. هل الخوارزمية سريعة للمدخلات الصغيرة؟ نظرًا لأن HashCode.Combine سيتعامل بشكل عام مع 8 ints أو أقل ، فقد يكون وقت بدء التشغيل أكثر أهمية من الإنتاجية. يحتوي هذا الموقع على مجموعة مثيرة للاهتمام من البيانات لتبدأ بها. هذا أيضًا هو المكان الذي قد نحتاج فيه إلى إجابات مختلفة لبنيات مختلفة أو محاور أخرى (OS ، AoT مقابل JIT ، إلخ).

ما نود رؤيته حقًا هو أرقام أداء المرشحين المكتوبة بلغة C # حتى نكون واثقين بشكل معقول من أن خصائصهم ستصمد أمام .NET. إذا كتبت مرشحًا ولم نختاره لهذا الغرض ، فسيظل هذا عملاً مفيدًا عندما أحصل بالفعل على اقتراح واجهة برمجة التطبيقات معًا لواجهة برمجة تطبيقات التجزئة غير المشفرة.

في ما يلي بعض المرشحين الذين أعتقد أنهم يستحقون التقييم (لكن لا تتردد في اقتراح آخرين):

  • Marvin32 (لدينا بالفعل تطبيق C # هنا ). نحن نعلم أنه سريع بما يكفي لـ String.GetHashCode ونعتقد أنه مقاوم لـ HashDoS
  • xxHash32 (الأسرع في الخوارزمية على x86 هنا والتي تتمتع بأعلى جودة وفقًا لـ SMHasher)
  • FarmHash (الأسرع في x64 هنا . لم أجد مؤشرًا جيدًا للجودة. قد يكون من الصعب كتابة هذا المؤشر في C # رغم ذلك)
  • xxHash64 (مقطوع إلى 32 بت) (هذا ليس فائزًا واضحًا للسرعة ، ولكن قد يكون من السهل القيام به إذا كان لدينا بالفعل xxHash32)
  • SpookyHash (يميل إلى الأداء الجيد في مجموعات البيانات الأكبر)

عار على الأساليب Add لا يمكن أن يكون لها نوع إرجاع ref HashCode وإرجاع ref this حتى يمكن استخدامها بطلاقة ،

هل ستسمح عمليات إرجاع readonly ref بهذا؟ تضمين التغريدة

تحذير: إذا اختار أي شخص تنفيذ التجزئة من قاعدة التعليمات البرمجية الموجودة في مكان ما على الإنترنت ، فيرجى الاحتفاظ بالرابط إلى المصدر والتحقق من الترخيص (سيتعين علينا القيام بذلك أيضًا).

إذا كان الترخيص غير متوافق ، فقد نحتاج إلى كتابة الخوارزمية من البداية.

IMO ، استخدام طرق الإضافة يجب أن يكون غير شائع للغاية. سيكون لسيناريوهات متقدمة للغاية ، ولن تكون هناك حاجة إلى أن تكون "بطلاقة".

بالنسبة لحالات الاستخدام الشائعة لـ 99٪ من جميع حالات رمز المستخدم ، يجب أن يكون المرء قادرًا على استخدام => HashCode.Combine(...) ويكون على ما يرام.

تضمين التغريدة

قد نريد أيضًا public static int Combine<T1>(T1 value); . أعلم أنه يبدو مضحكا بعض الشيء ، لكنه سيوفر طريقة لنشر البتات من شيء ذي مساحة تجزئة إدخال محدودة

منطقي. لقد أضفته.

تضمين التغريدة

Nit: يجب تسمية المعلمة StringComparison comparisonType لمطابقة التسمية المستخدمة في أي مكان آخر يتم استخدام StringComparison كمعامل.

مثبت.

تضمين التغريدة

IMO ، استخدام طرق Add يجب أن يكون غير شائع للغاية. سيكون لسيناريوهات متقدمة للغاية ، ولن تكون هناك حاجة إلى أن تكون "بطلاقة".

متفق.

benaadams - re: ref بإرجاع this من Add - لا ، this لا يمكن إرجاعها بواسطة ref في الطريقة Struct حيث يمكن أن تكون rValue أو temp.

ج #
المرجع var r = (new T ()).

// r يشير إلى بعض المتغيرات هنا. أي واحد؟ ما هو النطاق / العمر؟
r = SomethingElse () ؛
""

في حال كانت مفيدة لأغراض المقارنة ، قمت منذ بضع سنوات بنقل وظيفة تجزئة مصدر C ) إلى C # هنا .

أتساءل عن المجموعات:

تضمين التغريدة

c# public void Add<T>(T[] value);

لماذا يوجد حمل زائد للمصفوفات ، لكن ليس هناك حمل زائد للمجموعات العامة (على سبيل المثال ، IEnumerable<T>

أيضًا ، أليس من المربك أن يتصرف HashCode.Combine(array) و hashCode.Add((object)array) بطريقة واحدة (استخدم المساواة المرجعية) وأن hashCode.Add(array) يتصرف بطريقة أخرى (يجمع رموز التجزئة للقيم في المصفوفة)؟

تضمين التغريدة

بالنسبة لحالات الاستخدام الشائعة لـ 99٪ من جميع حالات رمز المستخدم ، يجب أن يكون المرء قادرًا فقط على استخدام => HashCode.Combine(...) ويكون على ما يرام.

إذا كان الهدف حقًا هو أن تكون قادرًا على استخدام Combine في 99٪ من حالات الاستخدام (وليس 80٪ على سبيل المثال) ، فلا يجب أن يدعم Combine بطريقة أو بأخرى مجموعات التجزئة بناءً على القيم في المجموعة؟ ربما يجب أن تكون هناك طريقة منفصلة تقوم بذلك (إما طريقة تمديد أو طريقة ثابتة على HashCode

إذا كان Add سيناريو power ، فهل يجب أن نفترض أن المستخدم يجب أن يختار بين Object.GetHashCode والجمع بين العناصر الفردية للمجموعات؟ إذا كان سيساعد ، فيمكننا التفكير في إعادة تسمية المصفوفة (وإصدارات IEnumerable المحتملة). شيء مثل:
c# public void AddEnumerableHashes<T>(IEnumerable<T> enumerable); public void AddEnumerableHashes<T>(T[] array); public void AddEnumerableHashes<T>(T[] array, int index, int length);
أتساءل عما إذا كنا سنحتاج أيضًا إلى زيادة التحميل مع IEqualityComparers.

الاقتراح: اجعل منشئ البناء يطبق IEnumerable لدعم بناء جملة مُهيئ المجموعة:

C# return new HashCode { SomeField, OtherField, { SomeString, StringComparer.UTF8 }, { SomeHashSet, HashSet<int>.CreateSetComparer() } }.GetHashCode()

هذا أكثر أناقة من استدعاء Add() يدويًا (على وجه الخصوص ، لا تحتاج إلى متغير مؤقت) ، ولا يزال لديك أي تخصيصات.

المزيد من التفاصيل

SLaks ربما ينتظر بناء الجملة https://github.com/dotnet/csharplang/issues/455 (على افتراض أن هذا الاقتراح يحظى بالدعم) ، حتى لا يضطر HashCode إلى تنفيذ IEnumerable الوهمي

قررنا عدم تجاوز GetHashCode () لإنتاج كود التجزئة لأن هذا سيكون غريبًا ، سواء من حيث التسمية أو من وجهة نظر سلوكية (يجب أن يُرجع GetHashCode () رمز تجزئة الكائن ، وليس الرمز الذي يتم حسابه).

أجد أنه من الغريب أن GetHashCode لن يقوم بإرجاع شفرة التجزئة المحسوبة. أعتقد أن هذا سوف يربك المطورين. على سبيل المثال ، استخدمهSLaks بالفعل في اقتراحه بدلاً من استخدام ToHashCode .

justinvp إذا لم GetHashCode() بإرجاع رمز التجزئة المحسوب ، فمن المحتمل أن يتم وضع علامة عليه [Obsolete] و [EditorBrowsable(Never)] .

من ناحية أخرى ، لا أرى الضرر في إعادة رمز التجزئة المحسوب.

تضمين التغريدة

قررنا عدم تجاوز GetHashCode() لإنتاج كود التجزئة لأن هذا سيكون غريبًا ، سواء من حيث التسمية أو من وجهة نظر سلوكية (يجب أن يُرجع GetHashCode() كود تجزئة الكائن ، وليس الرمز يتم حسابها).

نعم ، يجب أن يعرض GetHashCode() كود تجزئة الكائن ، ولكن هل هناك أي سبب يجعل رمزي التجزئة مختلفين؟ لا يزال هذا صحيحًا ، نظرًا لأن مثيلين من HashCode مع نفس الحالة الداخلية سيعودان نفس القيمة من GetHashCode() .

terrajobst لقد رأيت للتو تعليقك. سامحني على الرد المتأخر ، لقد كنت بطيئًا في النظر في الإشعار لأنني اعتقدت أنه سيكون أكثر من ذهاب وإياب لن يذهب إلى أي مكان. يسعدني أن أرى أن الأمر ليس كذلك! : تادا:

يسعدني اختيار هذا والقيام بقياس الإنتاجية / التوزيع (أفترض أن هذا ما قصدته بعبارة "مهتم بالعمل في هذا المجال"). مع ذلك ، أعطني ثانية لإنهاء قراءة جميع التعليقات هنا.

تضمين التغريدة

هل يمكننا التغيير

public void Add<T>(T[] value);
public void Add<T>(T[] value, int index, int length);
public void Add(byte[] value);
public void Add(byte[] value, int index, int length);

إلى

public void AddRange<T>(T[] values);
public void AddRange<T>(T[] values, int index, int count);
public void AddRange<T>(T[] values, int index, int count, IEqualityComparer<T> comparer);

؟ لقد أعدت تسمية Add -> AddRange لتجنب السلوك svick المذكور. لقد قمت بإزالة التحميلات الزائدة byte حيث يمكننا التخصص باستخدام typeof(T) == typeof(byte) داخل الطريقة إذا احتجنا إلى القيام بأي شيء خاص بالبايت. أيضًا ، قمت بتغيير value -> values و length -> count . من المنطقي أيضًا أن يكون لديك حمل زائد للمقارنة.

terrajobst هل يمكنك أن تذكرني لماذا

        public void Add(string value);
        public void Add(string value, StringComparison comparisonType);

ضروري عندما يكون لدينا

        public void Add<T>(T value);
        public void Add<T>(T value, IEqualityComparer<T> comparer);

؟

svick

justinvp إذا كان

: +1:

terrajobst هل يمكننا العودة إلى تحويل ضمني من HashCode -> int ، لذا لا توجد طريقة ToHashCode ؟ تحرير: ToHashCode جيد. انظر ردCyrusNajmabadi أدناه.

jamesqo StringComparison تعداد.
ومع ذلك ، يمكن للأشخاص استخدام ما يعادل StringComparer بدلاً من ذلك.

هل يمكننا العودة إلى تحويل ضمني من HashCode -> int ، لذا لا توجد طريقة ToHashCode؟

ناقشنا هذا وقررنا رفضه في الاجتماع. تكمن المشكلة في أنه عندما يحصل المستخدم على "int" النهائي ، يتم تنفيذ العمل الإضافي غالبًا. على سبيل المثال ، غالبًا ما تقوم الأجزاء الداخلية لشفرة التجزئة بخطوة إنهاء ، وقد تعيد تعيين نفسها إلى حالة جديدة. إن حدوث ذلك مع التحويل الضمني سيكون غريبًا. إذا فعلت هذا:

HashCode hc = ...

int i1 = hc;
int i2 = hc;

ثم يمكنك الحصول على نتائج مختلفة.

لهذا السبب ، نحن أيضًا لا نحب التحويل الصريح (حيث لا يفكر الناس في التحويلات على أنها تغيير للحالة الداخلية).

باستخدام طريقة يمكننا أن نوثق صراحة أن هذا يحدث. يمكننا حتى تسميتها لنقلها بنفس القدر. أي "ToHashCodeAndReset" (على الرغم من أننا قررنا ضد ذلك). ولكن على الأقل يمكن أن تحتوي الطريقة على وثائق واضحة يمكن لمستخدم hte رؤيتها في أشياء مثل intellisense. هذا ليس هو الحال حقًا مع التحويلات.

لقد قمت بإزالة الأحمال الزائدة للبايت حيث يمكننا التخصص باستخدام typeof (T) == typeof (بايت)

IIRC كان هناك بعض القلق حول هذا ليس على ما يرام من منظور JIT. ولكن قد يكون هذا فقط لحالات "typeof ()" غير ذات القيمة. طالما أن jit ستفعل الشيء الصحيح بفاعلية لحالات نوع القيمة () ، فيجب أن يكون ذلك جيدًا.

CyrusNajmabadi لم أكن int قد يتضمن تغيير الحالة. إذن ، ToHashCode .

لأولئك الذين يفكرون في منظور التشفير - http://tuprints.ulb.tu-darmstadt.de/2094/1/thesis.lehmann.pdf

terrajobst ، هل كان لديك الوقت لقراءة تعليقاتي (بدءًا من هنا ) وتحديد ما إذا كنت توافق على شكل واجهة برمجة التطبيقات المعدلة؟ إذا كان الأمر كذلك ، فأعتقد أنه يمكن وضع علامة على الموافقة / الموافقة عليها من قبل api ويمكننا البدء في اتخاذ قرار بشأن خوارزمية التجزئة.

blowdart ، أي جزء معين تريد تسليط الضوء عليه؟

ربما لم أكن صريحًا جدًا بشأن ذلك أعلاه ، لكن التجزئات غير المشفرة الوحيدة التي لا أعرفها عن فواصل HashDoS هي Marvin و SipHash. وهذا يعني أنه حتى البذر (على سبيل المثال) لا يزال من الممكن كسر البذرة ذات القيمة العشوائية واستخدامها في DoS.

لا شيء ، لقد وجدته مثيرًا للاهتمام ، وأعتقد أن المستندات الخاصة بهذا يجب أن تقول "ليس للاستخدام في أكواد التجزئة التي يتم إنشاؤها عبر خوارزميات التشفير."

قرارات

  • يجب علينا إزالة جميع طرق AddRange لأن السيناريو غير واضح. من غير المرجح إلى حد ما أن تظهر Array's كثيرًا. وبمجرد تضمين المصفوفات الكبيرة ، فإن السؤال المطروح هو ما إذا كان يجب تخزين الحساب مؤقتًا. توضح رؤية حلقة for على جانب الاتصال أنك بحاجة إلى التفكير في ذلك.
  • كما أننا لا نريد إضافة حمولات زائدة من IEnumerable إلى AddRange لأنها ستخصص.
  • لا نعتقد أننا بحاجة إلى التحميل الزائد إلى Add الذي يأخذ string و StringComparison . نعم ، من المحتمل أن تكون هذه أكثر فاعلية من الاتصال عبر IEqualityComparer ، لكن يمكننا إصلاح ذلك لاحقًا.
  • نعتقد أن وضع علامة على GetHashCode عفا عليه الزمن مع وجود خطأ فكرة جيدة ، لكننا سنذهب إلى أبعد من ذلك ونخفي أيضًا من IntelliSense.

هذا يترك لنا:

ج #
// سيعيش في التجميع الأساسي
// .NET Framework: mscorlib
// NET Core: System.Runtime / System.Private.CoreLib
نظام مساحة الاسم
{
HashCode الهيكل العام
{
الجمع بين كثافة العمليات العامة العامة(T1 value1) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2) ؛
الجمع بين كثافة العمليات العامة العامة(قيمة T1 ، قيمة T2 ، قيمة T3) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6، T7 value7) ؛
الجمع بين كثافة العمليات العامة العامة(T1 value1، T2 value2، T3 value3، T4 value4، T5 value5، T6 value6، T7 value7، T8 value8) ؛

    public void Add<T>(T value);
    public void Add<T>(T value, IEqualityComparer<T> comparer);

    [Obsolete("Use ToHashCode to retrieve the computed hash code.", error: true)]
    [EditorBrowsable(Never)]
    public override int GetHashCode();

    public int ToHashCode();
}

}
""

الخطوات التالية: المشكلة جاهزة - لتنفيذ واجهة برمجة التطبيقات التي نحتاجها مع العديد من الخوارزميات المرشحة كتجارب - راجع https://github.com/dotnet/corefx/issues/14354#issuecomment -305028686 للحصول على القائمة ، حتى نتمكن من تحديد الخوارزمية التي يجب اتخاذها (بناءً على قياسات الإنتاجية والتوزيع ، من المحتمل أن تكون الإجابة مختلفة لكل بنية وحدة المعالجة المركزية).

التعقيد: كبير

إذا كان أي شخص مهتمًا باستلامه ، يرجى الاتصال بنا. قد يكون هناك متسع للعديد من الأشخاص الذين يعملون عليها معًا. ( jamesqo لديك خيار الأولوية لأنك استثمرت أكثر وأطول في المشكلة)

karelz على الرغم من تعليقي أعلاه ، فقد غيرت رأيي لأنني لا أعتقد أنني أمتلك المؤهلات لاختيار أفضل خوارزمية تجزئة. نظرت في بعض المكتبات @ morganbr المدرجة وأدركت أن التنفيذ معقد للغاية ، لذلك لا يمكنني ترجمته بسهولة إلى C # لاختبارها بنفسي. لدي القليل من الخلفية في C ++ ، لذلك سأواجه صعوبة أيضًا في تثبيت المكتبة وكتابة تطبيق اختبار.

لا أريد أن يظل هذا على قائمة الطلبات الجاهزة إلى الأبد ، رغم ذلك. إذا لم يأخذها أحد لمدة أسبوع من اليوم ، فسأفكر في نشر سؤال على Programmers SE أو Reddit.

لم أقم بتحسينها (أو قمت بتحسينها بطريقة أخرى) ، ولكن هنا تطبيق أساسي لخوارزمية تجزئة Murmur3 التي أستخدمها في العديد من مشاريعي الشخصية: https://gist.github.com/tannergooding/0a12559d1a912068b9aeb4b9586aad7f

أشعر أن الحل الأمثل هنا هو تغيير خوارزمية التجزئة ديناميكيًا بناءً على حجم بيانات الإدخال.

على سبيل المثال: Mumur3 (وآخرون) سريع جدًا بالنسبة لمجموعات كبيرة من البيانات ويوفر توزيعًا رائعًا ، ولكن يمكن أن يكون أداءهم "ضعيفًا" (من حيث السرعة وليس التوزيع الحكيم) لمجموعات البيانات الأصغر.

أتخيل أننا يجب أن نفعل شيئًا مثل: إذا كان عدد البايت الإجمالي أقل من X ، فقم بإجراء الخوارزمية A ؛ خلاف ذلك ، قم بإجراء الخوارزمية B. سيظل هذا حتميًا (لكل تشغيل) ، ولكنه سيسمح لنا بتوفير السرعة والتوزيع بناءً على الحجم الفعلي لبيانات الإدخال.

من الجدير بالذكر أيضًا أن العديد من الخوارزميات المذكورة لها تطبيقات مصممة خصيصًا لتعليمات SIMD ، لذلك من المحتمل أن يتضمن الحل الأكثر أداءً FCALL على مستوى ما (كما هو الحال مع بعض تطبيقات BufferCopy) أو قد ينطوي على تبعية على System.Numerics.Vector .

jamesqo ، يسعدنا مساعدتك في اتخاذ الخيارات ؛ أكثر ما نحتاج إليه هو بيانات الأداء للتطبيقات المرشحة (من الناحية المثالية C # ، على الرغم من أنه كما يشير tannergooding ، فإن بعض الخوارزميات تحتاج إلى دعم مترجم خاص). كما ذكرت أعلاه ، إذا قمت ببناء مرشح لم يتم اختياره ، فربما نستخدمه لاحقًا ، لذلك لا تقلق بشأن إهدار العمل.

أعلم أن هناك معايير مرجعية للعديد من التطبيقات ، لكنني أعتقد أنه من المهم إجراء مقارنة باستخدام واجهة برمجة التطبيقات هذه ومجموعة محتملة من المدخلات (مثل الهياكل ذات الحقول 1-10).

tannergooding ، قد يكون هذا النوع من التكيف هو الأكثر أداءً ، لكنني لا أرى كيف سيعمل مع طريقة Add نظرًا لأنه لا يعرف عدد مرات الاتصال به. بينما يمكننا القيام بذلك باستخدام Combine ، فإن هذا يعني أن سلسلة من استدعاءات Add قد تنتج نتيجة مختلفة عن استدعاء Combine المقابل.

أيضًا ، نظرًا لأن نطاق المدخلات الأكثر احتمالًا هو 4-32 بايت ( Combine`1 - Combine`8 ) ، نأمل ألا تكون هناك تغييرات كبيرة في الأداء على هذا النطاق.

قد يكون هذا النوع من القدرة على التكيف هو الأكثر أداءً ، لكنني لا أرى كيف سيعمل مع طريقة الإضافة نظرًا لأنه لا يعرف عدد مرات استدعائه.

لست مقتنعًا شخصيًا بأن شكل واجهة برمجة التطبيقات مناسب تمامًا للتجزئة للأغراض العامة (ومع ذلك ، فهو قريب) ...

نكشف حاليًا عن طرق Combine للبناء الثابت. إذا كان الهدف منها هو دمج جميع المدخلات وإنتاج كود تجزئة نهائي ، فإن الاسم هو "ضعيف" وقد يكون شيء مثل Compute أكثر ملاءمة.

إذا كنا نكشف عن طرق Combine ، فيجب عليهم فقط مزج جميع المدخلات ويجب أن يطلب من المستخدمين استدعاء طريقة Finalize التي تأخذ الإخراج من آخر مجموعة بالإضافة إلى إجمالي عدد البايتات التي كانت مجتمعة لإنتاج رمز تجزئة نهائي (يعد الانتهاء من رمز التجزئة أمرًا مهمًا لأنه يتسبب في انهيار البتات).

بالنسبة إلى نموذج المنشئ ، نكشف عن طريقة Add و ToHashCode . ليس من الواضح ما إذا كانت طريقة Add تهدف إلى تخزين البايتات والجمع / الإنهاء فقط عند الاستدعاء إلى ToHashCode (في هذه الحالة يمكننا اختيار الخوارزمية الصحيحة ديناميكيًا) أو إذا كانت كذلك من المفترض أن يتم دمجها بسرعة ، يجب أن يكون واضحًا أن هذه هي الحالة (وأن التنفيذ يجب أن يتتبع داخليًا الحجم الإجمالي للبايتات مجتمعة).

لأي شخص يبحث عن نقطة بداية أقل تعقيدًا ، جرب xxHash32. من المحتمل أن يترجم هذا بسهولة إلى C # ( لقد فعلها الناس ).

ما زلت أختبر محليًا ، لكني أرى معدلات الإنتاجية التالية لتطبيق C # الخاص بي لـ Murmur3.

هذه مخصصة لطرق الجمع الثابتة لمدخلات 1-8:

1070.18 mb/s
1511.49 mb/s
1674.89 mb/s
1957.65 mb/s
2083.24 mb/s
2140.94 mb/s
2190.27 mb/s
2245.53 mb/s

يفترض تطبيقي أنه يجب استدعاء GetHashCode لكل إدخال وأنه يجب إنهاء القيمة المحسوبة قبل إعادتها.

لقد جمعت قيم int ، حيث إنها أبسط اختبار.

لحساب الإنتاجية ، قمت بتشغيل 10001 تكرار ، وألغيت التكرار الأول باعتباره تشغيل "الإحماء".

في كل تكرار ، أقوم بتشغيل 10000 تكرار فرعي حيث أستدعي HashCode.Combine ، وأمرر نتيجة التكرار الفرعي السابق كأول قيمة إدخال في التكرار التالي.

ثم أقوم بتوسيط جميع التكرارات للحصول على متوسط ​​الوقت المنقضي ، ثم أقسم ذلك على عدد التكرارات الفرعية التي يتم تشغيلها لكل حلقة للحصول على متوسط ​​الوقت لكل مكالمة. ثم أحسب عدد المكالمات التي يمكن إجراؤها في الثانية وضربها في عدد البايتات المجمعة لحساب الإنتاجية الفعلية.

سنقوم بتنظيف الكود ومشاركته بعد قليل.

tannergooding ، هذا يبدو وكأنه تقدم عظيم. للتأكد من حصولك على القياسات الصحيحة ، فإن الغرض من واجهة برمجة التطبيقات هو أن استدعاء HashCode.Combine(a, b) يعادل الاتصال

HashCode hc = new HashCode();
hc.Add(a); // Initializes the hash state, calls a.GetHashCode() and feeds the result into the hash state
hc.Add(b); // Calls b.GetHashCode() and feeds the result into the hash state
return hc.ToHashCode(); // Finalizes the hash state, truncates it to an int, resets the internal state and returns the int

في كلتا الحالتين ، يجب إدخال البيانات في نفس حالة التجزئة الداخلية ويجب إنهاء التجزئة مرة واحدة في النهاية.

👍

هذا هو ما يفعله الكود الذي كتبته. الاختلاف الوحيد هو أنني أقوم بتضمين كل الكود بشكل فعال (ليست هناك حاجة لتخصيص new HashCode() وتتبع عدد البايتات المجمعة لأنه ثابت).

تضمين التغريدة التنفيذ + اختبار الإنتاجية لـ Murmur3: https://gist.github.com/tannergooding/89bd72f05ab772bfe5ad3a03d6493650

يعتمد MurmurHash3 على الخوارزمية الموضحة هنا: https://github.com/aappleby/smhasher/wiki/MurmurHash3 ، يقول الريبو إنه MIT

العمل على xxHash32 (بند BSD-2 - https://github.com/Cyan4973/xxHash/blob/dev/xxhash.c) و SpookyHash (المجال العام - http://www.burtleburtle.net/bob/hash /spooky.html) المتغيرات

tannergooding مرة أخرى ، لست خبيرًا في التجزئة ، لكنني تذكرت [قراءة مقال] [1] قال إن الكرم لم يكن مقاومًا لـ DoS ، لذا فقط أشرنا إلى ذلك قبل أن نختار ذلك.

jamesqo ، قد أكون مخطئًا ، لكنني متأكد تمامًا من أن الضعف ينطبق على Murmur2 وليس Murmur3.

في كلتا الحالتين ، أقوم بتنفيذ العديد من الخوارزميات حتى نتمكن من الحصول على نتائج إنتاجية لـ C #. التوزيع والخصائص الأخرى لهذه الخوارزميات معروفة جيدًا حتى نتمكن من انتقاء واختيار الأفضل لاحقًا 😄

عفوًا ، نسيت الارتباط بالمقال: http://emboss.github.io/blog/2012/12/14/breaking-murmur-hash-flooding-dos-reloaded/.

تضمين التغريدة معرض الأصوات: +1:

tannergooding ، ألقيت نظرة على تطبيق Murmur3 الخاص بك ويبدو بشكل عام صحيحًا وربما تم تحسينه جيدًا. للتأكد من فهمي بشكل صحيح ، هل تستخدم حقيقة أن القيمة المجمعة والحالة الداخلية للهمهمة كلاهما 32 بتًا؟ من المحتمل أن يكون هذا تحسينًا جيدًا لهذه الحالة ويشرح بعض ارتباكي السابق.

إذا كنا سنتبناها ، فقد تحتاج إلى بعض التعديلات (ربما لن تحدث فرقًا كبيرًا في قياسات الأداء):

  • يجمعيجب أن يستمر استدعاء CombineValue على value1
  • يجب أن تأخذ مكالمات CombineValue الأولى بذرة عشوائية
  • يجب إعادة تعيين ToHashCode إلى _bytesCombined و _combinedValue

في هذه الأثناء بينما أتوق إلى واجهة برمجة التطبيقات هذه ، ما مدى سوء تطبيق GetHashCode عبر (field1, field2, field3).GetHashCode() ؟

@ jnm2 ، يميل مُجمع شفرة تجزئة ValueTuple إلى ترتيب مدخلاتك في كود التجزئة (وتجاهل أقلها حداثة). بالنسبة إلى حقلين وجدول تجزئة يقسم على رقم أولي ، قد لا تلاحظ ذلك. بالنسبة للكثير من الحقول أو جدول التجزئة الذي يقسم على قوة اثنين ، فإن إنتروبيا الحقل الأخير الذي أدخلته سيكون لها التأثير الأكبر على ما إذا كان لديك تصادمات (على سبيل المثال ، إذا كان الحقل الأخير منطقيًا أو عدد قليل من int ، فأنت من المحتمل أن يكون هناك الكثير من الاصطدامات ، إذا كان دليلًا ، فمن المحتمل أنك لن تفعل ذلك).

لا يعمل ValueTuple أيضًا بشكل جيد مع الحقول التي تكون جميعها 0.

في ملاحظة جانبية ، اضطررت إلى التوقف عن العمل على تطبيقات أخرى (لها أولوية أعلى للعمل). لست متأكدًا من الوقت الذي سأتمكن فيه من استلامه احتياطيًا.

إذا لم يكن هذا جيدًا بما يكفي لنوع منظم ، فلماذا يكون جيدًا بما يكفي لـ tuple؟

@ jnm2 ، هذا هو أحد الأسباب التي تجعل هذه الميزة تستحق البناء - حتى نتمكن من استبدال تجزئة دون المستوى عبر إطار العمل.

جدول كبير لوظائف التجزئة مع خصائص الأداء والجودة:
https://github.com/leo-yuriev/t1ha

arespr أعتقد أن الفريق يبحث عن تطبيق C # لوظائف التجزئة. شكرا لك على المشاركة ، مع ذلك.

tannergooding ألا تزال غير قادر على إعادة هذه المشكلة احتياطيًا؟ إذا كان الأمر كذلك ، فسأنشر على Reddit / Twitter أننا نبحث عن خبير تجزئة.

تحرير: تم نشر منشور على Reddit. https://www.reddit.com/r/csharp/comments/6qsysm/looking_for_hash_expert_to_help_net_core_team/؟ref=share&ref_source=link

jamesqo ، لدي بعض الأشياء ذات الأولوية الأعلى على

أيضًا ، ستكون القياسات الحالية محدودة بما يمكننا حاليًا ترميزه في C # ، ومع ذلك ، إذا / عندما يصبح هذا شيئًا (https://github.com/dotnet/designs/issues/13) ، فمن المحتمل أن تتغير القياسات إلى حد ما ؛)

أيضًا ، ستكون القياسات الحالية محدودة بما يمكننا حاليًا ترميزه في C # ، ومع ذلك ، إذا / عندما يصبح هذا شيئًا (dotnet / Design # 13) ، فمن المحتمل أن تتغير القياسات إلى حد ما ؛)

لا بأس بذلك - يمكننا دائمًا تغيير خوارزمية التجزئة بمجرد أن تصبح العناصر الجوهرية متاحة ، مما يتيح لنا تحديد / توزيع رمز التجزئة عشوائيًا القيام بذلك. نحن نبحث فقط عن شيء يقدم أفضل أداء / مقايضة توزيع لوقت التشغيل في حالته الحالية.

jamesqo ، شكرًا للبحث عن أشخاص للمساعدة. يسعدنا أن يكون هناك شخص ليس خبيرًا في التجزئة يعمل على هذا أيضًا - فنحن نحتاج فقط إلى شخص يمكنه نقل بعض الخوارزميات إلى C # من لغات أو تصميمات أخرى ثم إجراء قياسات الأداء. بمجرد اختيار المرشحين ، سيقوم خبراؤنا بما نقوم به بشأن أي تغيير - مراجعة الكود للتحقق من الصحة والأداء والأمان وما إلى ذلك.

أهلا! لقد قرأت للتو من خلال المناقشة ، وعلى الأقل بالنسبة لي يبدو أن القضية مغلقة بشدة لصالح الهمهمة 3-32 PoC. الذي راجع للشغل يبدو اختيارًا جيدًا جدًا بالنسبة لي ، وأنا أوصي بعدم إنفاق أي عمل لا داعي له (ولكن ربما حتى إسقاط أعضاء .Add() ...).

ولكن في الحالة غير المحتملة التي يرغب فيها شخص ما في الاستمرار في المزيد من أعمال الأداء ، يمكنني توفير بعض التعليمات البرمجية لـ xx32 و xx64 و hsip13 / 24 و seahash و murmur3-x86 / 32 (وقمت بدمج إشارة marvin32 من الأعلى) ، و (حتى الآن) غير محسن) sip13 / 24 ، spookyv2. تبدو بعض إصدارات City سهلة بما يكفي لنقلها ، إذا دعت الحاجة إلى ذلك. كان لهذا المشروع نصف المهجور حالة استخدام مختلفة قليلاً في الاعتبار ، لذلك لا توجد فئة HashCode مع واجهة برمجة التطبيقات المقترحة ؛ ولكن بالنسبة للقياس المعياري ، لا ينبغي أن يكون الأمر مهمًا كثيرًا.

بالتأكيد ليس جاهزًا للإنتاج: يطبق الكود كميات سخية من القوة الغاشمة مثل نسخ المعكرونة ، والانتشار السرطاني للعدوانية المضمنة وغير الآمنة ؛ endianess غير موجود ، ولا القراءات غير المحاذية. حتى الاختبارات ضد نواقل اختبار ref-impl فهي تتحدث بشكل ملطف "غير مكتملة".

إذا كانت هذه أي مساعدة على الإطلاق ، فيجب أن أجد وقتًا كافيًا خلال الأسبوعين المقبلين لإصلاح أكثر المشكلات فظاعة ، وإتاحة الكود وبعض النتائج الأولية.

تضمين التغريدة

لقد قرأت للتو من خلال المناقشة ، وعلى الأقل بالنسبة لي يبدو أن القضية مغلقة بشدة لصالح الهمهمة 3-32 PoC. الذي راجع للشغل يبدو اختيارًا جيدًا جدًا بالنسبة لي ، وأنصح بعدم إنفاق أي عمل لا داعي له

لا ، الناس لا يفضلون Murmur3 حتى الآن. نريد التأكد من أننا نختار أفضل خوارزمية مطلقة من حيث التوازن بين الأداء / التوزيع ، لذلك لا يمكننا ترك أي حجر دون تغيير.

ولكن في الحالة غير المحتملة التي يرغب فيها شخص ما في الاستمرار في المزيد من أعمال الأداء ، يمكنني توفير بعض التعليمات البرمجية لـ xx32 و xx64 و hsip13 / 24 و seahash و murmur3-x86 / 32 (وقمت بدمج إشارة marvin32 من الأعلى) ، و (حتى الآن) غير محسن) sip13 / 24 ، spookyv2. تبدو بعض إصدارات City سهلة بما يكفي لنقلها ، إذا دعت الحاجة إلى ذلك.

نعم من فضلك! نريد جمع التعليمات البرمجية لأكبر عدد ممكن من الخوارزميات لاختبارها. كل خوارزمية جديدة يمكنك المساهمة بها لها قيمة. سيكون من دواعي تقديرنا للغاية أن تتمكن من نقل خوارزميات المدينة أيضًا.

بالتأكيد ليس جاهزًا للإنتاج: يطبق الكود كميات سخية من القوة الغاشمة مثل نسخ المعكرونة ، والانتشار السرطاني للعدوانية المضمنة وغير الآمنة ؛ endianess غير موجود ، ولا القراءات غير المحاذية. حتى الاختبارات ضد نواقل اختبار ref-impl فهي تتحدث بشكل ملطف "غير مكتملة".

هذا حسن. ما عليك سوى إحضار الشفرة ، ويمكن لشخص آخر العثور عليها إذا دعت الحاجة.

إذا كانت هذه أي مساعدة على الإطلاق ، فيجب أن أجد وقتًا كافيًا خلال الأسبوعين المقبلين لإصلاح أكثر المشكلات فظاعة ، وإتاحة الكود وبعض النتائج الأولية.

نعم هذا سيكون أمرا رائعا!

jamesqo حسنًا ، سأقوم بإسقاط ملاحظة بمجرد أن يكون لدي شيء لعرضه.

gimpf هذا يبدو رائعًا حقًا ونود أن نسمع عن تقدمك أثناء التنقل (لا داعي للانتظار حتى تبدأ في العمل من خلال كل خوارزمية!). ليس جاهزًا للإنتاج ما دمت تعتقد أن الكود ينتج نتائج صحيحة وأن الأداء يمثل تمثيلًا جيدًا لما نراه في التنفيذ الجاهز للإنتاج. بمجرد اختيار المرشحين ، يمكننا العمل معك للوصول إلى تطبيقات عالية الجودة.

لم أر تحليلاً لكيفية مقارنة إنتروبيا سيهاش بالخوارزميات الأخرى. هل لديك أي مؤشرات على ذلك؟ لديها مقايضات أداء مثيرة للاهتمام ... تبدو التوجيهات سريعة ، لكن الحسابات المعيارية تبدو بطيئة.

@ morganbr لدي دعابة جاهزة.

حول SeaHash : لا ، لا أعرف الجودة حتى الآن ؛ إذا كان الأداء مثيرًا للاهتمام ، فسأضيفه إلى SMHasher. يدعي المؤلف على الأقل أنه أمر جيد (باستخدامه لمجموعات اختبارية في نظام ملفات) ، ويدعي أيضًا أنه لا يتم التخلص من أي إنتروبيا أثناء الخلط.

حول التجزئة والمعايير : Project Haschisch.Kastriert ، صفحة wiki مع نتائج قياس الأداء الأولى التي تقارن xx32 و xx64 و hsip13 و hsip24 و marvin32 و sea و murmur3-32.

بعض المحاذير المهمة:

  • كان هذا تشغيلًا سريعًا للغاية مع إعدادات دقة منخفضة.
  • لم يتم تنفيذ التطبيقات بعد ، ولا يزال بعض المتنافسين مفقودين. تطبيقات البث (مثل هذا الشيء قد يصبح ضروريًا لدعم .Add () المعقول) بحاجة إلى تحسين فعلي.
  • لا يستخدم SeaHash بذرة حاليًا.

الإنطباعات الأولى:

  • بالنسبة للرسائل الكبيرة ، يعد xx64 أسرع التطبيقات المدرجة (حوالي 3.25 بايت لكل دورة ، على حد فهمي ، أو 9.5 جيجابايت / ثانية في دفتر ملاحظاتي)
  • بالنسبة للرسائل القصيرة ، لا يوجد شيء رائع ، لكن الهمهمة 3-32 ، و (بشكل مدهش) seahash لهما ميزة ، ولكن من المحتمل أن يفسر هذا الأخير بواسطة seahash الذي لم يستخدم بذرة بعد.
  • يحتاج "المعيار" للوصول إلى HashSet<> إلى العمل ، حيث أن كل شيء تقريبًا ضمن خطأ القياس (لقد رأيت اختلافات أكبر ، لكن لا يزال لا يستحق الحديث عنها)
  • عند الجمع بين رموز التجزئة ، فإن murmur-3A PoC يكون أسرع بحوالي 5 إلى 20 مرة مما لدينا هنا
  • بعض التجريدات في C # غالية الثمن ؛ مما يجعل مقارنة خوارزميات التجزئة مزعجًا أكثر من اللازم.

سأكتب لك مرة أخرى بمجرد أن أحسّن الوضع قليلاً.

@ gimpf ، هذه بداية رائعة! ألقيت نظرة على الكود والنتائج ولدي بعض الأسئلة.

  1. تظهر النتائج الخاصة بك SimpleMultiplyAdd كما حول 5X أبطأ منtannergooding الصورة Murmur3a. يبدو هذا غريبًا نظرًا لأن لدى Murmur عملًا أكثر من الضرب + الإضافة (على الرغم من أنني سأسلم بأن التدوير عملية أسرع من الإضافة). هل من الممكن أن يكون لعمليات التنفيذ الخاصة بك عدم كفاءة شائع ليس في تنفيذ الهمهمة أو هل يجب أن أقرأ هذا على أنه تطبيقات مخصصة لها ميزة كبيرة على التطبيقات ذات الأغراض العامة؟
  2. الحصول على نتائج لتركيبات 1 و 2 و 4 أمر جيد ، لكن واجهة برمجة التطبيقات هذه ترتفع إلى 8. هل من الممكن الحصول على نتائج لذلك أيضًا أم أن ذلك يسبب الكثير من الازدواجية؟
  3. لقد رأيت أنك قمت بتشغيل X64 ، لذا يجب أن تساعدنا هذه النتائج في اختيار خوارزمية X64 الخاصة بنا ، لكن معايير أخرى تشير إلى أن الخوارزميات يمكن أن تختلف اختلافًا كبيرًا بين X86 و X64. هل من السهل عليك أيضًا الحصول على نتائج X86؟ (في مرحلة ما ، سنحتاج أيضًا إلى الحصول على ARM و ARM64 ، لكن هؤلاء يمكنهم الانتظار بالتأكيد)

نتائج HashSet الخاصة بك مثيرة للاهتمام بشكل خاص. إذا صمدوا ، فهذه حالة محتملة لتفضيل إنتروبيا أفضل على وقت تجزئة أسرع.

morganbr كانت عطلة نهاية الأسبوع هذه متقطعة ، لذا فإن التقدم محدود.

حول أسئلتك:

  1. تظهر النتائج الخاصة بك SimpleMultiplyAdd كما حول 5X أبطأ منtannergooding الصورة Murmur3a. يبدو ذلك غريبا ...

كنت أتساءل نفسي. كان هذا خطأ نسخ / لصق ، كان SimpleMultiplyAdd يجمع دائمًا أربع قيم ... أيضًا ، من خلال إعادة ترتيب بعض العبارات ، أصبح مُجمع الضرب والإضافة أسرع قليلاً (~ 60٪ إنتاجية أعلى).

هل من الممكن أن يكون لعمليات التنفيذ الخاصة بك عدم كفاءة شائع ليس في تنفيذ الهمهمة أو هل يجب أن أقرأ هذا على أنه تطبيقات مخصصة لها ميزة كبيرة على التطبيقات ذات الأغراض العامة؟

من المحتمل أن أفتقد بعض الأشياء ، ولكن يبدو أن تطبيقات الأغراض العامة لـ .NET غير قابلة للاستخدام في حالة الاستخدام هذه. لقد كتبت أساليب الجمع بين جميع الخوارزميات ، وكتبت كود التجزئة الذي يجمع بين معظم الأداء _ بكثير_ أفضل من تلك ذات الأغراض العامة.

ومع ذلك ، حتى تلك التطبيقات لا تزال بطيئة للغاية ؛ هناك حاجة إلى مزيد من العمل. إن أداء .NET في هذا المجال معتم تمامًا بالنسبة لي ؛ يمكن أن تؤدي إضافة أو إزالة نسخة من متغير محلي إلى تغيير الأداء بسهولة بمعامل اثنين. من المحتمل ألا أكون قادرًا على توفير عمليات التنفيذ التي تم تحسينها بشكل كافٍ لغرض تحديد الخيار الأفضل.

  1. الحصول على نتائج لتركيبات 1 و 2 و 4 أمر جيد ، لكن واجهة برمجة التطبيقات هذه ترتفع إلى 8.

لقد قمت بتوسيع معايير الجمع. لا مفاجآت على هذه الجبهة.

  1. رأيت أنك ركضت على X64 (...) ، هل من السهل عليك أيضًا الحصول على نتائج X86؟

لقد كانت ذات مرة ، ولكن بعد ذلك انتقلت إلى .NET Standard. أنا الآن في جحيم التبعية ، ولا تعمل سوى معايير .NET Core 2 و CLR 64bit. يمكن حل هذا بسهولة كافية بمجرد حل المشكلات الحالية.

هل تعتقد أن هذا سيجعله في الإصدار 2.1؟

gimpf لم تنشر منذ فترة - هل لديك تحديث للتقدم في عمليات التنفيذ الخاصة بك؟ : مبتسم:

jamesqo لقد أصلحت بعض المعايير التي تسببت في نتائج غريبة ، وأضفت City32 و SpookyV2 و Sip13 و Sip24 إلى قائمة الخوارزميات المتاحة. تعتبر Sips سريعة كما هو متوقع (بالنسبة إلى سرعة نقل xx64) ، و City و Spooky ليست كذلك (لا يزال الأمر كذلك بالنسبة لـ SeaHash).

للجمع بين أكواد التجزئة ، لا يزال Murmur3-32 يبدو رهانًا جيدًا ، لكن لم أجري بعد مقارنة أكثر شمولاً.

من ناحية أخرى ، فإن واجهة برمجة التطبيقات المتدفقة (.Add ()) لها تأثير جانبي مؤسف يتمثل في إزالة بعض خوارزميات التجزئة من قائمة المرشحين. نظرًا لأن أداء واجهة برمجة التطبيقات مشكوك فيه أيضًا ، فقد ترغب في إعادة التفكير فيما إذا كنت ستقدمها من البداية.

إذا تم تجنب الجزء .Add() ، وبالنظر إلى أن أداة دمج التجزئة تستخدم بذرة ، فلا أعتقد أنه سيكون هناك أي ضرر في تنظيف أداة دمج tg ، وإنشاء مجموعة اختبار صغيرة ، و أوقف العمل. نظرًا لأنني لا أملك سوى بضع ساعات في نهاية كل أسبوع ، وكان تحسين الأداء مملاً إلى حد ما ، فإن جعل الإصدار المطلي بالذهب قد يستمر قليلاً ...

gimpf ، هذا يبدو وكأنه تقدم رائع. هل لديك جدول نتائج في متناول يدك حتى نتمكن من معرفة ما إذا كان هناك ما يكفي لاتخاذ القرار والمضي قدمًا؟

morganbr لقد قمت بتحديث نتائج القياس الخاصة بي.

في الوقت الحالي ، حصلت على نتائج 64 بت فقط على .NET Core 2. بالنسبة لهذا النظام الأساسي ، يعد City64 بدون بذور هو الأسرع عبر جميع الأحجام. بدمج بذرة ، يتم ربط XX-32 بـ Murmur-3-32. لحسن الحظ ، هذه هي نفس الخوارزميات التي تتمتع بسمعة كونها سريعة لمنصات 32 بت ، ولكن من الواضح أننا بحاجة إلى التحقق من صحة ذلك بالنسبة لتطبيقي أيضًا. يبدو أن النتائج تمثل أداء العالم الحقيقي ، باستثناء أن Sea و SpookyV2 يبدوان بطيئين بشكل غير عادي.

ستحتاج إلى التفكير في مقدار ما تحتاج إليه حقًا من حماية تجزئة لأدوات دمج كود التجزئة. إذا كانت هناك حاجة إلى البذر فقط لجعل التجزئة غير قابلة للاستخدام بشكل واضح للاستمرار ، فإن city64 بمجرد أن يكون XOR يحتوي على بذرة 32 بت سيكون تحسينًا. نظرًا لأن هذه الأداة متوفرة فقط للجمع بين التجزئة (وليس استبدال رمز التجزئة للسلاسل على سبيل المثال ، أو تجزئة المصفوفات الصحيحة وما إلى ذلك) ، فقد يكون ذلك جيدًا بدرجة كافية.

إذا كنت تعتقد أنك بحاجة إليها ، فسيسعدك أن ترى أن Sip13 عادة ما يكون أبطأ بنسبة 50٪ من XX-32 (على الأنظمة الأساسية 64 بت) ، ولكن من المحتمل أن تكون هذه النتيجة مختلفة بشكل كبير بالنسبة لتطبيقات 32 بت.

لا أعرف مدى ارتباطها بـ corefx ، لكني أضفت نتائج LegacyJit 32bit (w / FW 4.7).

أود أن أقول إن النتائج بطيئة بشكل مضحك. ومع ذلك ، على سبيل المثال ، عند 56 ميجابايت / ثانية مقابل 319 ميجابايت / ثانية أنا لا أضحك (هذا هو Sip ، إنه يفتقد إلى أقصى حد للاستدارة لليسار). أعتقد أنني أتذكر سبب إلغاء مشروع خوارزمية التجزئة .NET في كانون الثاني (يناير) ...

لذا ، فإن RyuJit-32bit لا يزال مفقودًا ، وسوف يعطي (نأمل) نتائج مختلفة جدًا ، ولكن بالنسبة لـ LegacyJit-x86 ، فإن Murmur-3-32 يفوز بسهولة ، ويمكن أن يقترب فقط City-32 و xx-32. لا يزال أداء Murmur سيئًا عند حوالي 0.4 إلى 1.1 جيجابايت / ثانية فقط بدلاً من 0.6 إلى 2 جيجابايت / ثانية (على نفس الجهاز) ، ولكنه على الأقل في الملعب الصحيح.

سأقوم بتشغيل المعايير على عدد قليل من الصناديق الخاصة بي الليلة ونشر النتائج (Ryzen و i7 و Xeon و A10 و i7 Mobile وأعتقد أن هناك زوجين آخرين).

tannergooding @ morganbr بعض التحديثات لطيفة وبعض التحديثات الهامة.

المهم أولا:

  • لقد أصلحت بعض عمليات الدمج التي كانت تنتج قيم تجزئة غير صحيحة.
  • تعمل المجموعة المعيارية الآن بجدية أكبر لتجنب الطي المستمر. كان City64 عرضة للإصابة (كما كان نفخة 3-32 في الماضي). لا يعني ذلك أنني فهمت كل نتيجة الآن ، لكنها أكثر منطقية بكثير.

أشياء جميله:

  • تتوفر تطبيقات Combiner الآن لجميع الأحمال الزائدة من الوسيطات من 1 إلى 8 ، بما في ذلك عمليات التنفيذ غير المرهقة يدويًا إلى حد ما لـ xx / city.
  • تقوم الاختبارات والمعايير بالتحقق من ذلك أيضًا. نظرًا لأن العديد من خوارزميات التجزئة تحتوي على رسائل ذات بايت منخفض ذات غلاف خاص ، فقد تكون هذه القياسات ذات أهمية.
  • معايير تشغيل مبسطة لأهداف متعددة (Core مقابل FW).

لتشغيل مجموعة على جميع التطبيقات الأولية للجمع بين أكواد التجزئة ، بما في ذلك "Empty" (عبء خالص) و "مضاعفة - إضافة" (نسخة محسّنة للسرعة من SO answer الشهيرة):

bin\Release\net47\Haschisch.Benchmarks.Net47.exe -j:clr_x86 -j:clr_x64_legacy -j:clr_x64 -j:core_x64 -- CombineHashCode --allcategories=prime

(_ يبدو أن تشغيل المعايير الأساسية 32 بت بشكل ملائم يتطلب الإصدار التجريبي من BenchmarkDotNet (أو ربما إعداد 32 بت فقط بالإضافة إلى استخدام معيار الأداء الأساسي). يجب أن يعمل بعد ذلك باستخدام -j: core_x86 ، على أمل) _

النتائج : بعد كل إصلاحات الأخطاء ، يبدو أن xx32 يفوز بجميع الأحمال الزائدة مع 64 بت RyuJIT ، على Windows 10 على هاتف Haswell i7 ، في تشغيل "سريع". بين Sips و marvin32 ، Sip-1-3 يفوز دائمًا. Sip-1-3 هو أبطأ بنحو 4 مرات من xx32 ، وهو مرة أخرى أبطأ مرتين تقريبًا من مُجمع بدائي متعدد الإضافة. لا تزال نتائج 32 بت Core مفقودة ، لكنني أنتظر إلى حد ما إصدار BenchmarkDotNet المستقر الذي سيحل هذه المشكلة بالنسبة لي.

(تحرير) لقد أضفت للتو تشغيلًا سريعًا لمعيار للوصول إلى مجموعة التجزئة . من الواضح أن هذا يعتمد على التفاصيل أكثر بكثير من معايير أعلاه ، ولكن قد ترغب في إلقاء نظرة عليها.

شكرا مرة أخرى gimpf على البيانات الرائعة! دعونا نرى ما إذا كان بإمكاننا تحويل ذلك إلى قرار.

في البداية ، سأقسم الخوارزميات على النحو التالي:
إنتروبيا سريعة + جيدة (مرتبة حسب السرعة):

  1. xxHash32
  2. City64 (من المحتمل أن يكون هذا بطيئًا على x86 ، لذلك ربما يتعين علينا اختيار شيء آخر لـ x86)
  3. همهمة

مقاومة HashDoS:

  • مارفين 32
  • سيفهاش. إذا كنا نميل إلى ذلك ، فسنحتاج إلى مراجعته من قبل خبراء التشفير في Microsoft للتأكد من أن نتائج البحث مقبولة. سيتعين علينا أيضًا معرفة المعلمات الآمنة بدرجة كافية. تقترح الورقة مكانًا ما بين Sip-2-4 و Sip-4-8.

خارج الخلاف (بطيء):

  • سبوكي
  • مدينة 32
  • xxHash64
    * SeaHash (وليس لدينا بيانات عن الإنتروبيا)

خارج الخلاف (إنتروبيا سيئة):

  • اضرب
  • HSip

قبل أن نختار فائزًا ، أود أن أتأكد من أن الأشخاص الآخرين يتفقون مع مجموعتي أعلاه. إذا كان الأمر كذلك ، أعتقد أننا نحتاج فقط إلى اختيار ما إذا كنا سنقوم بدفع 2x لمقاومة HashDoS ثم نذهب بالسرعة.

morganbr يبدو التجميع الخاص بك جيدًا. كنقطة بيانات في جولات SipHash ، سأل مشروع Rust Jean-Philippe Aumasson ، الذي قام بتأليف sip-hash w / DJB. بعد تلك المناقشة قرروا الذهاب ل sip-1-3 لجداول التجزئة.

(انظر صدأ العلاقات العامة: # 33940 والصدأ المصاحب

بناءً على البيانات والتعليقات ، أود أن أقترح استخدام xxHash32 في جميع البنى. الخطوة التالية هي تنفيذها. gimpf ، هل أنت مهتم بتجميع العلاقات العامة لذلك؟

بالنسبة لأولئك المهتمين بـ HashDoS ، سأتابع قريبًا اقتراحًا لواجهة برمجة تطبيقات تجزئة للأغراض العامة والتي يجب أن تتضمن Marvin32 وقد تتضمن SipHash. وسيكون ذلك أيضا مكان مناسب لتطبيقات أخرىgimpf وtannergooding قد عملت على.

morganbr يمكنني تجميع العلاقات العامة عندما يسمح الوقت بذلك. أيضًا ، أنا شخصياً أفضل xx32 أيضًا ، طالما أنه لا يقلل من القبول.

@ gimpf ، كيف يبدو وقتك؟ إذا لم يكن لديك الوقت حقًا ، فيمكننا أيضًا معرفة ما إذا كان أي شخص آخر يرغب في منحها فرصة.

morganbr كنت

gimpf ، يبدو رائعًا. شكرا للتحديث!

terrajobst - لقد تأخرت قليلاً عن الحفلة (آسف) ، لكن لا يمكننا تغيير نوع الإرجاع لطريقة الإضافة؟

ج #
إضافة HashCode العامة(قيمة T) ؛
إضافة HashCode العامة(قيمة T ، IEqualityComparerالمقارنة) ؛

The params code is clearly there for scenarios where you have multiple fields, e.g.

```c#
        public override int GetHashCode() => new HashCode().Add(Name, Surname).ToHashCode();

ومع ذلك ، يمكن تحقيق الشيء نفسه تمامًا مثل هذا ، وإن كان ذلك مع تخصيص مصفوفة أقل إهدارًا:

c# public override int GetHashCode() => new HashCode().Add(Name).Add(Surname).Add(Age).ToHashCode();

لاحظ أنه يمكن أيضًا خلط الأنواع. من الواضح أن هذا يمكن أن يتم من خلال عدم تسميته بطلاقة داخل طريقة عادية. بالنظر إلى هذه الحجة القائلة بأن الواجهة الطلاقة ليست ضرورية تمامًا ، فلماذا يوجد التحميل الزائد المهدر من params لتبدأ به؟ إذا كان هذا الاقتراح اقتراحًا سيئًا ، فإن الحمل الزائد هو params يقع في نفس الفأس. هذا ، وإجبار طريقة منتظمة للحصول على رمز تجزئة تافه ولكنه مثالي يبدو كثيرًا من الاحتفالات.

تحرير: implicit operator int سيكون أيضًا أمرًا رائعًا بالنسبة لـ DRY ، لكن ليس بالغ الأهمية.

تضمين التغريدة

لا يمكننا تغيير نوع الإرجاع لطريقة الإضافة؟

لقد ناقشنا ذلك بالفعل في الاقتراح القديم ، وتم رفضه.

لماذا توجد المعلمات المهدرة للحمل الزائد لتبدأ؟

نحن لا نضيف أي بارامز الزائد؟ قم بعمل Ctrl + F لـ "params" على صفحة الويب هذه ، وسترى أن تعليقك هو المكان الوحيد الذي تظهر فيه هذه الكلمة.

قد يكون عامل التشغيل الضمني جيدًا أيضًا لـ DRY ، ولكنه ليس مهمًا تمامًا.

أعتقد أن هذا تمت مناقشته أيضًا في مكان ما أعلاه ...

jamesqo شكرا على الشرح.

البارامز الزائدة

قصدت AddRange ، لكنني أعتقد أنه لن يكون هناك أي تأثير على هذا.

jcdickinson AddRange كان في الاقتراح الأصلي ، لكنه ليس في الإصدار الحالي. تم رفضه من خلال مراجعة واجهة برمجة التطبيقات (راجع https://github.com/dotnet/corefx/issues/14354#issuecomment-308190321 بواسطةterrajobst):

يجب علينا إزالة جميع طرق AddRange لأن السيناريو غير واضح. من غير المحتمل إلى حد ما أن تظهر المصفوفات كثيرًا. وبمجرد تضمين المصفوفات الكبيرة ، فإن السؤال المطروح هو ما إذا كان يجب تخزين الحساب مؤقتًا. توضح رؤية حلقة for على جانب الاتصال أنك بحاجة إلى التفكير في ذلك.

@ gimpf لقد الاقتراح باستخدام xxHash32 . لا تتردد في الحصول على هذا التنفيذ. لديها اختبارات ضد ناقلات xxHash32 الفعلية.

يحرر

بخصوص الواجهة. إنني أدرك تمامًا أنني أصنع جبلًا من تلة - فلا تتردد في تجاهلها. أنا أستخدم الاقتراح الحالي ضد أشياء حقيقية وهو تكرار مزعج كثيرًا.

لقد كنت أتلاعب بالواجهة وأدرك الآن سبب رفض الواجهة بطلاقة ؛ إنه أبطأ بشكل ملحوظ .

BenchmarkDotNet=v0.10.9, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i7-4800MQ CPU 2.70GHz (Haswell), ProcessorCount=8
Frequency=2630626 Hz, Resolution=380.1377 ns, Timer=TSC
.NET Core SDK=2.0.2
  [Host]     : .NET Core 2.0.0 (Framework 4.6.00001.0), 64bit RyuJIT
  DefaultJob : .NET Core 2.0.0 (Framework 4.6.00001.0), 64bit RyuJIT

استخدام طريقة غير مضمنة كمصدر شفرة تجزئة ؛ 50 طلبًا للإضافة مقابل طريقة التمديد بطلاقة:

| الطريقة | يعني | خطأ | StdDev | تحجيم |
| ------- | ---------: | ---------: | ---------: | -------: |
| أضف | 401.6 نانوثانية | 1.262 نانوثانية | 1.180 نانوثانية | 1.00 |
| تالي | 747.8 نانوثانية | 2.329 نانوثانية | 2.178 نانوثانية | 1.86 |

ومع ذلك ، فإن النمط التالي يعمل:

ج #
HashCode للبنية العامة: System.Collections.Inumerable
{
[EditorBrowsable (EditorBrowsableState.Never)]
[قديم ("يتم توفير هذه الطريقة لبناء جملة مُهيئ المجموعة." ، خطأ: صحيح)]
public IEnumerator GetEnumerator () => رمي NotImplementedException () الجديد ؛
}

public override int GetHashCode() => new HashCode()
{
    Age, // int
    { Name, StringComparer.Ordinal }, // use Comparer
    Hat // some arbitrary object
}.ToHashCode();

""

كما أن لها خصائص أداء متطابقة مع العرض الحالي:

| الطريقة | يعني | خطأ | StdDev | تحجيم |
| ------------ | ---------: | ---------: | ---------: | --- ----: |
| أضف | 405.0 نانوثانية | 2.130 نانوثانية | 1.889 نانوثانية | 1.00 |
| المُهيئ | 400.8 نانوثانية | 4.821 نانوثانية | 4.274 نانوثانية | 0.99 |

للأسف ، يعد هذا اختراقًا إلى حد ما ، حيث يجب تنفيذ IEnumerable لإبقاء المترجم سعيدًا. ومع ذلك ، فإن Obsolete سيظهر خطأ حتى foreach - يجب أن ترغب حقًا في كسر الأشياء من أجل الوصول إلى الاستثناء. MSIL عبر الاثنين متطابق بشكل أساسي.

jcdickinson شكرا للاستيلاء على القضية. لقد أرسلت إليك دعوة Collaborator ، أخبرني عندما تقبل وسأكون قادرًا على تعيين هذه المشكلة لك (التخصيص لنفسي في الوقت الحالي).

نصيحة احترافية: بمجرد قبولك ، سيقوم GitHub بتسجيل اشتراكك تلقائيًا لجميع الإشعارات من الريبو (أكثر من 500 في اليوم) ، أوصي بتغييرها إلى "عدم المشاهدة" فقط والتي سترسل إليك جميع الإشارات والإشعارات الخاصة بالمشكلات قمت بالاشتراك فيها.

jcdickinson ، أنا مهتم بالتأكيد بطرق تجنب التكرار المزعج (على الرغم من أنني لا أملك أي فكرة عن شعور الناس تجاه بناء جملة التهيئة). يبدو أنني أتذكر أن هناك مشكلتين مع الطلاقة:

  1. مشكلة الأداء التي لاحظتها
  2. القيمة المعادة من الطرق بطلاقة هي نسخة من البنية. من السهل جدًا أن ينتهي بك الأمر عن طريق الخطأ إلى فقدان المدخلات أثناء القيام بأشياء مثل:
var hc = new HashCode();
var newHc = hc.Add(foo);
hc.Add(bar);
return newHc.ToHashCode();

نظرًا لأنه تمت الموافقة بالفعل على الاقتراح الخاص بسلسلة الرسائل هذه (وأنت في طريقك لدمجها) ، أقترح بدء اقتراح واجهة برمجة تطبيقات جديد لأية تغييرات.

Karelz أعتقد أن @ gimpf قد gimpf بدلاً من ذلك. ( تحرير: nvm)

terrajobst نوع واحد من طلبات API في اللحظة الأخيرة لهذا الغرض. نظرًا لأننا حددنا GetHashCode عفا عليه الزمن ، فإننا نخبر المستخدم ضمنيًا أن HashCode s ليست قيمًا من المفترض مقارنتها ، على الرغم من كونها هياكل غير قابلة للتغيير / قابلة للمقارنة عادةً. في هذه الحالة ، هل يجب أن نضع علامة على Equals عفا عليها الزمن أيضًا؟

[Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)]
[EditorBrowsable(Never)]
// If this is too harsh, base.Equals() is fine as long as the [Obsolete] stays
public override bool Equals(object obj) => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes.");

أعتقد أن شيئًا مشابهًا تم القيام به بـ Span .

إذا تم قبول ذلك ، فأعتقد ...

  1. أفكر في استخدام should not أو may not بدلاً من cannot في الرسالة القديمة.
  2. بشرط بقاء الاستثناء ، سأضع نفس السلسلة في رسالتها ، فقط في حالة استدعاء الطريقة من خلال فريق عمل أو عام مفتوح.

@ Joe4evr بخير معي ؛ لقد قمت بتحديث التعليق. قد يكون من المفيد أيضًا تضمين نفس الرسالة في استثناء GetHashCode أيضًا ، إذًا:

public override int GetHashCode() => throw new NotSupportedException("HashCode is a mutable struct and should not be compared with other HashCodes.");

morganbr لماذا

لم تمر العلاقات العامة لعرضها في CoreFX بعد.

gimpf هل لديك الكود الذي قمت بقياس الأداء و / أو هل ستكون قادرًا على رؤية كيفية عرض حزمة SpookilySharp nuget بسرعة. إنني أتطلع إلى إزالة الغبار عن هذا المشروع بعد عامين من الركود وأنا أشعر بالفضول لمعرفة كيف يقف.

JonHanna لقد نشرها هنا: https://github.com/gimpf/Haschisch.Kastriert

JonHanna ، سأكون مهتمًا بمعرفة كيفية إجراء الاختبار حتى نتمكن من البدء في التفكير فيما قد يكون مفيدًا في واجهة برمجة تطبيقات التجزئة غير المشفرة للأغراض العامة.

morganbr أين سيكون المنتدى المناسب لمناقشة مثل API؟ أتوقع أن تتكون واجهة برمجة التطبيقات هذه من أكثر من مجرد قاسم مشترك صغير ، وربما تحتاج واجهة برمجة تطبيقات جيدة أيضًا إلى معالجة JIT wrt محسنة للهياكل الأكبر. مناقشة كل ما يمكن القيام به بشكل أفضل في قضية منفصلة ...

gimpf فتح واحدًا لك. دوت نت / corefx # 25666

morganbr - هل يمكننا الحصول على اسم الحزمة والإصدار رقم الذي سيتضمن هذا الالتزام؟

karelz ، هل يمكنك مساعدة smitpatel بمعلومات الحزمة / الإصدار؟

سأحاول البناء اليومي لـ .NET Core - سأنتظر حتى الغد.
لا أعتقد أن هناك حزمة يمكنك الاعتماد عليها ببساطة.

سؤال للمشاركين هنا. يسمح Roslyn IDE للمستخدمين بإنشاء ضمنية GetHashCode استنادًا إلى مجموعة من الحقول / الخصائص في فئتهم / هيكلهم. من الناحية المثالية ، يمكن للأشخاص استخدام HashCode.Combine الجديد الذي تمت إضافته في https://github.com/dotnet/corefx/pull/25013 . ومع ذلك ، لن يتمكن بعض المستخدمين من الوصول إلى هذا الرمز. لذلك ، نود أن نبقى قادرين على إنشاء GetHashCode الذي سيعمل معهم.

في الآونة الأخيرة ، لاحظنا أن الشكل الذي ننشئه يمثل مشكلة. وبالتحديد ، لأن VB يقوم بالتجميع مع عمليات التحقق من الفائض افتراضيًا ، وسوف يتسبب الضمير الخاص بنا في حدوث تجاوزات. أيضًا ، ليس لدى VB طريقة لتعطيل عمليات التحقق من الفائض لمنطقة من التعليمات البرمجية. إنه إما مفتوح أو مغلق تمامًا للتجميع بأكمله.

لهذا السبب ، أود أن أكون قادرًا على استبدال الضميمة التي نقدمها بنموذج لا يعاني من هذه المشاكل. من الناحية المثالية ، سيكون للنموذج الذي تم إنشاؤه الخصائص التالية:

  1. سطر / سطرين في GetHashCode لكل حقل / خاصية مستخدمة.
  2. لا يفيض.
  3. تجزئة جيدة بشكل معقول. نحن لا نتوقع نتائج مذهلة. ولكن هناك شيء تم التحقق منه بالفعل ليكون لائقًا ، ولكي لا تواجه المشاكل التي تواجهها عادةً مع a + b + c + d أو a ^ b ^ c ^ d .
  4. لا تبعيات / متطلبات إضافية على الكود.

على سبيل المثال ، قد يكون أحد خيارات VB هو إنشاء شيء مثل:

return (a, b, c, d).GetHashCode()

لكن هذا يعتمد بعد ذلك على وجود إشارة إلى System.ValueTuple. من الناحية المثالية ، يمكن أن يكون لدينا ضمانة تعمل حتى في حالة عدم وجود ذلك.

هل يعرف أي شخص خوارزمية تجزئة جيدة يمكنها العمل مع هذه القيود؟ شكرا!

-

ملاحظة: الكود الحالي المنبعث لدينا هو:

        Dim hashCode = -252780983
        hashCode = hashCode * -1521134295 + i.GetHashCode()
        hashCode = hashCode * -1521134295 + j.GetHashCode()
        Return hashCode

من الواضح أن هذا يمكن أن يفيض.

هذه أيضًا ليست مشكلة لـ C # حيث يمكننا فقط إضافة unchecked { } حول هذا الرمز. هذا التحكم الدقيق غير ممكن في VB.

هل يعرف أي شخص خوارزمية تجزئة جيدة يمكنها العمل مع هذه القيود؟ شكرا!

حسنًا ، يمكنك عمل Tuple.Create(...).GetHashCode() . من الواضح أن ذلك ينطوي على تخصيصات ، لكنه يبدو أفضل من طرح استثناء.

هل هناك أي سبب يمنعك من إخبار المستخدم بتثبيت System.ValueTuple ؟ نظرًا لأنها ميزة لغة مضمنة ، فأنا متأكد من أن حزمة System.ValueTuple متوافقة جدًا مع جميع الأنظمة الأساسية بشكل أساسي ، أليس كذلك؟

من الواضح أن ذلك ينطوي على تخصيصات ، لكنه يبدو أفضل من طرح استثناء.

نعم فعلا. سيكون من الجيد ألا يكون سبب التخصيصات.

هل هناك أي سبب يمنعك من إخبار المستخدم بتثبيت System.ValueTuple؟

سيكون هذا هو السلوك إذا أنشأنا نهج ValueTuple. ومع ذلك ، مرة أخرى ، سيكون من الجيد أن نتمكن من إنشاء شيء جيد يتناسب مع الطريقة التي بنى المستخدم بها الكود الخاص به حاليًا ، دون جعله يغير هيكله بطريقة ثقيلة الوزن.

يبدو حقًا أن مستخدمي VB يجب أن يكون لديهم طريقة لمعالجة هذه المشكلة بطريقة معقولة :) لكن مثل هذا النهج يراوغني :)

CyrusNajmabadi ، إذا كنت حقًا بحاجة إلى إجراء حساب التجزئة الخاص بك في كود المستخدم ، فقد يعمل CRC32 لأنه مزيج من عمليات البحث في الجدول و XORs (ولكن ليس الحساب الذي يمكن أن يتجاوز). هناك بعض العيوب على الرغم من:

  1. لا يحتوي CRC32 على إنتروبيا رائعة (لكن من المحتمل أنه لا يزال أفضل مما ينبعث من Roslyn الآن).
  2. ستحتاج إلى وضع جدول بحث 256 إدخالًا في مكان ما في الكود أو إرسال رمز لإنشاء جدول البحث.

إذا لم تكن تفعل ذلك بالفعل ، آمل أن تتمكن من اكتشاف نوع HashCode واستخدامه عندما يكون ذلك ممكنًا لأن XXHash يجب أن يكون أفضل بكثير.

@ morganbr راجع https://github.com/dotnet/roslyn/pull/24161

نقوم بما يلي:

  1. استخدم System.HashCode إذا كان متاحًا. منتهي.
  2. خلاف ذلك ، إذا كان في C #:
    2 أ. إذا لم تكن في وضع التحديد: قم بإنشاء تجزئة غير مسجلة.
    2 ب. إذا كنت في وضع التحديد: قم بإنشاء تجزئة غير مسجلة ، ملفوفة في "غير محدد {}".
  3. خلاف ذلك ، إذا كان في VB:
    3 ب. إذا لم تكن في وضع التحديد: قم بإنشاء تجزئة غير مسجلة.
    3 ج. إذا كان في وضع التحديد ، ولكن لديه حق الوصول إلى System.ValueTuple: إنشاء Return (a, b, c, ...).GetHashCode()
    ثلاثي الأبعاد. إذا كنت في وضع التحديد دون الوصول إلى System.ValueTuple. قم بإنشاء تجزئة غير مسجلة ، ولكن أضف تعليقًا في VB من المحتمل جدًا أن يحدث تجاوزات.

إنه "ثلاثي الأبعاد" هذا أمر مؤسف حقًا. بشكل أساسي ، لن يتمكن أي شخص يستخدم VB ولكن لا يستخدم ValueTuple أو نظامًا حديثًا من استخدامنا للحصول على خوارزمية تجزئة معقولة تم إنشاؤها لهم.

ستحتاج إلى وضع جدول بحث 256 إدخالًا في مكان ما في الكود

سيكون هذا غير مستساغ على الإطلاق :)

هل رمز إنشاء الجدول غير مستساغ أيضًا؟ على الأقل وفقًا لمثال ويكيبيديا ، فهو ليس رمزًا كبيرًا (ولكن لا يزال يتعين عليه الانتقال إلى مكان ما في مصدر المستخدم).

ما مدى فظاعة إضافة مصدر HashCode إلى المشروع مثلما يفعل Roslyn (مع IL) مع تعريفات فئة سمات المحول البرمجي (الأبسط بكثير) عندما لا تكون متاحة من خلال أي تجميع مرجعي؟

ما مدى فظاعة إضافة مصدر HashCode إلى المشروع كما يفعل Roslyn مع (أبسط بكثير) تعريفات فئة سمات المحول البرمجي عندما لا تكون متاحة من خلال أي تجميع مرجعي؟

  1. هل مصدر HashCode لا يحتاج إلى سلوك تجاوز؟
  2. لقد قمت بقشط مصدر HashCode. إنه غير تافه. سيكون توليد كل هذه الأشياء في مشروع المستخدم ثقيلًا جدًا.

أنا مندهش من عدم وجود طرق جيدة لجعل الرياضيات الفائضة تعمل في VB على الإطلاق :(

لذلك ، على الأقل ، حتى لو قمنا بتجزئة قيمتين معًا ، يبدو أنه يتعين علينا إنشاء:

ج #
var hc1 = (uint) (value1؟ .GetHashCode () ؟؟ 0) ؛ // يمكن أن تفيض
var hc2 = (uint) (value2؟ .GetHashCode () ؟؟ 0) ؛ // يمكن أن تفيض

        uint hash = MixEmptyState();
        hash += 8; // can overflow

        hash = QueueRound(hash, hc1);
        hash = QueueRound(hash, hc2);

        hash = MixFinal(hash);
        return (int)hash; // can overflow
Note that this code already has 4 lines that can overflow.  It also has two helper functions you need to call (i'm ignoring MixEmptyState as that seems more like a constant).  MixFinal can *definitely* overflow:

```c#
        private static uint MixFinal(uint hash)
        {
            hash ^= hash >> 15;
            hash *= Prime2;
            hash ^= hash >> 13;
            hash *= Prime3;
            hash ^= hash >> 16;
            return hash;
        }

كما يمكن لـ QueueRound:

c# private static uint QueueRound(uint hash, uint queuedValue) { hash += queuedValue * Prime3; return Rol(hash, 17) * Prime4; }

لذلك أنا لا أرى بصدق كيف سيعمل هذا :(

ما مدى فظاعة إضافة مصدر HashCode إلى المشروع مثلما يفعل Roslyn (مع IL) مع (الكثير

كيف تتخيل هذا العمل؟ ما الذي سيكتبه العملاء ، وما الذي سيفعله المترجمون بعد ذلك؟

أيضًا ، الشيء الذي من شأنه أن يعالج كل هذا هو إذا كان لدى .Net بالفعل مساعدين عموميين مكشوفين على واجهة برمجة التطبيقات السطحية التي تتحول من uint إلى int32 (والعكس صحيح) بدون تجاوز.

هل هؤلاء موجودون؟ إذا كان الأمر كذلك ، يمكنني بسهولة كتابة إصدارات VB ، فقط باستخدامها في المواقف التي نحتاج فيها إلى الانتقال بين الأنواع دون تجاوزها.

هل رمز إنشاء الجدول غير مستساغ أيضًا؟

أعتقد ذلك. أعني ، فكر في هذا من منظور العميل. إنهم يريدون فقط طريقة GetHashCode محترمة وذاتية جيدة وتعطي نتائج معقولة. إن امتلاك هذه الميزة وانتفاخ الكود الخاص بهم مع حماقة إضافية سيكون أمرًا مزعجًا للغاية. إنه أمر سيء أيضًا نظرًا لأن تجربة C # ستكون على ما يرام.

قد تتمكن من الحصول على سلوك الفائض الصحيح تقريبًا عن طريق الإرسال من وإلى مجموعة من أنواع 64 بت الموقعة وغير الموقعة. شيء من هذا القبيل (لم يتم اختباره ولا أعرف بناء جملة VB casting):

Dim hashCode = -252780983
hashCode = (Int32)((Int32)((Unt64)hashCode * -1521134295) + (UInt64)i.GetHashCode())

كيف تعرف أن ما يلي لا يفيض؟

c# (Int32)((Unt64)hashCode * -1521134295)

أو النهائي (int32) يلقي لهذه المسألة؟

لم أكن أدرك أنه سيستخدم عمليات تحويل تم التحقق من فائضها. أعتقد أنه يمكنك إخفاءه حتى 32 بت قبل الإرسال:

(Int32)(((Unt64)hashCode * -1521134295) & 0xFFFFFFFF)

من المفترض أن 31 بت ، كقيمة uint32. سيتجاوز الحد الأقصى أيضًا عند التحويل إلى Int32 :)

هذا ممكن مواطنه. قبيح ... لكن ممكن :) هناك الكثير من الممثلين في هذا الكود.

نعم. أعتقد أن لدي حل عملي. جوهر الخوارزمية التي ننتجها اليوم هو:

c# hashCode = hashCode * -1521134295 + j.GetHashCode();

لنفترض أننا نجري عملية حسابية 64 بت ، ولكن تم تقييد "hashCode" إلى 32 بت. ثم <largest_32_bit> * -1521134295 + <largest_32_bit> لن يتجاوز 64 بت. لذلك يمكننا دائمًا إجراء العمليات الحسابية في 64 بت ، ثم التشديد على 32 (أو 32 بت) لضمان عدم تجاوز الجولة التالية.

شكرا!

@ MaStr11morganbrsharwell والجميع هنا. لقد قمت بتحديث الكود الخاص بي لإنشاء ما يلي لـ VB:

        Dim hashCode As Long = 2118541809
        hashCode = (hashCode * -1521134295 + a.GetHashCode()) And Integer.MaxValue
        hashCode = (hashCode * -1521134295 + b.GetHashCode()) And Integer.MaxValue
        Return CType(hashCode And Integer.MaxValue, Integer)

هل يمكن لشخص ما أن يفحصني للتأكد من أن هذا أمر منطقي ولا ينبغي تجاوزه حتى مع تشغيل الوضع المحدد؟

CyrusNajmabadi ، هذا لن يتجاوز (لأن Int64.Max = Int32.Max * Int32.Max وثوابتك أصغر بكثير من ذلك) لكنك تخفي البت العالي إلى الصفر ، لذا فهي فقط تجزئة 31 بت. هل ترك البت العالي في اعتبار أنه تجاوز؟

CyrusNajmabadi hashCode هو Long يمكن أن يكون في أي مكان من 0 إلى Integer.MaxValue . لماذا أحصل على هذا؟

image

لكن لا ، لا يمكن أن تفيض بالفعل.

راجع للشغل - أفضل جعل Roslyn يضيف حزمة NuGet بدلاً من إضافة تجزئة دون المستوى الأمثل.

لكنك تقوم بإخفاء البت المرتفع إلى الصفر ، لذا فهي عبارة عن تجزئة 31 بت فقط. هل ترك البت العالي في اعتبار أنه تجاوز؟

هذه نقطة جيدة. أعتقد أنني كنت أفكر في خوارزمية أخرى كانت تستخدم uints. لذلك من أجل التحويل بأمان من الطويل إلى uint ، كنت بحاجة إلى عدم تضمين بت الإشارة. ومع ذلك ، نظرًا لأن هذه كلها حسابات موقعة ، أعتقد أنه سيكون من الجيد إخفاء 0xffffffff فقط للتأكد من أننا نحتفظ فقط بالجزء السفلي 32 بت بعد إضافة كل إدخال.

أفضل أن يضيف Roslyn حزمة NuGet بدلاً من إضافة تجزئة دون المستوى الأمثل.

يمكن للمستخدمين فعل ذلك بالفعل إذا أرادوا. يتعلق الأمر بما يجب فعله عندما لا يقوم المستخدمون بإضافة تلك التبعيات أو لا يمكنهم ذلك. يتعلق هذا أيضًا بتوفير تجزئة "جيدة بما يكفي" للمستخدمين. أي شيء أفضل من طريقة "x + y + z" الشائعة التي يتبعها الأشخاص غالبًا. ليس المقصود أن يكون "الأمثل" لأنه لا يوجد تعريف جيد لما هو "الأمثل" عندما يتعلق الأمر بالتجزئة لجميع المستخدمين. لاحظ أن النهج الذي نتخذه هنا هو الأسلوب الذي أصدره بالفعل المترجم لأنواع مجهولة. يُظهر سلوكًا جيدًا بشكل معقول بينما لا يضيف الكثير من التعقيد إلى كود المستخدم. مع مرور الوقت ، نظرًا لأن المزيد والمزيد من المستخدمين قادرون على المضي قدمًا ، يمكن أن يختفي هذا ببطء ويتم استبداله بـ HashCode.

لذلك عملت عليها قليلاً وتوصلت إلى ما يلي أعتقد أنه يعالج جميع المخاوف:

        Dim hashCode As Long = 2118541809
        hashCode = (hashCode * -1521134295 + a.GetHashCode()).GetHashCode()
        hashCode = (hashCode * -1521134295 + b.GetHashCode()).GetHashCode()
        Return CType(hashCode, Integer)

القطعة المثيرة للاهتمام هي على وجه التحديد استدعاء .GetHashCode() على قيمة int64 التي أنتجها (hashCode * -1521134295 + a.GetHashCode()) . استدعاء .GetHashCode على هذه القيمة 64 بت له خاصيتان جيدتان لاحتياجاتنا. أولاً ، يضمن أن hashCode يخزن فقط قيمة int32 القانونية فيه (مما يجعل طاقم إعادة التدوير النهائي آمنًا دائمًا). ثانيًا ، يضمن أننا لا نفقد أي معلومات قيمة في أعلى 32 بت من قيمة درجة الحرارة int64 التي نعمل معها.

CyrusNajmabadi عرض تثبيت الحزمة بالفعل هو ما كنت أسأل عنه. يحفظني من الاضطرار إلى القيام بذلك.

إذا قمت بكتابة HashCode ، ثم إذا تم توفير System.HashCode في حزمة MS nuget ، فستعرضه Roslyn.

أريده أن يولد التحميل الزائد GetHashCode غير الموجود وتثبيت الحزمة في نفس العملية.

لا أعتقد أن هذا خيار مناسب لمعظم المستخدمين. تعد إضافة التبعيات عملية ثقيلة للغاية لا ينبغي إجبار المستخدمين عليها. يمكن للمستخدمين تحديد الوقت المناسب لاتخاذ هذه الاختيارات ، وسيحترمها IDE. كان هذا هو النهج الذي اتبعناه مع جميع ميزاتنا حتى الآن ، وكان أسلوبًا صحيًا يبدو أن الناس يحبونه.

ملاحظة: ما هي حزمة nuget التي يتم تضمين واجهة برمجة التطبيقات فيها حتى نتمكن من إضافة مرجع إليها؟

التطبيق موجود في System.Private.CoreLib.dll ، لذلك قد يأتي كجزء من حزمة وقت التشغيل. العقد هو System.Runtime.dll.

نعم. إذا كان الأمر كذلك ، فيبدو أن المستخدم سيحصل على هذا إذا / عندما ينتقل إلى إطار عمل أكثر حداثة. هذا النوع من الأشياء ليس على الإطلاق خطوة سأفعلها "إنشاء يساوي + رمز التجزئة" لمشروع المستخدم.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات