Runtime: Proposal: Tambahkan System.HashCode untuk memudahkan pembuatan kode hash yang baik.

Dibuat pada 9 Des 2016  ·  182Komentar  ·  Sumber: dotnet/runtime

Pembaruan 16/06/17: Mencari sukarelawan

Bentuk API telah diselesaikan. Namun, kami masih memutuskan algoritme hash terbaik dari daftar kandidat yang akan digunakan untuk implementasi, dan kami membutuhkan seseorang untuk membantu kami mengukur throughput/distribusi setiap algoritme. Jika Anda ingin mengambil peran itu, silakan tinggalkan komentar di bawah dan @karelz akan memberikan masalah ini kepada Anda.

Pembaruan 13/06/17: Proposal diterima!

Inilah API yang disetujui oleh @terrajobst di 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();
    }
}

Teks asli dari proposal ini berikut.

Alasan

Menghasilkan kode hash yang baik seharusnya tidak memerlukan penggunaan konstanta ajaib yang jelek dan sedikit mengutak-atik kode kita. Seharusnya tidak terlalu tergoda untuk menulis implementasi GetHashCode buruk tapi ringkas seperti

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

Usul

Kita harus menambahkan tipe HashCode untuk merangkum pembuatan kode hash dan menghindari memaksa pengembang untuk mencampuradukkan detail yang berantakan. Ini proposal saya, yang didasarkan pada https://github.com/dotnet/corefx/issues/14354#issuecomment -305019329, dengan beberapa revisi kecil.

// 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();
    }
}

Perkataan

Lihat @terrajobst 's komentar di https://github.com/dotnet/corefx/issues/14354#issuecomment -305.019.329 untuk tujuan API ini; semua ucapannya valid. Saya ingin menunjukkan yang ini secara khusus, namun:

  • API tidak perlu menghasilkan hash kriptografi yang kuat
  • API akan memberikan kode hash "a", tetapi tidak menjamin algoritma kode hash tertentu. Ini memungkinkan kita untuk menggunakan algoritma yang berbeda nanti atau menggunakan algoritma yang berbeda pada arsitektur yang berbeda.
  • API akan menjamin bahwa dalam proses tertentu nilai yang sama akan menghasilkan kode hash yang sama. Instance yang berbeda dari aplikasi yang sama kemungkinan akan menghasilkan kode hash yang berbeda karena pengacakan. Ini memungkinkan kami untuk memastikan bahwa konsumen tidak dapat mempertahankan nilai hash dan secara tidak sengaja mengandalkannya agar stabil di seluruh proses (atau lebih buruk lagi, versi platform).
api-approved area-System.Numerics up-for-grabs

Komentar yang paling membantu

Keputusan

  • Kita harus menghapus semua metode AddRange karena skenarionya tidak jelas. Array agak tidak mungkin muncul sangat sering. Dan begitu array yang lebih besar terlibat, pertanyaannya adalah apakah perhitungan harus di-cache. Melihat for loop di sisi panggilan memperjelas bahwa Anda perlu memikirkannya.
  • Kami juga tidak ingin menambahkan kelebihan IEnumerable ke AddRange karena mereka akan dialokasikan.
  • Kami tidak berpikir kami membutuhkan kelebihan untuk Add yang membutuhkan string dan StringComparison . Ya, itu mungkin lebih efisien daripada menelepon melalui IEqualityComparer , tetapi kami dapat memperbaikinya nanti.
  • Kami pikir menandai GetHashCode sebagai usang dengan kesalahan adalah ide yang bagus, tetapi kami akan melangkah lebih jauh dan juga bersembunyi dari IntelliSense.

Ini meninggalkan kita dengan:

```C#
// Akan tinggal di rakitan inti
// .NET Framework : mscorlib
// .NET Core : System.Runtime / System.Private.CoreLib
Sistem ruang nama
{
struct publik HashCode
{
Gabungkan int statis publik(nilai T1);
Gabungkan int statis publik(nilai T1, nilai T2);
Gabungkan int statis publik(nilai T1, nilai T2, nilai T33);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T44);
Gabungkan int statis publik(nilai T1, nilai T22, nilai T33, nilai T4, nilai T55);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66);
Gabungkan int statis publik(nilai T1, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66, nilai T7);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66, nilai T77, nilai T88);

    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();
}

}
```

Semua 182 komentar

Proposal: tambahkan dukungan pengacakan hash

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

T atau Type type diperlukan untuk mendapatkan hash acak yang sama untuk tipe yang sama.

Proposal: tambahkan dukungan untuk koleksi

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);

Saya pikir tidak perlu kelebihan Combine(_field1, _field2, _field3, _field4, _field5) karena kode berikutnya HashCode.Empty.Combine(_field1).Combine(_field2).Combine(_field3).Combine(_field4).Combine(_field5); harus dioptimalkan sebaris tanpa Menggabungkan panggilan.

@AlexRadch

Proposal: tambahkan dukungan untuk koleksi

Ya, itu adalah bagian dari rencana akhir saya untuk proposal ini. Saya pikir penting untuk fokus pada bagaimana kita ingin API terlihat seperti sebelum kita menambahkan metode tersebut.

Dia ingin menggunakan algoritma yang berbeda, seperti hash Marvin32 yang digunakan untuk string di coreclr. Ini akan membutuhkan perluasan ukuran HashCode menjadi 8 byte.

Bagaimana dengan memiliki tipe Hash32 dan Hash64 yang secara internal akan menyimpan data senilai 4 atau 8 byte? Dokumentasikan pro/kontra masing-masing. Hash64 bagus untuk X, tetapi berpotensi lebih lambat. Hash32 menjadi lebih cepat, tetapi berpotensi tidak didistribusikan (atau apa pun pengorbanannya sebenarnya).

Dia ingin mengacak benih hash, jadi hash tidak akan bersifat deterministik.

Ini sepertinya perilaku yang berguna. Tapi saya bisa melihat orang-orang ingin mengendalikan ini. Jadi mungkin harus ada dua cara untuk membuat Hash, yang tidak membutuhkan benih (dan menggunakan benih acak) dan yang memungkinkan benih disediakan.

Catatan: Roslyn akan senang jika ini bisa disediakan di Fx. Kami menambahkan fitur untuk mengeluarkan GetHashCode bagi pengguna. Saat ini, ini menghasilkan kode seperti:

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; }

Ini bukan pengalaman yang luar biasa, dan ini memperlihatkan banyak konsep buruk. Kami akan senang memiliki Hash.Whatever API yang dapat kami panggil sebagai gantinya.

Terima kasih!

Bagaimana dengan MurmurHash? Ini cukup cepat dan memiliki sifat hashing yang sangat baik. Ada juga dua implementasi yang berbeda, satu yang mengeluarkan hash 32-bit dan yang lain mengeluarkan hash 128-bit.

Ada juga implementasi vektor untuk format 32-bit.dan 128-bit.

@tannergooding MurmurHash cepat, tetapi tidak aman, dari suara posting blog ini .

@jkotas , apakah ada pekerjaan di JIT untuk menghasilkan kode yang lebih baik untuk >4-byte struct pada 32-bit sejak diskusi kami tahun lalu? Juga, apa pendapat Anda tentang proposal @CyrusNajmabadi :

Bagaimana dengan memiliki tipe Hash32 dan Hash64 yang secara internal akan menyimpan data senilai 4 atau 8 byte? Dokumentasikan pro/kontra masing-masing. Hash64 bagus untuk X, tetapi berpotensi lebih lambat. Hash32 menjadi lebih cepat, tetapi berpotensi tidak didistribusikan (atau apa pun pengorbanannya sebenarnya).

Saya masih berpikir jenis ini akan sangat berharga untuk ditawarkan kepada pengembang dan akan sangat bagus untuk memilikinya di 2.0.

@jamesqo , saya tidak berpikir implementasi ini perlu aman secara kriptografis (itulah tujuan dari fungsi hashing kriptografis eksplisit).

Juga, artikel itu berlaku untuk Murmur2. Masalah telah diselesaikan dalam algoritma Murmur3.

JIT sekitar menghasilkan kode yang lebih baik untuk struct> 4-byte pada 32-bit sejak diskusi kami tahun lalu

Saya tidak menyadari apapun.

apa pendapat Anda tentang proposal @CyrusNajmabadi ?

Jenis kerangka kerja harus berupa pilihan sederhana yang berfungsi dengan baik untuk 95%+ kasus. Mereka mungkin bukan yang tercepat, tapi tidak apa-apa. Meminta Anda untuk memilih antara Hash32 dan Hash64 bukanlah pilihan yang mudah.

Itu baik-baik saja dengan saya. Tetapi bisakah kita setidaknya memiliki solusi yang cukup baik untuk 95% kasus itu? Saat ini tidak ada apa-apa... :-/

kode hash = kode hash * -1521134295 + EqualityComparer.Default.GetHashCode(ini.s);

@CyrusNajmabadi Mengapa Anda memanggil EqualityComparer di sini, dan bukan hanya this.s.GetHashCode()?

Untuk non-struct: agar kita tidak perlu memeriksa null.

Ini mendekati apa yang kami hasilkan untuk tipe anonim di belakang layar juga. Saya mengoptimalkan kasus nilai non-null yang diketahui untuk menghasilkan kode yang akan lebih menyenangkan bagi pengguna. Tetapi alangkah baiknya jika hanya memiliki API bawaan untuk ini.

Panggilan ke EqualityComparer.Default.GetHashCode seperti 10x+ lebih mahal daripada memeriksa nol... .

Panggilan ke EqualityComparer.Default.GetHashCode seperti 10x+ lebih mahal daripada memeriksa nol..

Kedengarannya seperti masalah. jika saja ada API kode hash yang bagus, kami dapat memanggil Fx yang dapat saya tunda :)

(juga, kami memiliki masalah itu dalam jenis anonim kami karena itulah yang kami hasilkan di sana juga).

Tidak yakin apa yang kami lakukan untuk tupel, tapi saya kira itu serupa.

Tidak yakin apa yang kami lakukan untuk tupel, tapi saya kira itu serupa.

System.Tuple melewati EqualityComparer<Object>.Default karena alasan historis. System.ValueTuple memanggil Object.GetHashCode dengan cek nol - https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/ValueTuple.cs#L809.

Oh tidak. Sepertinya Tuple hanya bisa menggunakan "HashHelpers". Bisakah itu diekspos sehingga pengguna bisa mendapatkan manfaat yang sama?

Besar. Saya senang melakukan hal serupa. Saya mulai dari tipe anonim kami karena saya pikir itu adalah praktik terbaik yang masuk akal. Jika tidak, tidak apa-apa. :)

Tapi bukan karena itu aku di sini. Saya di sini untuk mendapatkan beberapa sistem yang benar-benar menggabungkan hash secara efektif. Jika/ketika itu dapat diberikan, kami akan dengan senang hati beralih ke panggilan itu alih-alih melakukan hardcoding dalam angka acak dan menggabungkan nilai hash sendiri.

Apa bentuk API yang menurut Anda paling cocok untuk kode yang dihasilkan kompiler?

Secara harfiah salah satu solusi 32bit yang disajikan sebelumnya akan baik-baik saja dengan saya. Heck, solusi 64bit baik-baik saja dengan saya. Hanya semacam API yang bisa Anda dapatkan yang mengatakan "saya dapat menggabungkan hash dengan cara yang masuk akal dan menghasilkan hasil yang terdistribusi secara wajar".

Saya tidak dapat mendamaikan pernyataan ini:

Kami memiliki struct HashCode yang tidak dapat diubah yang berukuran 4 byte. Itu memiliki metode Combine(int), yang mencampur kode hash yang disediakan dengan kode hashnya sendiri melalui algoritme mirip DJBX33X, dan mengembalikan HashCode baru.

@jkotas tidak berpikir algoritma seperti DJBX33X cukup kuat.

Dan

Jenis kerangka kerja harus berupa pilihan sederhana yang berfungsi dengan baik untuk 95%+ kasus.

Bisakah kita tidak membuat hash akumulasi 32bit sederhana yang berfungsi cukup baik untuk 95% kasus? Kasus apa saja yang tidak ditangani dengan baik di sini, dan mengapa menurut kami kasusnya berada di 95% kasus?

@jkotas , apakah kinerja sangat penting untuk tipe ini? Saya pikir rata-rata hal-hal seperti pencarian hashtable dan ini akan memakan waktu lebih lama daripada beberapa salinan struct. Jika ternyata menjadi hambatan, apakah masuk akal untuk meminta tim JIT untuk mengoptimalkan salinan struct 32-bit setelah API dirilis sehingga mereka memiliki beberapa insentif, daripada memblokir API ini pada saat tidak ada yang bekerja untuk mengoptimalkan salinan?

Bisakah kita tidak membuat hash akumulasi 32bit sederhana yang berfungsi cukup baik untuk 95% kasus?

Kami telah dibakar sangat parah secara default 32bit mengumpulkan hash untuk string, dan itulah mengapa Marvin hash untuk string di .NET Core - https://github.com/dotnet/corert/blob/87e58839d6629b5f90777f886a2f52d7a99c076f/src/System.Private.CoreLib src/System/Marvin.cs#L25. Saya tidak berpikir kami ingin mengulangi kesalahan yang sama di sini.

@jkotas , apakah kinerja sangat penting untuk tipe ini?

Saya tidak berpikir kinerja sangat penting. Karena sepertinya API ini akan digunakan oleh kode kompiler yang dibuat secara otomatis, saya pikir kita harus lebih memilih kode yang dihasilkan lebih kecil daripada tampilannya. Pola tidak lancar adalah kode yang lebih kecil.

Kami telah terbakar sangat parah secara default 32bit mengumpulkan hash untuk string

Itu tidak tampak seperti kasus 95%. Kita berbicara tentang pengembang normal yang hanya menginginkan hash "cukup baik" untuk semua jenis di mana mereka melakukan sesuatu secara manual hari ini.

Karena sepertinya API ini akan digunakan oleh kode kompiler yang dibuat secara otomatis, saya pikir kita harus lebih memilih kode yang dihasilkan lebih kecil daripada tampilannya. Pola tidak lancar adalah kode yang lebih kecil.

Ini tidak untuk digunakan oleh kompiler Roslyn. Ini untuk digunakan oleh Roslyn IDE ketika kami membantu pengguna menghasilkan GetHashCodes untuk tipe mereka. Ini adalah kode yang akan dilihat dan harus dipelihara oleh pengguna, dan memiliki sesuatu yang masuk akal seperti:

```c#
kembalikan 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;

Maksud saya, kita sudah memiliki kode ini di Fx:

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

Kami pikir itu cukup baik untuk tupel. Tidak jelas bagi saya mengapa akan menjadi masalah untuk membuatnya tersedia untuk pengguna yang menginginkannya untuk tipe mereka sendiri.

Catatan: kami bahkan mempertimbangkan untuk melakukan ini di roslyn:

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

Tapi sekarang Anda memaksa orang untuk menghasilkan struct (berpotensi besar) hanya untuk mendapatkan semacam perilaku hashing default yang masuk akal.

Kita berbicara tentang pengembang normal yang hanya menginginkan hash "cukup baik" untuk semua jenis di mana mereka melakukan sesuatu secara manual hari ini.

Hash string asli adalah hash "cukup baik" yang bekerja dengan baik untuk pengembang normal. Tetapi kemudian ditemukan bahwa server web ASP.NET rentan terhadap serangan DoS karena mereka cenderung menyimpan barang yang diterima di hashtables. Jadi hash "cukup baik" pada dasarnya berubah menjadi masalah keamanan yang buruk.

Kami pikir itu cukup baik untuk tupel

Tidak perlu. Kami membuat langkah mundur untuk tupel untuk membuat kode hash acak yang memberi kami opsi untuk memodifikasi algoritme nanti.

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

Ini terlihat masuk akal bagi saya.

Saya tidak mengerti positoin Anda. Anda sepertinya mengatakan dua hal:

Hash string asli adalah hash "cukup baik" yang bekerja dengan baik untuk pengembang normal. Tetapi kemudian ditemukan bahwa server web ASP.NET rentan terhadap serangan DoS karena mereka cenderung menyimpan barang yang diterima di hashtables. Jadi hash "cukup baik" pada dasarnya berubah menjadi masalah keamanan yang buruk.

Oke, jika itu masalahnya, mari berikan kode hash yang bagus untuk orang yang memiliki masalah keamanan/DoS.

Jenis kerangka kerja harus berupa pilihan sederhana yang berfungsi dengan baik untuk 95%+ kasus.

Oke, jika itu masalahnya, maka mari berikan kode hash yang cukup baik untuk 95% kasus. Orang yang memiliki masalah keamanan/DoS dapat menggunakan formulir khusus yang didokumentasikan untuk tujuan itu.

Tidak perlu. Kami membuat langkah mundur untuk tupel untuk membuat kode hash acak yang memberi kami opsi untuk memodifikasi algoritme nanti.

Oke. Bisakah kami mengeksposnya sehingga pengguna dapat menggunakan mekanisme yang sama.

--
Saya benar-benar berjuang di sini karena sepertinya kami mengatakan "karena kami tidak dapat membuat solusi universal, setiap orang harus menggulungnya sendiri". Itu sepertinya salah satu tempat terburuk untuk dikunjungi. Karena tentu saja sebagian besar pelanggan kami tidak berpikir untuk menggulirkan 'marvin hash' mereka sendiri untuk masalah DoS. Mereka hanya menambahkan, memperbaiki, atau menggabungkan hash bidang dengan buruk menjadi satu hash akhir.

Jika kita peduli dengan kasus 95%, maka kita harus membuat hash yang cukup bagus secara umum. JIKA kami peduli dengan kasus 5%, kami dapat menyediakan solusi khusus untuk itu.

Ini terlihat masuk akal bagi saya.

Hebat :) Bisakah kita mengekspos:

```c#
namespace System.Numerics.Hashing
{
HashHelpers kelas statis internal
{
public static readonly int RandomSeed = new Random().Next(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);

Ini akan memiliki manfaat menjadi "cukup baik" untuk sebagian besar kasus, sementara juga mengarahkan orang ke jalur yang baik untuk menginisialisasi dengan nilai acak sehingga mereka tidak mengambil ketergantungan pada hash non-acak.

Orang yang memiliki masalah keamanan/DoS dapat menggunakan formulir khusus yang didokumentasikan untuk tujuan itu.

Setiap aplikasi ASP.NET memiliki masalah keamanan/DoS.

Hebat :) Bisakah kita mengekspos:

Ini berbeda dari apa yang saya katakan adalah wajar.

Apa pendapat Anda tentang https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.HashCodeCombiner.Sources/HashCodeCombiner.cs . Ini adalah apa yang digunakan di ASP.NET secara internal di sejumlah tempat saat ini, dan itulah yang akan sangat saya senangi (kecuali bahwa fungsi penggabungan harus lebih kuat - detail implementasi yang dapat terus kami ubah).

@jkotas saya dengar itu :p

Jadi masalahnya di sini adalah pengembang tidak tahu kapan mereka rentan terhadap serangan DoS, karena itu bukan sesuatu yang mereka sukai, itulah sebabnya kami mengganti string untuk menggunakan Marvin32.

Kita seharusnya tidak mengatakan "95% kasus tidak penting", karena kita tidak memiliki cara untuk membuktikannya, dan kita harus berhati-hati bahkan ketika itu memiliki biaya kinerja. Jika Anda akan menjauh dari itu maka implementasi kode hash perlu ditinjau Crypto Board, bukan hanya kami yang memutuskan "Ini terlihat cukup bagus".

Setiap aplikasi ASP.NET memiliki masalah keamanan/DoS.

Oke. Jadi bagaimana Anda menangani masalah hari ini bahwa tidak ada yang membantu dengan kode hash, dan dengan demikian kemungkinan melakukan hal-hal dengan buruk? Jelas itu dapat diterima untuk memiliki keadaan dunia itu. Jadi apa yang dirugikan dengan menyediakan sistem hashing yang masuk akal yang kemungkinan berkinerja lebih baik daripada apa yang dilakukan orang saat ini?

karena kami tidak memiliki cara untuk membuktikannya, dan kami harus berhati-hati meskipun harus mengorbankan kinerja

Jika Anda tidak memberikan sesuatu, orang akan terus melakukan hal-hal buruk. Penolakan terhadap "cukup baik" karena tidak ada yang sempurna hanya berarti buruknya status quo yang kita miliki saat ini.

Setiap aplikasi ASP.NET memiliki masalah keamanan/DoS.

Bisakah Anda menjelaskan ini? Seperti yang saya pahami, Anda memiliki kekhawatiran DoS jika Anda menerima input sewenang-wenang dan kemudian menyimpannya di beberapa struktur data yang berkinerja buruk jika input dapat dibuat secara khusus. Oke, saya mengerti bagaimana itu menjadi perhatian dengan string yang didapat dalam skenario web yang berasal dari pengguna.

Jadi bagaimana itu berlaku untuk sisa jenis di luar sana yang tidak digunakan dalam skenario ini?

Kami memiliki set jenis ini:

  1. Jenis pengguna yang harus aman untuk DoS. Saat ini kami tidak menyediakan apa pun untuk membantu, jadi kami sudah berada di tempat yang buruk karena orang mungkin tidak melakukan hal yang benar.
  2. Jenis pengguna yang tidak perlu DoS aman. Saat ini kami tidak menyediakan apa pun untuk membantu, jadi kami sudah berada di tempat yang buruk karena orang mungkin tidak melakukan hal yang benar.
  3. Jenis kerangka kerja yang harus aman untuk DoS. Saat ini kami telah membuatnya aman untuk DoS, tetapi melalui API kami tidak mengeksposnya.
  4. Jenis kerangka kerja yang tidak perlu aman untuk DoS. Saat ini kami telah memberi mereka hash, tetapi melalui API kami tidak mengekspos.

Pada dasarnya, kami menganggap kasus-kasus ini penting, tetapi tidak cukup penting untuk benar-benar memberikan solusi kepada pengguna untuk menangani '1' atau '2'. Karena kami khawatir solusi untuk '2' tidak akan baik untuk '1', kami bahkan tidak akan menyediakannya sejak awal. Dan jika kita bahkan tidak mau memberikan solusi untuk '1' rasanya kita berada dalam posisi yang sangat aneh. Kami khawatir tentang DoSing dan ASP, tetapi tidak cukup khawatir untuk benar-benar membantu orang. Dan karena kami tidak akan membantu orang dengan itu, kami bahkan tidak bersedia membantu kasus non-DoS.

--

Jika kedua kasus ini penting (yang saya bersedia terima) lalu mengapa tidak memberikan dua API saja? Dokumentasikan. Buat mereka jelas untuk apa mereka. Jika orang menggunakannya dengan benar, bagus . Jika orang tidak menggunakannya dengan benar, itu masih baik-baik saja. Setelah semua, mereka kemungkinan besar tidak melakukan hal-hal dengan benar hari ini anyways, jadi bagaimana hal-hal lebih buruk?

Apa yang Anda pikirkan

Saya tidak punya pendapat satu atau lain cara. Jika itu adalah API yang dapat digunakan pelanggan yang berkinerja dapat diterima dan yang menyediakan API sederhana dengan kode yang jelas di pihak mereka, maka saya pikir itu baik-baik saja.

Saya pikir akan menyenangkan untuk memiliki formulir statis sederhana yang menangani kasus 99% keinginan untuk menggabungkan satu set bidang/properti secara teratur. Sepertinya hal seperti itu bisa ditambahkan ke tipe ini dengan cukup sederhana.

Saya pikir akan menyenangkan untuk memiliki bentuk statis sederhana

Setuju.

Saya pikir akan menyenangkan untuk memiliki formulir statis sederhana yang menangani kasus 99% keinginan untuk menggabungkan satu set bidang/properti secara teratur. Sepertinya hal seperti itu bisa ditambahkan ke tipe ini dengan cukup sederhana.

Setuju.

Saya bersedia bertemu Anda berdua di tengah jalan yang satu ini karena saya benar-benar ingin melihat semacam API datang. @jkotas Saya masih tidak mengerti Anda menentang menambahkan API berbasis instance yang tidak dapat diubah; pertama Anda mengatakan itu karena salinan 32-bit akan lambat, kemudian karena API yang bisa berubah akan lebih singkat (yang tidak benar; h.Combine(a).Combine(b) (versi tidak berubah) lebih pendek dari h.Combine(a); h.Combine(b); (bisa berubah Versi: kapan)).

Yang mengatakan, saya bersedia untuk kembali ke:

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
}

Apakah ini tampak masuk akal?

Saya tidak dapat mengedit posting saya sekarang, tetapi saya baru menyadari bahwa tidak semua metode dapat menerima T. Dalam hal ini, kami hanya dapat memiliki 8 kelebihan yang menerima semua int dan memaksa pengguna untuk memanggil GetHashCode.

Jika kedua kasus ini penting (yang saya bersedia terima) lalu mengapa tidak memberikan dua API saja? Dokumentasikan. Buat mereka jelas untuk apa mereka. Jika orang menggunakannya dengan benar, bagus. Jika orang tidak menggunakannya dengan benar, itu masih baik-baik saja. Lagi pula, mereka sepertinya tidak melakukan sesuatu dengan benar hari ini, jadi bagaimana keadaannya menjadi lebih buruk?

Karena orang tidak menggunakan barang dengan benar saat berada di sana. Mari kita ambil contoh sederhana, XSS. Sejak awal bahkan formulir web memiliki kemampuan untuk mengkodekan keluaran HTML. Namun pengembang tidak tahu risikonya, tidak tahu cara melakukannya dengan benar, dan baru mengetahui saat sudah terlambat, aplikasi mereka dipublikasikan, dan oops, sekarang cookie auth mereka telah dicabut.

Memberi orang pilihan keamanan mengasumsikan mereka

  1. Tahu tentang masalahnya.
  2. Pahami apa risikonya.
  3. Dapat mengevaluasi risiko tersebut.
  4. Dapat dengan mudah menemukan hal yang benar untuk dilakukan.

Asumsi tersebut umumnya tidak berlaku untuk sebagian besar pengembang, mereka hanya mengetahui masalahnya ketika sudah terlambat. Pengembang tidak menghadiri konferensi keamanan, mereka tidak membaca kertas putih dan mereka tidak memahami solusinya. Jadi dalam skenario ASP.NET HashDoS kami membuat pilihan untuk mereka, kami melindungi mereka secara default, karena itu adalah hal yang benar untuk dilakukan, dan memiliki dampak terbesar. Namun kami hanya menerapkannya ke string, dan itu membuat orang-orang yang membuat kelas khusus dari input pengguna di tempat yang buruk. Kita harus melakukan hal yang benar, dan membantu melindungi pelanggan itu sekarang, dan menjadikannya default, memiliki lubang kesuksesan, bukan kegagalan. Desain API untuk keamanan terkadang bukan tentang pilihan, tetapi tentang membantu pengguna apakah mereka mengetahuinya atau tidak.

Seorang pengguna selalu dapat membuat hash yang berfokus pada non-keamanan; jadi diberikan dua pilihan

  1. Utilitas hash default adalah non-keamanan sadar; pengguna dapat membuat fungsi hash yang sadar keamanan
  2. Utilitas hash default adalah sadar keamanan; pengguna dapat membuat fungsi hash sadar non-keamanan khusus

Maka yang kedua mungkin lebih baik; dan apa yang disarankan tidak akan memiliki dampak kinerja penuh pada hash kripto; jadi itu membuat kompromi yang baik?

Salah satu pertanyaan yang sedang berjalan di utas ini adalah algoritma mana yang sempurna untuk semua orang. Saya pikir aman untuk mengatakan tidak ada satu algoritma yang sempurna. Namun, saya tidak berpikir itu akan menghentikan kami untuk menyediakan sesuatu yang lebih baik daripada kode seperti yang ditunjukkan oleh

Saya ingin mengusulkan beberapa opsi untuk mengatasi masalah "algoritma terbaik":

  1. Pilihan Eksplisit: Saya berencana untuk mengirimkan proposal API segera untuk rangkaian hash non-kriptografis (mungkin xxHash, Marvin32, dan SpookyHash misalnya). API semacam itu memiliki penggunaan yang sedikit berbeda dari jenis HashCode atau HashCodeHelper, tetapi demi diskusi, anggaplah kita dapat mengatasi perbedaan tersebut. Jika kita menggunakan API itu untuk GetHashCode:

    • Kode yang dihasilkan secara eksplisit tentang apa yang dilakukannya -- jika Roslyn menghasilkan Marvin32.Create(); , ini memungkinkan pengguna yang kuat mengetahui apa yang diputuskan untuk dilakukan dan mereka dapat dengan mudah mengubahnya ke algoritme lain di suite jika mereka mau.

    • Itu berarti kita tidak perlu khawatir tentang melanggar perubahan. Jika kita memulai dengan algoritme non-pengacakan/entropi buruk/lambat, kita cukup memperbarui Roslyn untuk mulai menghasilkan sesuatu yang lain dalam kode baru. Kode lama akan tetap menggunakan hash lama dan kode baru akan menggunakan hash baru. Pengembang (atau perbaikan kode Roslyn) dapat mengubah kode lama jika mereka mau.

    • Kelemahan terbesar yang dapat saya pikirkan adalah bahwa beberapa pengoptimalan yang mungkin kita inginkan untuk GetHashCode dapat merugikan algoritme lain. Misalnya, sementara keadaan internal 32-bit bekerja dengan baik dengan struct yang tidak dapat diubah, keadaan internal 256-bit di (katakanlah) CityHash mungkin membuang banyak waktu untuk menyalin.

  1. Pengacakan: Mulailah dengan algoritme yang diacak dengan benar (kode yang ditunjukkan @CyrusNajmabadi dengan nilai awal acak tidak dihitung karena kemungkinan menghilangkan keacakan). Ini memastikan bahwa kami dapat mengubah implementasi tanpa masalah kompatibilitas. Kami masih harus sangat sensitif tentang perubahan kinerja jika kami mengubah algoritme. Namun itu juga akan menjadi potensi keuntungan karena kami dapat membuat pilihan per arsitektur (atau bahkan per perangkat). Misalnya, situs ini menunjukkan bahwa xxHash tercepat di Mac x64 sementara SpookyHash tercepat di Xbox dan iPhone. Jika kita melakukan rute ini dengan maksud untuk mengubah algoritme di beberapa titik, kita mungkin perlu memikirkan untuk merancang API yang masih memiliki kinerja yang wajar jika ada status internal 64+ bit.

CC @bartonjs , @terrajobst

@morganbr Tidak ada satu algoritme yang sempurna, tetapi saya pikir memiliki beberapa algoritme, yang sebagian besar bekerja dengan cukup baik, diekspos menggunakan API yang sederhana dan mudah dipahami adalah hal paling berguna yang dapat dilakukan. Memiliki serangkaian algoritme selain itu, untuk penggunaan lanjutan tidak masalah. Tapi itu seharusnya bukan satu-satunya pilihan, saya tidak harus belajar siapa Marvin hanya agar saya bisa memasukkan objek saya ke dalam Dictionary .

Saya tidak perlu mempelajari siapa Marvin hanya agar saya dapat memasukkan objek saya ke dalam Kamus.

Saya suka cara Anda mengatakan itu. Saya juga suka Anda menyebutkan Kamus itu sendiri. IDictionary adalah sesuatu yang dapat memiliki banyak impls berbeda dengan segala macam kualitas yang berbeda (lihat koleksi API di banyak platform). Namun, kami masih hanya menyediakan 'Kamus' dasar yang melakukan pekerjaan yang layak secara keseluruhan, meskipun mungkin tidak unggul di setiap kategori.

Saya berpikir bahwa ini apa satu ton orang mencari di perpustakaan hashing. Sesuatu yang menyelesaikan pekerjaan, bahkan jika itu tidak sempurna untuk setiap tujuan.

@morganbr Saya pikir orang-orang sederhana menginginkan cara untuk menulis GetHashCode yang lebih baik daripada apa yang mereka lakukan hari ini (biasanya beberapa kombinasi grabbag dari operasi matematika yang mereka salin dari sesuatu di web). Jika Anda bisa memberikan impl dasar yang berjalan dengan baik, maka orang-orang akan senang. Anda kemudian dapat memiliki API di belakang layar untuk pengguna tingkat lanjut jika mereka sangat membutuhkan fungsi hashing tertentu .

Dengan kata lain, orang yang menulis kode hash hari ini tidak akan tahu atau peduli mengapa mereka menginginkan Spooky vs Marvin vs Murmur. Hanya seseorang yang memiliki kebutuhan khusus untuk salah satu kode hash tertentu yang akan mencari. Tetapi banyak orang perlu mengatakan "inilah keadaan objek saya, berikan saya cara untuk menghasilkan hash terdistribusi dengan baik yang cepat yang kemudian dapat saya gunakan dengan kamus, dan yang saya kira mencegah saya dari DOS jika saya terjadi untuk mengambil input yang tidak dipercaya dan hash dan menyimpannya".

@CyrusNajmabadi Masalahnya adalah jika kami memperluas gagasan kompatibilitas kami saat ini ke masa depan, kami menemukan bahwa setelah jenis ini dikirimkan, ia tidak akan pernah berubah (kecuali jika kami menemukan bahwa algoritme rusak parah dengan cara "itu membuat semua aplikasi dapat diserang" ).

Once dapat berargumentasi bahwa jika dimulai sebagai cara acak yang stabil, akan menjadi mudah untuk mengubah implementasinya, karena Anda tidak dapat bergantung pada nilai dari run to run. Tetapi jika beberapa tahun kemudian kami menemukan bahwa ada algoritme yang menyediakan penyeimbangan yang baik-jika-tidak-lebih baik dari ember hash dengan kinerja kasus yang lebih baik secara umum, tetapi membuat struktur yang melibatkan Daftar\

Di bawah saran Morgan adalah bahwa kode yang Anda tulis hari ini akan memiliki karakteristik kinerja yang sama secara efektif selamanya. Untuk aplikasi yang bisa menjadi lebih baik, ini sangat disayangkan. Untuk aplikasi yang akan menjadi lebih buruk, ini fantastis. Tetapi ketika kami menemukan algoritme baru, kami memeriksanya, dan kami mengubah Roslyn (dan menyarankan perubahan ke ReSharper/dll) untuk mulai menghasilkan sesuatu dengan NewAwesomeThing2019 alih-alih SomeThingThatWasConsideredAwesomeIn2018.

Apa pun kotak hitam super seperti ini hanya bisa dilakukan sekali. Dan kemudian kita terjebak dengan itu selamanya. Kemudian seseorang menulis yang berikutnya, yang memiliki kinerja rata-rata yang lebih baik, jadi ada dua implementasi kotak hitam yang Anda tidak tahu mengapa Anda memilih di antara keduanya. Dan kemudian ... dan kemudian ... .

Jadi, tentu, Anda mungkin tidak tahu mengapa Roslyn/ReSharper/etc otomatis menulis GetHashCode untuk Anda menggunakan Marvin32, atau Murmur, atau FastHash, atau kombinasi/kondisional berdasarkan IntPtr.Size. Tetapi Anda memiliki kekuatan untuk melihat ke dalamnya. Dan Anda memiliki kekuatan untuk mengubahnya pada tipe Anda nanti, saat informasi baru terungkap... tapi kami juga memberi Anda kekuatan untuk tetap sama. (Akan menyedihkan jika kita menulis ini, dan dalam 3 tahun Roslyn/ReSharper/etc secara eksplisit menghindari menyebutnya, karena algoritma baru Jauh Lebih Baik... Biasanya).

@bartonjs Apa yang membuat hashing berbeda dari semua tempat di mana .Net memberi Anda algoritma kotak hitam atau struktur data? Misalnya, pengurutan (introsort), Dictionary (rantai terpisah berbasis array), StringBuilder (daftar tertaut dari 8k potongan), sebagian besar LINQ.

Kami telah melihat lebih dalam hari ini. Mohon maaf atas keterlambatan dan bolak-baliknya masalah ini.

Persyaratan

  • Untuk siapa API-nya?

    • API tidak perlu menghasilkan hash kriptografi yang kuat

    • Tetapi: API harus cukup baik agar kita dapat menggunakannya dalam kerangka itu sendiri (misalnya di BCL dan ASP.NET)

    • Namun, ini tidak berarti bahwa kita harus menggunakan API di mana-mana. Tidak apa-apa jika ada bagian dari FX di mana kita ingin menggunakan yang khusus baik untuk risiko keamanan/DOS atau karena kinerja. Pengecualian akan selalu ada .

  • Apa properti yang diinginkan dari hash ini?

    • Semua bit dalam input digunakan

    • Hasilnya terdistribusi dengan baik

    • API akan memberikan kode hash "a", tetapi tidak menjamin algoritma kode hash tertentu. Ini memungkinkan kita untuk menggunakan algoritma yang berbeda nanti atau menggunakan algoritma yang berbeda pada arsitektur yang berbeda.

    • API akan menjamin bahwa dalam proses tertentu nilai yang sama akan menghasilkan kode hash yang sama. Instance yang berbeda dari aplikasi yang sama kemungkinan akan menghasilkan kode hash yang berbeda karena pengacakan. Ini memungkinkan kami untuk memastikan bahwa konsumen tidak dapat mempertahankan nilai hash dan secara tidak sengaja mengandalkannya agar stabil di seluruh proses (atau lebih buruk lagi, versi platform).

Bentuk API

```C#
// Akan tinggal di rakitan inti
// .NET Framework : mscorlib
// .NET Core : System.Runtime / System.Private.CoreLib
Sistem ruang nama
{
struct publik HashCode
{
Gabungkan int statis publik(nilai T1);
Gabungkan int statis publik(nilai T1, nilai T2);
Gabungkan int statis publik(nilai T1, nilai T2, nilai T33);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T44);
Gabungkan int statis publik(nilai T1, nilai T22, nilai T33, nilai T4, nilai T55);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66);
Gabungkan int statis publik(nilai T1, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66, nilai T7);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66, nilai T77, nilai T88);

    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);
}

Kasus yang lebih rumit adalah ketika pengembang perlu mengubah cara hash dihitung. Idenya adalah bahwa situs panggilan melewati hash yang diinginkan daripada objek/nilai, seperti:

```C#
Pelanggan kelas parsial publik
{
menimpa publik int GetHashCode() =>
Kode Hash. Gabungkan (
Indo,
StringComparer.OrdinalIgnoreCase.GetHashCode(Nama Depan),
StringComparer.OrdinalIgnoreCase.GetHashCode(Nama Belakang),
);
}

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();
    }
}

Langkah selanjutnya

Masalah ini akan tetap diperebutkan. Untuk mengimplementasikan API, kita perlu memutuskan algoritma mana yang akan digunakan.

@morganbr akan membuat proposal untuk kandidat yang baik. Secara umum, kami tidak ingin menulis algoritme hashing dari awal -- kami ingin menggunakan algoritme terkenal yang propertinya dipahami dengan baik.

Namun, kita harus mengukur implementasi untuk beban kerja .NET tipikal dan melihat algoritma mana yang menghasilkan hasil yang baik (throughput dan distribusi). Kemungkinan jawabannya akan berbeda menurut arsitektur CPU, jadi kami harus mempertimbangkan ini saat mengukur.

@jamesqo , apakah Anda masih tertarik bekerja di bidang ini? Dalam hal ini, harap perbarui proposal yang sesuai.

@terrajobst , kami mungkin juga ingin public static int Combine<T1>(T1 value); . Saya tahu ini terlihat sedikit lucu, tetapi itu akan memberikan cara untuk menyebarkan bit dari sesuatu dengan ruang hash input yang terbatas. Misalnya, banyak enum hanya memiliki beberapa kemungkinan hash, hanya menggunakan beberapa bit kode di bawah. Beberapa koleksi dibangun dengan asumsi bahwa hash tersebar di ruang yang lebih besar, jadi menyebarkan bit dapat membantu koleksi bekerja lebih efisien.

public void Add(string value, StrinComparison comparison);

Nit: Parameter StringComparison harus diberi nama comparisonType agar sesuai dengan penamaan yang digunakan di tempat lain StringComparison digunakan sebagai parameter.

Kriteria yang akan membantu kami memilih algoritme adalah:

  1. Apakah algoritma memiliki efek longsoran yang baik? Artinya, apakah setiap bit input memiliki peluang 50% untuk membalik setiap bit output? Situs ini memiliki studi tentang beberapa algoritma populer.
  2. Apakah algoritma cepat untuk input kecil? Karena HashCode.Combine umumnya akan menangani 8 atau lebih sedikit int, waktu startup mungkin lebih penting daripada throughput. Situs ini memiliki kumpulan data yang menarik untuk memulai. Di sinilah kita mungkin memerlukan jawaban yang berbeda untuk arsitektur yang berbeda atau pivot lainnya (OS, AoT vs JIT, dll).

Apa yang benar-benar ingin kami lihat adalah angka kinerja untuk kandidat yang ditulis dalam C# sehingga kami cukup yakin bahwa karakteristik mereka akan bertahan untuk .NET. Jika Anda menulis kandidat dan kami tidak memilihnya untuk ini, itu akan tetap menjadi pekerjaan yang berguna setiap kali saya benar-benar mendapatkan proposal API bersama untuk API hash non-kriptografi.

Berikut adalah beberapa kandidat yang menurut saya layak untuk dievaluasi (tetapi jangan ragu untuk mengusulkan yang lain):

  • Marvin32 (kami sudah memiliki implementasi C# di sini ). Kami tahu ini cukup cepat untuk String.GetHashCode dan kami yakin ini tahan terhadap HashDoS
  • xxHash32 (Algoritme tercepat di x86 di sini yang memiliki kualitas terbaik menurut SMHasher)
  • FarmHash (Tercepat di x64 di sini . Saya belum menemukan indikator kualitas yang baik untuk itu. Yang ini mungkin sulit untuk ditulis dalam C #)
  • xxHash64 (dipotong menjadi 32 bit) (Ini bukan pemenang kecepatan yang jelas, tetapi mungkin mudah dilakukan jika kita sudah memiliki xxHash32)
  • SpookyHash (Cenderung bekerja dengan baik pada kumpulan data yang lebih besar)

Malu metode Add tidak dapat memiliki tipe pengembalian ref HashCode dan mengembalikan ref this sehingga dapat digunakan dengan lancar,

Apakah pengembalian readonly ref mengizinkan ini? /cc @jaredpar @VSadov

PERINGATAN: Jika ada yang memilih implementasi hash dari basis kode yang ada di suatu tempat di internet, harap simpan tautan ke sumbernya dan periksa lisensinya (kami juga harus melakukannya).

Jika lisensi tidak kompatibel, kami mungkin perlu menulis algoritme dari awal.

IMO, menggunakan metode Add seharusnya sangat jarang. Ini akan terjadi untuk skenario yang sangat maju, dan kebutuhan untuk bisa 'fasih' tidak akan benar-benar ada.

Untuk kasus penggunaan umum untuk 99% dari semua kasus kode pengguna, seseorang harus dapat menggunakan => HashCode.Combine(...) dan baik-baik saja.

@morganbr

kita mungkin juga ingin public static int Combine<T1>(T1 value); . Saya tahu ini terlihat sedikit lucu, tetapi itu akan memberikan cara untuk menyebarkan bit dari sesuatu dengan ruang hash input terbatas

Masuk akal. Saya telah menambahkannya.

@justinvp

Nit: Parameter StringComparison harus diberi nama comparisonType agar sesuai dengan penamaan yang digunakan di tempat lain StringComparison digunakan sebagai parameter.

Tetap.

@CyrusNajmabadi

IMO, menggunakan metode Add seharusnya sangat jarang. Ini akan terjadi untuk skenario yang sangat maju, dan kebutuhan untuk bisa 'fasih' tidak akan benar-benar ada.

Sepakat.

@benaadams - re: ref mengembalikan this dari Add - tidak, this tidak dapat dikembalikan oleh ref dalam metode struct karena dapat berupa rValue atau temp.

```C#
ref var r = (baru T()).ReturnsRefThis();

// r merujuk ke beberapa variabel di sini. Yang mana? Apa ruang lingkup/seumur hidup?
r = Sesuatu Yang Lain();
```

Jika ini berguna untuk tujuan perbandingan, beberapa tahun yang lalu saya mem-porting fungsi hash lookup3 Jenkins ( C source ) ke C# here .

Saya ingin tahu tentang koleksi:

@terrajobst

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

Mengapa ada kelebihan untuk array, tetapi tidak untuk koleksi umum (yaitu IEnumerable<T> )?

Juga, bukankah akan membingungkan bahwa HashCode.Combine(array) dan hashCode.Add((object)array) berperilaku satu arah (gunakan kesetaraan referensi) dan hashCode.Add(array) berperilaku dengan cara lain (menggabungkan kode hash dari nilai-nilai di susunan)?

@CyrusNajmabadi

Untuk kasus penggunaan umum untuk 99% dari semua kasus kode pengguna, seseorang harus dapat menggunakan => HashCode.Combine(...) dan baik-baik saja.

Jika tujuannya benar-benar untuk dapat menggunakan Combine dalam 99% kasus penggunaan (dan bukan, katakanlah, 80 %), maka seharusnya Combine entah bagaimana mendukung koleksi hashing berdasarkan nilai dalam koleksi? Mungkin harus ada metode terpisah yang melakukan itu (baik metode ekstensi atau metode statis pada HashCode )?

Jika Add adalah skenario kekuatan, haruskah kita menganggap pengguna harus memilih antara Object.GetHashCode dan menggabungkan elemen individual dari koleksi? Jika itu akan membantu, kami dapat mempertimbangkan untuk mengganti nama versi array (dan potensi IEnumerable). Sesuatu seperti:
c# public void AddEnumerableHashes<T>(IEnumerable<T> enumerable); public void AddEnumerableHashes<T>(T[] array); public void AddEnumerableHashes<T>(T[] array, int index, int length);
Saya ingin tahu apakah kita juga membutuhkan kelebihan dengan IEqualityComparers.

Proposal: Buat struct pembangun mengimplementasikan IEnumerable untuk mendukung sintaks penginisialisasi koleksi:

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

Ini jauh lebih elegan daripada memanggil Add() dengan tangan (khususnya, Anda tidak memerlukan variabel sementara), dan masih tidak memiliki alokasi.

keterangan lebih lanjut

@SLaks Mungkin sintaks yang lebih bagus itu bisa menunggu https://github.com/dotnet/csharplang/issues/455 (dengan asumsi proposal itu memiliki dukungan), sehingga HashCode tidak perlu mengimplementasikan IEnumerable palsu

Kami memutuskan untuk tidak menimpa GetHashCode() untuk menghasilkan kode hash karena ini akan aneh, baik dari segi penamaan maupun dari sudut pandang perilaku (GetHashCode() harus mengembalikan kode hash objek, bukan yang sedang dihitung).

Saya merasa aneh bahwa GetHashCode tidak akan mengembalikan kode hash yang dihitung. Saya pikir ini akan membingungkan pengembang. Misalnya, @SLaks sudah menggunakannya dalam proposalnya alih-alih menggunakan ToHashCode .

@justinvp Jika GetHashCode() tidak akan mengembalikan kode hash yang dihitung, mungkin harus ditandai [Obsolete] dan [EditorBrowsable(Never)] .

Di sisi lain, saya tidak melihat salahnya mengembalikan kode hash yang dihitung.

@terrajobst

Kami memutuskan untuk tidak mengganti GetHashCode() untuk menghasilkan kode hash karena ini akan aneh, baik dari segi penamaan maupun dari sudut pandang perilaku ( GetHashCode() harus mengembalikan kode hash objek, bukan yang sedang dihitung).

Ya, GetHashCode() harus mengembalikan kode hash objek, tetapi apakah ada alasan mengapa kedua kode hash harus berbeda? Itu masih benar, karena dua contoh HashCode dengan status internal yang sama akan mengembalikan nilai yang sama dari GetHashCode() .

@terrajobst Saya baru saja melihat komentar Anda. Maafkan saya atas keterlambatan balasan, saya lambat melihat notifikasi karena saya pikir itu hanya akan lebih bolak-balik yang tidak menuju ke mana-mana. Senang melihat bukan itu masalahnya! :tada:

Saya akan senang untuk mengambil ini dan melakukan pengukuran throughput/distribusi (saya berasumsi itulah yang Anda maksud dengan "tertarik untuk bekerja di area ini"). Beri saya waktu sebentar untuk menyelesaikan membaca semua komentar di sini.

@terrajobst

Bisakah kita berubah?

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);

ke

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);

? Saya mengganti nama Add -> AddRange untuk menghindari perilaku yang disebutkan @svick . Saya menghapus kelebihan byte karena kami dapat berspesialisasi menggunakan typeof(T) == typeof(byte) di dalam metode jika kami perlu melakukan sesuatu yang spesifik terhadap byte. Juga, saya mengubah value -> values dan length -> count . Masuk akal juga untuk memiliki kelebihan pembanding.

@terrajobst Bisakah Anda mengingatkan saya mengapa

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

diperlukan ketika kita memiliki

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

?

@svick

@justinvp Jika GetHashCode() tidak akan mengembalikan kode hash yang dihitung, mungkin harus ditandai [Usang] dan [EditorBrowsable(Never)].

:+1:

@terrajobst Bisakah kita kembali ke konversi implisit dari HashCode -> int , jadi tidak ada metode ToHashCode ? edit: ToHashCode baik-baik saja. Lihat tanggapan @CyrusNajmabadi di bawah ini.

@jamesqo StringComparison adalah enum.
Namun, orang dapat menggunakan StringComparer setara sebagai gantinya.

Bisakah kita kembali memiliki konversi implisit dari HashCode -> int, jadi tidak ada metode ToHashCode?

Kami membahas ini dan memutuskan untuk tidak melakukannya dalam pertemuan. Masalahnya adalah ketika pengguna mendapatkan 'int' terakhir, pekerjaan ekstra sering dilakukan. yaitu internal kode hash akan sering melakukan langkah finalisasi, dan dapat mengatur ulang dirinya sendiri ke keadaan baru. Memiliki itu terjadi dengan konversi implisit akan menjadi aneh. Jika Anda melakukan ini:

HashCode hc = ...

int i1 = hc;
int i2 = hc;

Maka Anda bisa mendapatkan hasil yang berbeda.

Karena alasan itu, kami juga tidak menyukai konversi eksplisit (karena orang tidak menganggap konversi sebagai perubahan keadaan internal).

Dengan metode kita dapat mendokumentasikan secara eksplisit bahwa ini terjadi. Kita bahkan berpotensi menamainya untuk menyampaikannya sebanyak-banyaknya. yaitu "ToHashCodeAndReset" (meskipun kami memutuskan untuk tidak melakukannya). Tetapi setidaknya metode ini dapat memiliki dokumentasi yang jelas tentangnya yang dapat dilihat oleh pengguna dalam hal-hal seperti intellisense. Itu tidak benar-benar terjadi dengan konversi.

Saya menghapus kelebihan byte karena kami dapat berspesialisasi menggunakan typeof(T) == typeof(byte)

IIRC ada beberapa kekhawatiran tentang ini tidak baik dari perspektif JIT. Tapi itu mungkin hanya untuk kasus "typeof()" non-nilai-tipe. Selama jit akan secara efektif melakukan hal yang benar untuk kasus typeof() tipe nilai, maka itu seharusnya bagus.

@CyrusNajmabadi Saya tidak menyadari bahwa konversi ke int mungkin melibatkan status mutasi. ToHashCode kemudian.

Bagi mereka yang berpikir tentang perspektif kripto - http://tuprints.ulb.tu-darmstadt.de/2094/1/thesis.lehmann.pdf

@terrajobst , apakah Anda punya waktu untuk membaca komentar saya (mulai dari sini ) dan memutuskan apakah Anda menyetujui bentuk API yang diubah? Jika demikian, maka saya pikir ini dapat ditandai api-disetujui/diperebutkan dan kita dapat mulai memutuskan algoritma hash.

@blowdart , ada bagian tertentu yang ingin Anda soroti?

Saya mungkin tidak terlalu eksplisit tentang hal itu di atas, tetapi satu-satunya hash non-kriptografis yang saya tidak tahu tentang pembobolan HashDoS adalah Marvin dan SipHash. Artinya, bahkan seeding (katakanlah) Murmur dengan nilai acak masih dapat dipecahkan dan digunakan untuk DoS.

Tidak ada, saya hanya menganggapnya menarik, dan saya pikir dokumen untuk ini seharusnya mengatakan "Tidak untuk digunakan pada kode hash yang dihasilkan melalui algoritme kriptografi."

Keputusan

  • Kita harus menghapus semua metode AddRange karena skenarionya tidak jelas. Array agak tidak mungkin muncul sangat sering. Dan begitu array yang lebih besar terlibat, pertanyaannya adalah apakah perhitungan harus di-cache. Melihat for loop di sisi panggilan memperjelas bahwa Anda perlu memikirkannya.
  • Kami juga tidak ingin menambahkan kelebihan IEnumerable ke AddRange karena mereka akan dialokasikan.
  • Kami tidak berpikir kami membutuhkan kelebihan untuk Add yang membutuhkan string dan StringComparison . Ya, itu mungkin lebih efisien daripada menelepon melalui IEqualityComparer , tetapi kami dapat memperbaikinya nanti.
  • Kami pikir menandai GetHashCode sebagai usang dengan kesalahan adalah ide yang bagus, tetapi kami akan melangkah lebih jauh dan juga bersembunyi dari IntelliSense.

Ini meninggalkan kita dengan:

```C#
// Akan tinggal di rakitan inti
// .NET Framework : mscorlib
// .NET Core : System.Runtime / System.Private.CoreLib
Sistem ruang nama
{
struct publik HashCode
{
Gabungkan int statis publik(nilai T1);
Gabungkan int statis publik(nilai T1, nilai T2);
Gabungkan int statis publik(nilai T1, nilai T2, nilai T33);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T44);
Gabungkan int statis publik(nilai T1, nilai T22, nilai T33, nilai T4, nilai T55);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66);
Gabungkan int statis publik(nilai T1, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66, nilai T7);
Gabungkan int statis publik(nilai T11, nilai T22, nilai T33, nilai T4, nilai T55, nilai T66, nilai T77, nilai T88);

    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();
}

}
```

Langkah selanjutnya: Masalahnya siap untuk untuk mengimplementasikan API yang kita perlukan dengan beberapa algoritme kandidat sebagai eksperimen -- lihat https://github.com/dotnet/corefx/issues/14354#issuecomment -305028686 untuk daftar, sehingga kami dapat memutuskan algoritma mana yang akan diambil (berdasarkan pengukuran throughput dan distribusi, kemungkinan jawaban berbeda per arsitektur CPU).

Kompleksitas: Besar

Jika ada yang tertarik untuk mengambilnya, silakan ping kami. Bahkan mungkin ada ruang untuk beberapa orang yang mengerjakannya bersama-sama. ( @jamesqo Anda memiliki pilihan prioritas karena Anda paling banyak berinvestasi & terlama dalam masalah ini)

@karelz Terlepas dari komentar saya di atas , saya berubah pikiran karena saya tidak berpikir saya memiliki kualifikasi untuk memilih algoritma hash terbaik. Saya melihat ke beberapa perpustakaan @morganbr terdaftar dan menyadari implementasinya cukup rumit , jadi saya tidak dapat dengan mudah menerjemahkannya ke C# untuk menguji sendiri. Saya memiliki sedikit latar belakang dalam C++, jadi saya juga akan kesulitan menginstal perpustakaan dan menulis aplikasi pengujian.

Namun, saya tidak ingin ini tetap berada di daftar yang diperebutkan selamanya. Jika tidak ada yang mengambilnya seminggu dari hari ini, saya akan mempertimbangkan untuk memposting pertanyaan di Programmers SE atau Reddit.

Saya belum memasangnya (atau mengoptimalkannya), tetapi di sini adalah implementasi dasar dari algoritma hash Murmur3 yang saya gunakan di beberapa proyek pribadi saya: https://Gist.github.com/tannergooding/0a12559d1a912068b9aeb4b9586aad7f

Saya merasa solusi paling optimal di sini adalah mengubah algoritma hashing secara dinamis berdasarkan ukuran data input.

Contoh: Mumur3 (dan lainnya) sangat cepat untuk kumpulan data yang besar dan memberikan distribusi yang bagus, tetapi mereka dapat berkinerja 'buruk' (dari segi kecepatan, bukan dari segi distribusi) untuk kumpulan data yang lebih kecil.

Saya membayangkan kita harus melakukan sesuatu seperti: Jika jumlah byte keseluruhan kurang dari X, lakukan algoritma A; jika tidak, lakukan algoritma B. Ini akan tetap bersifat deterministik (per run), tetapi akan memungkinkan kita untuk memberikan kecepatan dan distribusi berdasarkan ukuran sebenarnya dari data input.

Mungkin juga perlu dicatat bahwa beberapa algoritme yang disebutkan memiliki implementasi yang dirancang khusus untuk instruksi SIMD, jadi solusi yang paling berkinerja kemungkinan akan melibatkan FCALL pada tingkat tertentu (seperti yang dilakukan dengan beberapa implementasi BufferCopy) atau mungkin melibatkan pengambilan ketergantungan pada System.Numerics.Vector .

@jamesqo , dengan senang hati kami membantu membuat pilihan; yang paling kami butuhkan bantuan adalah data kinerja untuk implementasi kandidat (idealnya C#, meskipun seperti yang ditunjukkan oleh @tannergooding , beberapa algoritme memerlukan dukungan kompiler khusus). Seperti yang saya sebutkan di atas, jika Anda membangun kandidat yang tidak terpilih, kami mungkin akan menggunakannya nanti, jadi jangan khawatir tentang pekerjaan yang sia-sia.

Saya tahu ada tolok ukur di luar sana untuk berbagai implementasi, tetapi saya pikir penting untuk memiliki perbandingan menggunakan API ini dan kemungkinan kisaran input (misalnya struct dengan 1-10 bidang).

@tannergooding , kemampuan beradaptasi semacam itu mungkin yang paling berkinerja, tetapi saya tidak melihat cara kerjanya dengan metode Add karena tidak tahu berapa kali itu akan dipanggil. Meskipun kita bisa melakukannya dengan Combine, itu berarti serangkaian panggilan Add bisa menghasilkan hasil yang berbeda dari panggilan Combine yang sesuai.

Juga, mengingat rentang input yang paling mungkin adalah 4-32 byte ( Combine`1 - Combine`8 ), semoga tidak ada perubahan kinerja yang besar pada rentang itu.

adaptasi semacam itu mungkin yang paling berkinerja, tetapi saya tidak melihat bagaimana itu akan bekerja dengan metode Add karena tidak tahu berapa kali itu akan dipanggil.

Saya pribadi tidak yakin bentuk API cukup tepat untuk hashing tujuan umum (namun dekat) ...

Saat ini kami sedang memaparkan metode Combine untuk konstruksi statis. Jika ini dimaksudkan untuk menggabungkan semua input dan menghasilkan kode hash final, maka namanya 'miskin' dan sesuatu seperti Compute mungkin lebih tepat.

Jika kami mengekspos metode Combine , mereka hanya harus mencampur semua input dan pengguna harus diminta untuk memanggil metode Finalize yang mengambil output dari gabungan terakhir serta jumlah total byte yang digabungkan untuk menghasilkan kode hash final (menyelesaikan kode hash penting karena itulah yang menyebabkan bit longsor).

Untuk pola builder, kami mengekspos metode Add dan ToHashCode . Tidak jelas apakah metode Add dimaksudkan untuk menyimpan byte dan hanya menggabungkan/menyelesaikan panggilan ke ToHashCode (dalam hal ini kita dapat memilih algoritme yang benar secara dinamis) atau jika memang demikian dimaksudkan untuk digabungkan dengan cepat, harus jelas bahwa ini masalahnya (dan bahwa implementasinya harus melacak secara internal ukuran total byte yang digabungkan).

Bagi siapa pun yang mencari titik awal yang tidak terlalu rumit, coba xxHash32. Itu mungkin menerjemahkan dengan mudah ke C# ( orang telah melakukannya ).

Masih menguji secara lokal, tetapi saya melihat tingkat throughput berikut untuk implementasi C# saya dari Murmur3.

Ini adalah untuk metode Combine statis untuk 1-8 input:

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

Implementasi saya mengasumsikan bahwa GetHashCode harus dipanggil untuk setiap input dan bahwa nilai yang dihitung harus diselesaikan sebelum dikembalikan.

Saya menggabungkan nilai int , karena ini adalah yang paling sederhana untuk diuji.

Untuk menghitung throughput, saya menjalankan 10.000 iterasi, membuang iterasi pertama sebagai 'pemanasan'.

Di setiap iterasi, saya menjalankan 10.000 sub-iterasi di mana saya memanggil HashCode.Combine , meneruskan hasil dari sub-iterasi sebelumnya sebagai nilai input pertama di iterasi berikutnya.

Saya kemudian rata-rata semua iterasi untuk mendapatkan rata-rata waktu yang telah berlalu, selanjutnya membaginya dengan jumlah sub-iterasi yang dijalankan per loop untuk mendapatkan waktu rata-rata per panggilan. Saya kemudian menghitung jumlah panggilan yang dapat dilakukan per detik dan mengalikannya dengan jumlah byte yang digabungkan untuk menghitung throughput yang sebenarnya.

Akan membersihkan kode dan membagikannya sebentar lagi.

@tannergooding , itu terdengar seperti kemajuan besar. Untuk memastikan Anda mendapatkan pengukuran yang tepat, maksud dari API adalah bahwa panggilan ke HashCode.Combine(a, b) sama dengan panggilan

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

Dalam kedua kasus, data harus dimasukkan ke dalam status hash internal yang sama dan hash harus diselesaikan sekali di akhir.

👍

Itulah yang secara efektif dilakukan oleh kode yang saya tulis. Satu-satunya perbedaan adalah saya secara efektif memasukkan semua kode (tidak perlu mengalokasikan new HashCode() dan melacak jumlah byte yang digabungkan karena konstan).

@morganbr. Implementasi + Tes throughput untuk Murmur3: https://Gist.github.com/tannergooding/89bd72f05ab772bfe5ad3a03d6493650

MurmurHash3 didasarkan pada algoritma yang dijelaskan di sini: https://github.com/aappleby/smhasher/wiki/MurmurHash3 , repo mengatakan itu MIT

Bekerja pada xxHash32 (BSD-2 Clause -- https://github.com/Cyan4973/xxHash/blob/dev/xxhash.c) dan SpookyHash (Domain Publik -- http://www.burtleburtle.net/bob/hash /spooky.html) varian

@tannergooding Sekali lagi, bukan ahli hash tapi saya ingat [membaca artikel][1] yang mengatakan Murmur tidak tahan DoS, jadi tunjukkan saja sebelum kita memilihnya.

@jamesqo , saya mungkin salah, tapi saya cukup yakin bahwa kerentanan diterapkan ke Murmur2 dan bukan Murmur3.

Dalam kedua kasus, saya menerapkan beberapa algoritma sehingga kita bisa mendapatkan hasil throughput untuk C#. Distribusi dan properti lain dari algoritma ini cukup terkenal sehingga kita bisa memilih mana yang terbaik nantinya

Ups, lupa menautkan ke artikel: http://emboss.github.io/blog/2012/12/14/breaking-murmur-hash-flooding-dos-reloaded/.

@tannergooding oke. Kedengarannya adil :+1:

@tannergooding , saya melihat implementasi Murmur3 Anda dan umumnya terlihat benar dan mungkin dioptimalkan dengan cukup baik. Untuk memastikan saya mengerti dengan benar, apakah Anda menggunakan fakta bahwa nilai gabungan dan keadaan internal Murmur keduanya 32 bit? Itu mungkin pengoptimalan yang cukup bagus untuk kasus ini dan menjelaskan beberapa kebingungan saya sebelumnya.

Jika kita mengadopsinya, mungkin perlu beberapa penyesuaian (mungkin tidak akan membuat perbedaan besar pada pengukuran kinerja):

  • Menggabungkanharus tetap memanggil CombineValue pada value1
  • Panggilan CombineValue pertama harus mengambil benih acak
  • ToHashCode harus mengatur ulang _bytesCombined dan _combinedValue

Sementara itu ketika saya merindukan API ini, seberapa buruk bagi saya untuk mengimplementasikan GetHashCode melalui (field1, field2, field3).GetHashCode() ?

@jnm2 , penggabung kode hash ValueTuple cenderung mengatur input Anda dalam kode hash (dan membuang yang terbaru). Untuk beberapa bidang dan tabel hash yang dibagi dengan bilangan prima, Anda mungkin tidak menyadarinya. Untuk banyak bidang atau tabel hash yang dibagi dengan pangkat dua, entropi bidang terakhir yang Anda masukkan akan memiliki pengaruh paling besar terhadap apakah Anda memiliki benturan (misalnya jika bidang terakhir Anda adalah bool atau int kecil, Anda 'mungkin akan memiliki banyak tabrakan, jika itu adalah panduan, Anda mungkin tidak akan melakukannya).

ValueTuple juga tidak berfungsi dengan baik dengan bidang yang semuanya 0.

Di samping catatan, saya harus berhenti mengerjakan implementasi lain (memiliki pekerjaan dengan prioritas lebih tinggi). Tidak yakin kapan saya bisa mengambilnya kembali.

Jadi jika itu tidak cukup baik untuk tipe terstruktur, mengapa itu cukup baik untuk Tuple?

@jnm2 , itulah salah satu alasan mengapa fitur ini layak untuk dibangun -- jadi kita bisa mengganti hash di bawah standar di seluruh kerangka kerja.

Tabel besar fungsi hash dengan karakteristik kinerja dan kualitas:
https://github.com/leo-yuriev/t1ha

@arespr Saya pikir tim sedang mencari implementasi C# dari fungsi hash. Terima kasih telah berbagi.

@tannergooding Apakah Anda masih tidak dapat mengambil kembali masalah ini? Jika demikian maka saya akan memposting di Reddit/Twitter bahwa kami sedang mencari ahli hash.

edit: Membuat posting di Reddit. https://www.reddit.com/r/csharp/comments/6qsysm/Looking_for_hash_expert_to_help_net_core_team/?ref=share&ref_source=link

@jamesqo , saya memiliki beberapa hal prioritas yang lebih tinggi di piring saya dan tidak akan dapat mencapai ini dalam 3 minggu ke depan.

Juga, pengukuran saat ini akan dibatasi oleh apa yang saat ini dapat kami kodekan dalam C#, namun, jika/ketika ini menjadi sesuatu (https://github.com/dotnet/designs/issues/13), pengukuran kemungkinan akan sedikit berubah ;)

Juga, pengukuran saat ini akan dibatasi oleh apa yang saat ini dapat kami kodekan dalam C#, namun, jika/ketika ini menjadi sesuatu (dotnet/desain#13), pengukuran kemungkinan akan sedikit berubah;)

Tidak apa-apa-- kita selalu dapat mengubah algoritme hash setelah intrinsik tersedia, merangkum/mengacak kode hash memungkinkan kita melakukannya. Kami hanya mencari sesuatu yang menawarkan tradeoff kinerja/distribusi terbaik untuk runtime dalam kondisi saat ini.

@jamesqo , terima kasih telah mencari orang untuk membantu. Kami akan senang jika seseorang yang bukan ahli hash mengerjakan ini juga -- kami benar-benar hanya membutuhkan seseorang yang dapat mem-porting beberapa algoritme ke C# dari bahasa atau desain lain dan kemudian melakukan pengukuran kinerja. Setelah kami memilih kandidat, pakar kami akan melakukan apa yang kami lakukan pada setiap perubahan -- meninjau kode untuk kebenaran, kinerja, keamanan, dll.

Hai! Saya baru saja membaca diskusi, dan setidaknya bagi saya tampaknya kasus ini ditutup dengan kuat untuk mendukung murmur3-32 PoC. BTW mana yang tampaknya merupakan pilihan yang sangat bagus bagi saya, dan saya akan merekomendasikan untuk tidak menghabiskan pekerjaan yang tidak perlu lagi (tetapi mungkin bahkan menjatuhkan anggota .Add() ...).

Tetapi jika seseorang ingin melanjutkan dengan lebih banyak pekerjaan kinerja, saya dapat menyediakan beberapa kode untuk xx32, xx64, hsip13/24, seahash, murmur3-x86/32 (dan saya mengintegrasikan impl marvin32 dari atas), dan (belum tidak dioptimalkan) sip13/24, spookyv2. Beberapa versi City terlihat cukup mudah untuk dipindahkan, jika diperlukan. Proyek setengah terbengkalai itu memiliki kasus penggunaan yang sedikit berbeda, jadi tidak ada kelas HashCode dengan API yang diusulkan; tetapi untuk benchmarking seharusnya tidak terlalu menjadi masalah.

Jelas tidak siap produksi: kode berlaku murah hati jumlah brute-force seperti copy-pasta, gepeng kanker agresif-inline dan tidak aman; endianess tidak ada, begitu juga pembacaan yang tidak selaras. Bahkan tes terhadap vektor tes ref-impl secara halus berbicara "tidak lengkap".

Jika ini benar-benar membantu, saya akan menemukan cukup waktu selama dua minggu ke depan untuk memperbaiki masalah yang paling mengerikan, dan membuat kode dan beberapa hasil awal tersedia.

@gimpf

Saya baru saja membaca diskusi, dan setidaknya bagi saya tampaknya kasus ini ditutup dengan kuat untuk mendukung murmur3-32 PoC. BTW mana yang sepertinya merupakan pilihan yang sangat bagus bagi saya, dan saya sarankan untuk tidak menghabiskan pekerjaan yang tidak perlu lagi

Tidak, orang belum menyukai Murmur3. Kami ingin memastikan bahwa kami memilih algoritme terbaik mutlak dalam hal keseimbangan antara kinerja/distribusi, jadi kami tidak dapat meninggalkan kebutuhan bisnis yang terlewat.

Tetapi jika seseorang ingin melanjutkan dengan lebih banyak pekerjaan kinerja, saya dapat menyediakan beberapa kode untuk xx32, xx64, hsip13/24, seahash, murmur3-x86/32 (dan saya mengintegrasikan impl marvin32 dari atas), dan (belum tidak dioptimalkan) sip13/24, spookyv2. Beberapa versi City terlihat cukup mudah untuk dipindahkan, jika diperlukan.

Ya silahkan! Kami ingin mengumpulkan kode untuk sebanyak mungkin algoritme untuk diuji. Setiap algoritme baru yang dapat Anda sumbangkan sangat berharga. Akan sangat dihargai jika Anda juga dapat mem-porting algoritme City.

Jelas tidak siap produksi: kode ini menerapkan sejumlah besar kekerasan seperti copy-pasta, penyebaran kanker agresif-inline dan tidak aman; endianess tidak ada, begitu juga pembacaan yang tidak selaras. Bahkan tes terhadap vektor tes ref-impl secara halus berbicara "tidak lengkap".

Tidak apa-apa. Bawa saja kodenya, dan orang lain dapat menemukannya jika diperlukan.

Jika ini benar-benar membantu, saya akan menemukan cukup waktu selama dua minggu ke depan untuk memperbaiki masalah yang paling mengerikan, dan membuat kode dan beberapa hasil awal tersedia.

Ya itu akan luar biasa!

@jamesqo Ok, saya akan memberikan catatan setelah saya memiliki sesuatu untuk ditunjukkan.

@gimpf kedengarannya sangat bagus dan kami akan senang mendengar tentang kemajuan Anda saat ini (tidak perlu menunggu sampai Anda menyelesaikan setiap algoritme!). Tidak siap produksi tidak apa-apa selama Anda yakin kode tersebut menghasilkan hasil yang benar dan kinerjanya merupakan representasi yang baik dari apa yang akan kita lihat dalam implementasi siap produksi. Setelah kami memilih kandidat, kami dapat bekerja dengan Anda untuk mendapatkan implementasi berkualitas tinggi.

Saya belum melihat analisis bagaimana entropi seahash dibandingkan dengan algoritma lain. Apakah Anda memiliki petunjuk tentang itu? Ini memiliki pengorbanan perf yang terdengar menarik... vektorisasi terdengar cepat, tetapi aritmatika modular terdengar lambat.

@morganbr Saya sudah menyiapkan penggoda.

Tentang SeaHash : Tidak, saya belum tahu tentang kualitasnya; jika kinerjanya menarik, saya akan menambahkannya ke SMHasher. Setidaknya penulis mengklaim itu baik (menggunakannya untuk checksum dalam sistem file), dan juga mengklaim bahwa tidak ada entropi yang dibuang selama pencampuran.

Tentang hash dan benchmark : Project Haschisch.Kastriert , halaman wiki dengan hasil benchmarking pertama membandingkan xx32, xx64, hsip13, hsip24, marvin32, sea dan murmur3-32.

Beberapa peringatan penting:

  • Ini adalah bench run yang sangat cepat dengan pengaturan akurasi rendah.
  • Implementasinya belum benar-benar selesai, dan beberapa pesaing masih belum ada. Implementasi Streaming (hal seperti itu akan diperlukan untuk dukungan .Add() yang masuk akal) membutuhkan pengoptimalan yang sebenarnya.
  • SeaHash saat ini tidak menggunakan seed.

Kesan pertama:

  • untuk pesan besar, xx64 adalah yang tercepat dari implementasi yang terdaftar (sekitar 3,25 byte per siklus, sejauh yang saya mengerti, atau 9,5 GiB/s di notebook saya)
  • untuk pesan singkat, tidak ada yang hebat, tetapi murmur3-32, dan (mengejutkan) seahash memiliki keunggulan, tetapi yang terakhir kemungkinan dijelaskan oleh seahash yang belum menggunakan seed.
  • "patokan" untuk mengakses HashSet<> perlu bekerja, karena semuanya hampir dalam kesalahan pengukuran (saya telah melihat perbedaan yang lebih besar, tetapi masih tidak layak untuk dibicarakan)
  • saat menggabungkan kode hash, murmur-3A PoC sekitar 5 hingga 20 kali lebih cepat dari yang kita miliki di sini
  • beberapa abstraksi dalam C# sangat mahal; yang membuat membandingkan algoritma hash lebih menyebalkan daripada yang diperlukan.

Saya akan menulis surat lagi kepada Anda setelah saya sedikit memperbaiki situasi.

@gimpf , itu awal yang fantastis! Saya melihat kode dan hasilnya dan saya punya beberapa pertanyaan.

  1. Hasil Anda menunjukkan SimpleMultiplyAdd sekitar 5x lebih lambat dari Murmur3a @tannergooding. Itu tampak aneh karena Murmur memiliki lebih banyak pekerjaan yang harus dilakukan daripada mengalikan+menambahkan (walaupun saya akan mengakui bahwa memutar adalah operasi yang lebih cepat daripada menambahkan). Apakah mungkin implementasi Anda memiliki inefisiensi umum yang tidak ada dalam implementasi Murmur itu atau haruskah saya membaca ini sebagai implementasi khusus yang memiliki keunggulan besar dibandingkan yang bertujuan umum?
  2. Memiliki hasil untuk 1, 2, dan 4 kombinasi itu bagus, tetapi API ini naik hingga 8. Apakah mungkin untuk mendapatkan hasil untuk itu juga atau apakah itu menyebabkan terlalu banyak duplikasi?
  3. Saya melihat bahwa Anda menjalankan X64, jadi hasil ini akan membantu kami dalam memilih algoritme X64 kami, tetapi tolok ukur lain menunjukkan bahwa algoritme dapat berbeda secara dramatis antara X86 dan X64. Apakah mudah bagi Anda untuk juga mendapatkan hasil X86? (Pada titik tertentu, kami juga perlu mendapatkan ARM dan ARM64, tetapi itu pasti bisa menunggu)

Hasil HashSet Anda sangat menarik. Jika mereka bertahan, itu adalah kasus yang mungkin untuk lebih memilih entropi yang lebih baik daripada waktu hash yang lebih cepat.

@morganbr Akhir pekan ini lebih aktif dan tidak aktif, jadi kemajuannya terbatas.

Tentang pertanyaan Anda:

  1. Hasil Anda menunjukkan SimpleMultiplyAdd sekitar 5x lebih lambat dari Murmur3a @tannergooding. Itu sepertinya aneh...

Aku bertanya-tanya sendiri. Itu adalah kesalahan salin/tempel, SimpleMultiplyAdd selalu menggabungkan empat nilai... Juga, dengan menyusun ulang beberapa pernyataan, penggabung multiply-add menjadi sedikit lebih cepat (~60% throughput lebih tinggi).

Apakah mungkin implementasi Anda memiliki inefisiensi umum yang tidak ada dalam implementasi Murmur itu atau haruskah saya membaca ini sebagai implementasi khusus yang memiliki keunggulan besar dibandingkan yang bertujuan umum?

Saya mungkin melewatkan beberapa hal, tetapi tampaknya untuk .NET implementasi tujuan umum tidak dapat digunakan untuk kasus penggunaan ini. Saya telah menulis metode Combine-style untuk semua algoritme, dan kode hash wrt yang menggabungkan sebagian besar kinerja _jauh_ lebih baik daripada yang umum.

Namun, bahkan implementasi tersebut tetap terlalu lambat; pekerjaan lebih lanjut diperlukan. Kinerja .NET di area ini benar-benar buram bagi saya; menambahkan atau menghapus salinan variabel lokal dapat dengan mudah mengubah kinerja dengan faktor dua. Saya mungkin tidak akan dapat memberikan implementasi yang cukup dioptimalkan dengan baik untuk tujuan memilih opsi terbaik.

  1. Memiliki hasil untuk 1, 2, dan 4 kombinasi itu bagus, tetapi API ini naik hingga 8.

Saya telah memperluas tolok ukur gabungan. Tidak ada kejutan di depan itu.

  1. Saya melihat bahwa Anda menjalankan X64 (...), Apakah mudah bagi Anda untuk juga mendapatkan hasil X86?

Dulu, tapi kemudian saya porting ke .NET Standard. Sekarang saya berada di neraka ketergantungan, dan hanya benchmark .NET Core 2 dan CLR 64bit yang berfungsi. Ini dapat diselesaikan dengan cukup mudah setelah saya menyelesaikan masalah saat ini.

Apakah menurut Anda ini akan berhasil dalam rilis v2.1?

@gimpf Anda belum memposting dalam beberapa saat-- apakah Anda memiliki pembaruan kemajuan pada implementasi Anda? :senyum:

@jamesqo Saya telah memperbaiki beberapa benchmark yang menyebabkan hasil aneh, dan menambahkan City32, SpookyV2, Sip13 dan Sip24 ke daftar algoritma yang tersedia. Sips secepat yang diharapkan (relatif terhadap throughput xx64), City dan Spooky tidak (sama masih berlaku untuk SeaHash).

Untuk menggabungkan kode hash, Murmur3-32 masih terlihat seperti taruhan yang bagus, tetapi saya belum menjalankan perbandingan yang lebih lengkap.

Pada catatan lain, API streaming (.Add()) memiliki efek samping yang tidak menguntungkan untuk menghapus beberapa algoritme hash dari daftar kandidat. Mengingat kinerja API semacam itu juga dipertanyakan, Anda mungkin ingin memikirkan kembali apakah akan menawarkannya dari awal.

Jika bagian .Add() akan dihindari, dan mengingat bahwa hash-combiner menggunakan seed, saya tidak berpikir bahwa akan ada salahnya membersihkan tg's combiner, membuat test-suite kecil, dan menyebutnya sehari. Karena saya hanya memiliki beberapa jam setiap akhir pekan, dan pengoptimalan kinerja agak membosankan, membuat versi berlapis emas dapat sedikit berlarut-larut ...

@gimpf , itu terdengar seperti kemajuan besar. Apakah Anda memiliki tabel hasil yang berguna sehingga kami dapat melihat apakah cukup untuk membuat keputusan dan bergerak maju?

@morganbr Saya telah memperbarui hasil pembandingan saya.

Untuk saat ini saya hanya mendapatkan hasil 64bit di .NET Core 2. Untuk platform itu, City64 tanpa seed adalah yang tercepat di semua ukuran. Memasukkan benih, XX-32 diikat dengan Murmur-3-32. Untungnya, ini adalah algoritme yang sama yang memiliki reputasi cepat untuk platform 32bit, tetapi jelas kami perlu memverifikasi bahwa itu juga berlaku untuk implementasi saya. Hasilnya tampaknya mewakili kinerja dunia nyata, kecuali bahwa Sea dan SpookyV2 tampak sangat lambat.

Anda perlu mempertimbangkan seberapa besar Anda benar-benar membutuhkan perlindungan hash-dos untuk penggabung kode hash. Jika seeding hanya diperlukan untuk membuat hash jelas tidak dapat digunakan untuk persistensi, city64 setelah XOR dengan seed 32bit akan menjadi peningkatan. Karena utilitas ini hanya ada untuk menggabungkan hash (dan tidak menggantikan misalnya kode hash untuk string, atau menjadi hasher drop-in untuk array integer dll.), itu mungkin cukup baik.

Jika OTOH Anda merasa membutuhkannya, Anda akan senang melihat bahwa Sip13 biasanya kurang dari 50% lebih lambat dari XX-32 (pada platform 64bit), tetapi hasilnya kemungkinan akan sangat berbeda untuk aplikasi 32bit.

Tidak tahu seberapa relevan dengan corefx, tetapi saya telah menambahkan hasil LegacyJit 32bit (w/FW 4.7).

Saya ingin mengatakan bahwa hasilnya sangat lambat. Namun, sebagai contoh, pada 56 MiB/dtk vs. 319 MiB/dtk saya tidak tertawa (itu Sip, ini paling kehilangan optimasi putar-kiri). Saya pikir saya ingat mengapa saya membatalkan proyek algoritma hash .NET saya pada bulan Januari...

Jadi, RyuJit-32bit masih hilang, dan akan (semoga) memberikan hasil yang sangat berbeda, tetapi untuk LegacyJit-x86, Murmur-3-32 menang dengan mudah, dan hanya City-32 dan xx-32 yang bisa mendekati. Murmur masih memiliki kinerja yang buruk hanya sekitar 0,4 hingga 1,1 GB/dtk, bukan 0,6 hingga 2 GB/dtk (pada mesin yang sama), tetapi setidaknya berada di rata-rata yang tepat.

Saya akan menjalankan tolok ukur pada beberapa kotak saya malam ini dan memposting hasil (Ryzen, i7, Xeon, A10, i7 Mobile, dan saya pikir beberapa lainnya).

@tannergooding @morganbr Beberapa

Penting dulu:

  • Saya memperbaiki beberapa implementasi gabungan yang menghasilkan nilai hash yang salah.
  • Rangkaian benchmark sekarang bekerja lebih keras untuk menghindari pelipatan konstan. City64 rentan (seperti murmur-3-32 di masa lalu). Tidak berarti bahwa saya memahami setiap hasil sekarang, tetapi mereka jauh lebih masuk akal.

Hal-hal yang bagus:

  • Implementasi Penggabung sekarang tersedia untuk semua kelebihan argumen 1 hingga 8, termasuk implementasi yang dibuka secara manual yang agak lebih rumit untuk xx/city.
  • Tes dan Tolok Ukur juga memeriksanya. Karena banyak algoritma hash memiliki pesan byte rendah dengan casing khusus, pengukuran tersebut mungkin menarik.
  • Tolok ukur berjalan yang disederhanakan untuk beberapa target (Core vs. FW).

Untuk menjalankan suite pada semua implementasi utama untuk menggabungkan kode hash, termasuk "Empty" (overhead murni) dan "multiply-add" (versi kecepatan yang dioptimalkan dari jawaban SO terkenal):

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

(_Menjalankan benchmark Core 32bit tampaknya memerlukan BenchmarkDotNet pra-rilis (atau mungkin hanya penyiapan 32bit plus menggunakan bench-runner berbasis Core). Maka, ini akan berfungsi menggunakan -j:core_x86, semoga)_

Hasil : Setelah semua perbaikan bug, xx32 tampaknya menang untuk semua kelebihan beban dengan RyuJIT 64 bit, pada Windows 10 pada ponsel Haswell i7, dalam proses "cepat". Antara Sips dan marvin32, Sip-1-3 selalu menang. Sip-1-3 sekitar 4 kali lebih lambat dari xx32, yang lagi-lagi sekitar 2 kali lebih lambat dari penggabung perkalian-tambah primitif. Hasil Inti 32bit masih hilang, tetapi saya kurang lebih menunggu rilis BenchmarkDotNet yang stabil yang akan menyelesaikan masalah itu untuk saya.

(Sunting) Saya baru saja menambahkan benchmark cepat untuk mengakses hash-set . Ini jelas jauh lebih bergantung pada detail daripada -benchmark di atas, tetapi Anda mungkin ingin melihatnya.

Terima kasih sekali lagi @gimpf untuk data yang fantastis! Mari kita lihat apakah kita bisa mengubahnya menjadi keputusan.

Untuk memulainya, saya akan membagi algoritma seperti ini:
Cepat + Entropi bagus (diurutkan berdasarkan kecepatan):

  1. xxHash32
  2. City64 (Ini mungkin akan lambat pada x86, jadi kita mungkin harus memilih sesuatu yang lain untuk x86)
  3. gumam3A

Tahan HashDoS:

  • Marvin32
  • SipHash. Jika kami condong ke arah ini, kami harus meninjaunya oleh pakar kripto Microsoft untuk mengonfirmasi bahwa hasil penelitian dapat diterima. Kita juga harus mencari tahu parameter mana yang cukup aman. Makalah ini menyarankan suatu tempat antara Sip-2-4 dan Sip-4-8.

Keluar dari pertikaian (lambat):

  • seramV2
  • Kota32
  • xxHash64
    *SeaHash (dan kami tidak memiliki data tentang entropi)

Di luar pertikaian (entropi buruk):

  • KalikanTambahkan
  • Hsip

Sebelum kita memilih pemenang, saya ingin memastikan orang lain setuju dengan pendapat saya di atas. Jika itu berlaku, saya pikir kita hanya perlu memilih apakah akan membayar 2x untuk resistensi HashDoS dan kemudian pergi dengan kecepatan.

@morganbr Pengelompokan Anda tampaknya baik-baik saja. Sebagai titik data dalam putaran SipHash, proyek Rust meminta Jean-Philippe Aumasson , yang menulis sip-hash w/DJB. Setelah diskusi itu mereka memutuskan untuk menggunakan sip-1-3 untuk tabel hash.

(Lihat PR rust:#33940 dan edisi yang menyertainya

Berdasarkan data dan komentar, saya ingin mengusulkan agar kami menggunakan xxHash32 di semua arsitektur. Langkah selanjutnya adalah mengimplementasikannya. @gimpf , apakah Anda tertarik untuk menyusun PR untuk itu?

Bagi mereka yang peduli dengan HashDoS, saya akan segera menindaklanjuti dengan proposal untuk API hashing tujuan umum yang harus menyertakan Marvin32 dan mungkin menyertakan SipHash. Itu juga akan menjadi tempat yang tepat untuk implementasi lain yang telah dikerjakan oleh @gimpf dan @tannergooding .

@morganbr Saya dapat menyusun PR jika waktu memungkinkan. Juga, saya pribadi lebih suka xx32 juga, selama itu tidak mengurangi penerimaan.

@gimpf , bagaimana waktumu? Jika Anda tidak punya waktu, kami juga dapat melihat apakah ada orang lain yang ingin mencobanya.

@morganbr Saya telah merencanakan untuk melakukannya hingga 5 November, dan masih terlihat bagus bahwa saya akan menemukan waktu dalam dua minggu ke depan.

@gimpf , kedengarannya bagus. Terima kasih atas pembaruannya!

@terrajobst - Saya agak terlambat ke pesta (maaf), tetapi tidak bisakah kita mengubah tipe pengembalian metode Add?

```c#
Tambahkan Kode Hash publik(nilai T);
Tambahkan Kode Hash publik(Nilai T, IEqualityComparerpembanding);

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();

Namun, hal yang persis sama dapat dicapai seperti ini, meskipun dengan satu alokasi array yang tidak terlalu boros:

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

Perhatikan bahwa jenis juga dapat dicampur. Ini jelas bisa dilakukan dengan tidak menyebutnya dengan lancar di dalam metode biasa. Mengingat argumen ini bahwa antarmuka yang lancar tidak mutlak diperlukan, mengapa params boros ada untuk memulai? Jika saran ini adalah saran yang buruk, maka kelebihan params jatuh ke sumbu yang sama. Itu, dan memaksakan metode reguler untuk kode hash yang sepele namun optimal sepertinya banyak upacara.

Sunting: implicit operator int juga bagus untuk DRY, tetapi tidak terlalu penting.

@jcdickinson

tidak bisakah kita mengubah tipe pengembalian metode Add?

Kami sudah membahas itu di proposal lama, dan ditolak.

mengapa params overload yang boros ada sejak awal?

Kami tidak menambahkan kelebihan params? Lakukan Ctrl+F untuk "params" di halaman web ini, dan Anda akan melihat bahwa komentar Anda adalah satu-satunya tempat di mana kata itu muncul.

Operator implisit int juga bagus untuk KERING, tetapi tidak terlalu penting.

Saya percaya itu juga dibahas di suatu tempat di atas ...

@jamesqo terima kasih atas penjelasannya.

params kelebihan beban

Maksud saya AddRange , tapi saya rasa saya tidak akan memiliki daya tarik untuk ini.

@jcdickinson AddRange ada di proposal asli, tapi tidak ada di versi saat ini. Itu ditolak oleh tinjauan API (lihat https://github.com/dotnet/corefx/issues/14354#issuecomment-308190321 oleh @terrajobst):

Kita harus menghapus semua metode AddRange karena skenarionya tidak jelas. Array agak tidak mungkin muncul sangat sering. Dan begitu array yang lebih besar terlibat, pertanyaannya adalah apakah perhitungan harus di-cache. Melihat for loop di sisi panggilan memperjelas bahwa Anda perlu memikirkannya.

@gimpf saya melanjutkan proposal dengan xxHash32 . Jangan ragu untuk mengambil implementasi itu. Ini memiliki tes terhadap vektor xxHash32 yang sebenarnya.

Sunting

Mengenai antarmuka. Saya sepenuhnya sadar bahwa saya sedang membuat gunung dari sarang tikus tanah - jangan ragu untuk mengabaikannya. Saya menggunakan proposal saat ini terhadap hal-hal nyata dan itu banyak pengulangan yang mengganggu.

Saya telah bermain-main dengan antarmuka dan sekarang mengerti mengapa antarmuka yang lancar ditolak; itu secara signifikan lebih lambat.

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

Menggunakan metode non-inline sebagai sumber kode hash; 50 permintaan Add vs metode ekstensi yang lancar:

| Metode | Berarti | Kesalahan | StdDev | Berskala |
|------- |---------:|---------:|---------:|-------: |
| Tambahkan | 401,6 ns | 1.262 ns | 1.180 n | 1,00 |
| Hitungan | 747,8 ns | 2.329 ns | 2.178 ns | 1.86 |

Namun, pola berikut ini berfungsi:

```c#
struct publik HashCode : System.Collections.IEnumerable
{
[EditorBrowsable(EditorBrowsableState.Never)]
[Usang("Metode ini disediakan untuk sintaks penginisialisasi koleksi.", error: true)]
public IEnumerator GetEnumerator() => lempar NotImplementedException() baru;
}

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

```

Ini juga memiliki karakteristik kinerja yang identik dengan proposal saat ini:

| Metode | Berarti | Kesalahan | StdDev | Berskala |
|------------ |---------:|---------:|---------:|--- ----:|
| Tambahkan | 405,0 n | 2.130 ns | 1,889 ns | 1,00 |
| Inisialisasi | 400,8 ns | 4,821 ns | 4.274 ns | 0,99 |

Sayangnya ini adalah peretasan, karena IEnumerable harus diimplementasikan untuk membuat kompiler senang. Meskipun demikian, Obsolete akan error bahkan pada foreach - Anda harus benar-benar ingin memecahkan sesuatu untuk mendapatkan pengecualian. MSIL di keduanya pada dasarnya identik.

@jcdickinson terima kasih telah mengatasi masalah ini. Saya mengirimi Anda undangan Kolaborator, beri tahu saya ketika Anda menerima dan saya akan dapat menetapkan masalah ini kepada Anda (menetapkan untuk saya sendiri sementara itu).

Pro-tip: Setelah Anda menerima, GitHub akan secara otomatis mendaftarkan Anda untuk semua pemberitahuan dari repo (500+ per hari), saya akan merekomendasikan untuk mengubahnya menjadi hanya "Tidak Menonton" yang akan mengirimkan semua sebutan dan pemberitahuan Anda untuk masalah Anda berlangganan.

@jcdickinson , saya pasti tertarik dengan cara untuk menghindari pengulangan yang mengganggu (walaupun saya tidak tahu bagaimana perasaan orang tentang sintaks penginisialisasi). Sepertinya saya ingat bahwa ada dua masalah dengan fasih:

  1. Masalah kinerja yang Anda catat
  2. Nilai kembalian dari metode lancar adalah salinan dari struct. Terlalu mudah untuk secara tidak sengaja kehilangan input karena melakukan hal-hal seperti:
var hc = new HashCode();
var newHc = hc.Add(foo);
hc.Add(bar);
return newHc.ToHashCode();

Karena proposal di utas ini sudah disetujui (dan Anda sedang dalam proses untuk menggabungkannya), saya sarankan untuk memulai proposal API baru untuk setiap perubahan.

@karelz Saya percaya @gimpf sudah @gimpf . ( edit: nvm)

@terrajobst Salah satu jenis permintaan API menit terakhir untuk ini. Karena kami menandai GetHashCode usang, kami secara implisit memberi tahu pengguna bahwa HashCode s bukanlah nilai yang dimaksudkan untuk dibandingkan, meskipun merupakan struct yang biasanya tidak dapat diubah/dapat dibandingkan. Dalam hal ini, haruskah kita menandai Equals usang juga?

[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.");

Saya pikir hal serupa dilakukan dengan Span .

Jika itu diterima, maka saya pikir ...

  1. Saya akan mempertimbangkan untuk menggunakan should not , atau may not alih-alih cannot dalam pesan Usang.
  2. Asalkan Pengecualian tetap, saya akan memasukkan string yang sama dalam pesannya, untuk berjaga-jaga jika metode dipanggil melalui pemeran atau generik terbuka.

@ Joe4evr Baik dengan saya; Saya telah memperbarui komentar. Mungkin juga bermanfaat untuk menyertakan pesan yang sama dalam pengecualian GetHashCode juga, lalu:

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

@morganbr Mengapa Anda membuka kembali ini?

PR untuk mengeksposnya di CoreFX belum selesai.

@gimpf apakah Anda memiliki kode yang Anda tolok ukur yang tersedia dan/atau apakah Anda dapat dengan cepat melihat bagaimana paket nuget SpookilySharp adil. Saya ingin membersihkan proyek itu setelah stagnasi beberapa tahun dan saya ingin tahu bagaimana proyek itu berdiri.

@JonHanna Dia mempostingnya di sini: https://github.com/gimpf/Haschisch.Kastriert

@JonHanna , saya akan tertarik untuk mendengar bagaimana pengujian Anda berjalan sehingga kami dapat mulai berpikir tentang apa yang akan berguna dalam API hashing non-kriptografi untuk tujuan umum.

@morganbr Di mana forum yang tepat untuk membahas API semacam itu? Saya berharap bahwa API semacam itu akan terdiri dari lebih dari sekadar penyebut umum terendah, dan mungkin API yang baik juga memerlukan penanganan JIT wrt yang lebih baik dari struct yang lebih besar. Membahas semua yang mungkin lebih baik dilakukan dalam edisi terpisah...

@gimpf Membuka satu untuk Anda. dotnet/corefx#25666

@morganbr - Bisakah kita mendapatkan nama paket & no versi yang akan menyertakan komit ini?

@karelz , mungkin Anda dapat membantu @smitpatel dengan info paket/versi?

Saya akan mencoba build harian .NET Core - saya akan menunggu sampai besok.
Saya tidak berpikir ada paket yang bisa Anda andalkan.

Pertanyaan untuk peserta di sini. Roslyn IDE memungkinkan pengguna untuk menghasilkan impl GetHashCode berdasarkan sekumpulan bidang/properti di class/struct mereka. Idealnya, orang dapat menggunakan HashCode.Combine baru yang ditambahkan di https://github.com/dotnet/corefx/pull/25013 . Namun, beberapa pengguna tidak akan memiliki akses ke kode itu. Jadi, kami ingin tetap dapat membuat GetHashCode yang akan bekerja untuk mereka.

Baru-baru ini, kami menyadari bahwa formulir yang kami hasilkan bermasalah. Yaitu, karena VB mengkompilasi dengan pemeriksaan overflow secara default, dan impl kami akan menyebabkan overflow. Juga, VB tidak memiliki cara untuk menonaktifkan pemeriksaan luapan untuk wilayah kode. Ini baik hidup atau mati sepenuhnya untuk seluruh perakitan.

Karena itu, saya ingin mengganti impl yang kami sediakan dengan formulir yang tidak mengalami masalah ini. Idealnya, formulir yang dihasilkan akan memiliki properti berikut:

  1. Satu/dua baris di GetHashCode per bidang/properti yang digunakan.
  2. Tidak meluap.
  3. hashing yang cukup baik. Kami tidak mengharapkan hasil yang luar biasa. Tetapi sesuatu yang mudah-mudahan sudah diperiksa untuk menjadi layak, dan tidak memiliki masalah yang biasanya Anda dapatkan dengan a + b + c + d atau a ^ b ^ c ^ d .
  4. Tidak ada dependensi/persyaratan tambahan pada kode.

Misalnya, satu opsi untuk VB adalah menghasilkan sesuatu seperti:

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

Tapi ini kemudian tergantung pada referensi ke System.ValueTuple. Idealnya, kita dapat memiliki impl yang berfungsi bahkan tanpa itu.

Adakah yang tahu tentang algoritma hashing yang layak yang dapat bekerja dengan kendala ini? Terima kasih!

--

Catatan: kode emisi kami yang ada adalah:

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

Ini jelas bisa meluap.

Ini juga bukan masalah untuk C# karena kita bisa menambahkan unchecked { } sekitar kode itu. Kontrol berbutir halus itu tidak mungkin dilakukan di VB.

Adakah yang tahu tentang algoritma hashing yang layak yang dapat bekerja dengan kendala ini? Terima kasih!

Nah, Anda bisa melakukan Tuple.Create(...).GetHashCode() . Jelas itu menimbulkan alokasi, tetapi tampaknya lebih baik daripada melemparkan pengecualian.

Apakah ada alasan Anda tidak bisa memberi tahu pengguna untuk menginstal System.ValueTuple ? Karena ini adalah fitur bahasa bawaan, saya yakin paket System.ValueTuple pada dasarnya sangat kompatibel dengan semua platform bukan?

Jelas itu menimbulkan alokasi, tetapi tampaknya lebih baik daripada melemparkan pengecualian.

Ya. alangkah baiknya jika tidak menyebabkan alokasi.

Apakah ada alasan Anda tidak bisa begitu saja memberi tahu pengguna untuk menginstal System.ValueTuple?

Itu akan menjadi perilaku jika kita menghasilkan pendekatan ValueTuple. Namun, sekali lagi, alangkah baiknya jika kita bisa menghasilkan sesuatu yang baik yang sesuai dengan cara pengguna menyusun kode mereka saat ini, tanpa membuat mereka mengubah struktur mereka dengan cara yang berat.

Sepertinya pengguna VB harus memiliki cara untuk mengatasi masalah ini dengan cara yang masuk akal :) Tapi pendekatan seperti itu menghindari saya :)

@CyrusNajmabadi , Jika Anda benar-benar perlu melakukan perhitungan hash Anda sendiri dalam kode pengguna, CRC32 mungkin berfungsi karena ini adalah kombinasi dari pencarian tabel dan XOR (tetapi bukan aritmatika yang dapat meluap). Ada beberapa kelemahan meskipun:

  1. CRC32 tidak memiliki entropi yang besar (tapi sepertinya masih lebih baik daripada yang dipancarkan Roslyn sekarang).
  2. Anda harus meletakkan 256 entri tabel pencarian di suatu tempat dalam kode atau memancarkan kode untuk menghasilkan tabel pencarian.

Jika Anda belum melakukannya, saya harap Anda dapat mendeteksi tipe HashCode dan menggunakannya jika memungkinkan karena XXHash seharusnya jauh lebih baik.

@morganbr Lihat https://github.com/dotnet/roslyn/pull/24161

Kami melakukan hal berikut:

  1. Gunakan System.HashCode jika tersedia. Selesai.
  2. Jika tidak, jika dalam C#:
    2a. Jika tidak dalam mode centang: Hasilkan hash yang tidak digulung.
    2b. Jika dalam mode-tercentang: Hasilkan hash yang tidak digulung, dibungkus dengan 'tidak dicentang{}'.
  3. Jika tidak, jika di VB:
    3b. Jika tidak dalam mode centang: Hasilkan hash yang tidak digulung.
    3c. Jika dalam mode centang, tetapi memiliki akses ke System.ValueTuple: Hasilkan Return (a, b, c, ...).GetHashCode()
    3d. Jika dalam mode centang tanpa akses ke System.ValueTuple. Hasilkan hash yang tidak digulung, tetapi tambahkan komentar di VB yang sangat mungkin meluap.

Ini '3d' yang sangat disayangkan. Pada dasarnya, seseorang yang menggunakan VB tetapi tidak menggunakan ValueTuple atau Sistem terbaru, tidak akan dapat menggunakan kami untuk mendapatkan algoritme hash yang wajar yang dihasilkan untuk mereka.

Anda harus meletakkan 256 entri tabel pencarian di suatu tempat dalam kode

Ini akan sangat tidak menyenangkan :)

Apakah kode pembuatan tabel juga tidak enak? Setidaknya mengikuti contoh Wikipedia , itu tidak banyak kode (tetapi masih harus pergi ke suatu tempat di sumber pengguna).

Betapa buruknya menambahkan sumber HashCode ke proyek seperti yang dilakukan Roslyn (dengan IL) dengan definisi kelas atribut kompiler (yang lebih sederhana) ketika mereka tidak tersedia melalui Majelis yang direferensikan?

Betapa buruknya menambahkan sumber HashCode ke proyek seperti yang dilakukan Roslyn dengan definisi kelas atribut kompiler (yang lebih sederhana) ketika mereka tidak tersedia melalui Majelis yang direferensikan?

  1. Apakah sumber HashCode tidak memerlukan perilaku overflow?
  2. Saya telah membaca sekilas sumber HashCode. Ini tidak sepele. Menghasilkan semua goop itu ke dalam proyek pengguna akan sangat berat.

Saya hanya terkejut tidak ada cara yang baik untuk membuat matematika overflow bekerja di VB sama sekali :(

Jadi, setidaknya, bahkan jika kita memiliki dua nilai bersama-sama, sepertinya kita harus membuat:

```c#
var hc1 = (uint)(nilai1?.GetHashCode() ?? 0); // bisa meluap
var hc2 = (uint)(nilai2?.GetHashCode() ?? 0); // bisa meluap

        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;
        }

seperti yang bisa QueueRound:

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

Jadi saya tidak benar-benar melihat bagaimana ini akan bekerja :(

Betapa buruknya menambahkan sumber HashCode ke proyek seperti yang dilakukan Roslyn (dengan IL) dengan (banyak

Bagaimana Anda membayangkan ini bekerja? Apa yang akan ditulis pelanggan, dan apa yang akan dilakukan oleh kompiler sebagai tanggapan?

Juga, sesuatu yang akan mengatasi semua ini adalah jika .Net sudah memiliki pembantu publik yang diekspos di API permukaan yang mengkonversi dari uint ke int32 (dan sebaliknya) tanpa overflow.

Apakah itu ada? Jika demikian, saya dapat dengan mudah menulis versi VB, hanya menggunakan ini untuk situasi di mana kita perlu beralih di antara jenis tanpa meluap.

Apakah kode pembuatan tabel juga tidak enak?

Saya akan berpikir begitu. Maksud saya, pikirkan hal ini dari perspektif pelanggan. Mereka hanya menginginkan metode GetHashCode yang layak yang mandiri dengan baik dan memberikan hasil yang masuk akal. Memiliki fitur itu pergi dan mengasapi kode mereka dengan omong kosong tambahan akan sangat tidak menyenangkan. Ini juga sangat buruk mengingat pengalaman C# akan baik-baik saja.

Anda mungkin bisa mendapatkan secara kasar perilaku overflow yang tepat dengan mentransmisikan ke dan dari beberapa kombinasi tipe 64-bit yang ditandatangani dan tidak ditandatangani. Sesuatu seperti ini (belum diuji dan saya tidak tahu sintaks casting VB):

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

Bagaimana Anda tahu yang berikut ini tidak meluap?

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

Atau pemeran terakhir (int32) dalam hal ini?

Saya tidak menyadari itu akan menggunakan operasi konv yang diperiksa overflow. Saya kira Anda bisa menutupinya hingga 32 bit sebelum melakukan casting:

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

mungkin 31 bit, sebagai nilai uint32.Max juga akan meluap pada konversi ke Int32 :)

Itu pasti mungkin. Jelek... tapi mungkin :) Ada banyak gips dalam kode ini.

Oke. Saya pikir saya punya solusi yang bisa diterapkan. Inti dari algoritma yang kami hasilkan hari ini adalah:

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

Katakanlah kita mengerjakan matematika 64bit, tetapi "kode hash" telah dibatasi hingga 32 bit. Maka <largest_32_bit> * -1521134295 + <largest_32_bit> tidak akan overflow 64 bit. Jadi kita selalu dapat menghitung dalam 64 bit, lalu menekan ke 32 (atau 32 bit) untuk memastikan bahwa putaran berikutnya tidak akan meluap.

Terima kasih!

@MaStr11 @morganbr @sharwell dan semua orang di sini. Saya telah memperbarui kode saya untuk menghasilkan yang berikut untuk 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)

Dapatkah seseorang kewarasan memeriksa saya untuk memastikan bahwa ini masuk akal dan tidak boleh meluap bahkan dengan mode yang dicentang?

@CyrusNajmabadi , itu tidak akan meluap (karena Int64.Max = Int32.Max*Int32.Max dan konstanta Anda jauh lebih kecil dari itu) tetapi Anda menutupi bit tinggi ke nol, jadi itu hanya hash 31-bit. Apakah membiarkan bit tinggi tetap menyala?

@CyrusNajmabadi hashCode adalah Long yang dapat di mana saja dari 0 hingga Integer.MaxValue . Mengapa saya mendapatkan ini?

image

Tapi tidak, itu tidak bisa benar-benar meluap.

Btw- Saya lebih suka Roslyn menambahkan paket NuGet daripada menambahkan hash suboptimal.

tapi Anda menutupi bit tinggi ke nol, jadi itu hanya hash 31-bit. Apakah membiarkan bit tinggi tetap menyala?

Itu poin yang bagus. Saya pikir saya sedang memikirkan algoritma lain yang menggunakan uints. Jadi untuk mengonversi dengan aman dari panjang ke uint, saya tidak perlu menyertakan bit tanda. Namun, karena ini semua adalah matematika yang ditandatangani, saya pikir akan baik-baik saja untuk hanya menutupi 0xffffffff memastikan kami hanya menyimpan 32bit terbawah setelah menambahkan setiap entri.

Saya lebih suka Roslyn menambahkan paket NuGet daripada menambahkan hash suboptimal.

Pengguna sudah dapat melakukannya jika mereka mau. Ini tentang apa yang harus dilakukan ketika pengguna tidak, atau tidak bisa, menambahkan dependensi tersebut. Ini juga tentang menyediakan hash yang cukup 'cukup baik' untuk pengguna. yaitu sesuatu yang lebih baik daripada pendekatan umum "x + y + z" yang sering dilakukan orang. Ini tidak dimaksudkan untuk menjadi 'optimal' karena tidak ada definisi yang baik tentang apa yang 'optimal' dalam hal hashing untuk semua pengguna. Perhatikan bahwa pendekatan yang kita ambil di sini adalah pendekatan yang sudah dikeluarkan oleh kompiler untuk tipe anonim. Ini menunjukkan perilaku yang cukup baik tanpa menambahkan banyak kerumitan pada kode pengguna. Seiring waktu, karena semakin banyak pengguna dapat bergerak maju, hal tersebut dapat perlahan menghilang dan diganti dengan HashCode.Combine untuk kebanyakan orang.

Jadi saya mengerjakannya sedikit dan menemukan yang berikut yang menurut saya mengatasi semua masalah:

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

Bagian yang menarik secara khusus memanggil .GetHashCode() pada nilai int64 yang dihasilkan oleh (hashCode * -1521134295 + a.GetHashCode()) . Memanggil .GetHashCode pada nilai 64 bit ini memiliki dua properti yang baik untuk kebutuhan kita. Pertama, ini memastikan bahwa hashCode hanya menyimpan nilai int32 legal di dalamnya (yang membuat pemeran kembali terakhir selalu aman untuk dilakukan). Kedua, ini memastikan bahwa kami tidak kehilangan informasi berharga apa pun di 32 bit teratas dari nilai temp int64 yang sedang kami kerjakan.

@CyrusNajmabadi Sebenarnya menawarkan untuk menginstal paket adalah apa yang saya tanyakan. Menyelamatkan saya dari keharusan melakukannya.

Jika Anda mengetik HashCode, maka jika System.HashCode disediakan dalam paket nuget MS, maka Roslyn akan menawarkannya.

Saya ingin itu menghasilkan kelebihan GetHashCode yang tidak ada dan menginstal paket dalam operasi yang sama.

Saya tidak berpikir itu pilihan yang tepat untuk sebagian besar pengguna. Menambahkan dependensi adalah operasi kelas berat yang tidak boleh dipaksakan oleh pengguna. Pengguna dapat memutuskan waktu yang tepat untuk membuat pilihan tersebut, dan IDE akan menghargainya. Itulah pendekatan yang kami ambil dengan semua fitur kami hingga sekarang, dan ini adalah pendekatan yang sehat yang tampaknya disukai orang.

Catatan: paket nuget apa yang termasuk dalam api ini untuk kami tambahkan referensi?

Implementasinya ada di System.Private.CoreLib.dll, jadi itu akan datang sebagai bagian dari paket runtime. Kontraknya adalah System.Runtime.dll.

Oke. Jika itu masalahnya, maka sepertinya pengguna akan mendapatkan ini jika/ketika mereka pindah ke Kerangka Target yang lebih baru. Hal semacam itu sama sekali bukan langkah saya akan melakukan "hasilkan sama dengan + kode hash" untuk proyek pengguna.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

GitAntoinee picture GitAntoinee  ·  3Komentar

noahfalk picture noahfalk  ·  3Komentar

yahorsi picture yahorsi  ·  3Komentar

Timovzl picture Timovzl  ·  3Komentar

sahithreddyk picture sahithreddyk  ·  3Komentar