Latar Belakang
Saya baru-baru ini dalam proses bertahan wawancara teknologi melelahkan untuk posisi yang menggunakan .NET stack, beberapa di antaranya termasuk pertanyaan konyol seperti ini , dan beberapa pertanyaan yang lebih valid. Saya baru-baru ini menemukan masalah yang mungkin valid tetapi saya ingin memastikan dengan komunitas di sini.
Ketika ditanya oleh pewawancara bagaimana saya akan menghitung frekuensi kata-kata dalam dokumen teks dan memberi peringkat hasilnya, saya menjawab bahwa saya akan menghitungnya
- Gunakan objek aliran, letakkan file teks dalam memori sebagai string.
- Pisahkan string menjadi sebuah array pada spasi sambil mengabaikan tanda baca.
- Gunakan LINQ terhadap array ke
.GroupBy()
dan.Count()
, laluOrderBy()
ucapkan hitungan.
Saya salah menjawab karena dua alasan:
- Streaming seluruh file teks ke dalam memori bisa menjadi bencana. Bagaimana jika itu seluruh ensiklopedia? Alih-alih, saya harus mengalirkan satu blok pada satu waktu dan mulai membangun tabel hash.
- LINQ terlalu mahal dan membutuhkan terlalu banyak siklus pemrosesan. Saya seharusnya membangun tabel hash dan, untuk setiap iterasi, hanya menambahkan kata ke tabel hash jika tidak ada dan kemudian menambah itu dihitung.
Alasan pertama tampaknya, well, masuk akal. Tapi yang kedua memberi saya lebih banyak jeda. Saya berpikir bahwa salah satu nilai jual LINQ adalah bahwa ia hanya mengabstraksi operasi tingkat rendah seperti tabel hash tetapi bahwa, di bawah tabir, masih implementasi yang sama.
Pertanyaan
Selain dari beberapa siklus pemrosesan tambahan untuk memanggil metode abstrak apa pun, apakah LINQ membutuhkan siklus pemrosesan yang lebih signifikan untuk menyelesaikan tugas iterasi data yang diberikan daripada tugas tingkat rendah (seperti membuat tabel hash)?
sumber
Jawaban:
Saya akan mengatakan kelemahan utama dari jawaban ini adalah kurang penggunaan Linq dan lebih banyak operator tertentu yang dipilih.
GroupBy
mengambil setiap elemen dan memproyeksikannya ke kunci dan nilai yang masuk ke pencarian. Dengan kata lain, setiap kata akan menambahkan sesuatu ke pencarian.Implementasi naif
.GroupBy(e => e)
akan menyimpan salinan setiap kata dalam materi sumber, membuat pencarian terakhir hampir sebesar bahan sumber asli. Bahkan jika kami memproyeksikan nilainya dengan.GroupBy(e => e, e => null)
kami menciptakan pencarian besar nilai-nilai kecil.Yang kami inginkan adalah operator yang hanya menyimpan informasi yang diperlukan, yaitu satu salinan dari setiap kata dan jumlah kata itu sejauh ini. Untuk itu, kita bisa menggunakan
Aggregate
:Dari sini, ada beberapa cara yang dapat kami lakukan untuk mempercepat ini:
sumber
Saya pikir Anda memiliki jalan keluar yang sempit, pewawancara tidak benar-benar tahu apa yang dia bicarakan. Lebih buruk lagi, dia percaya ada jawaban yang "benar". Jika dia adalah seseorang yang Anda ingin bekerja, saya berharap dia mengambil jawaban awal Anda, mencari tahu mengapa Anda memilihnya, dan kemudian menantang Anda untuk membuatnya lebih baik jika dia dapat menemukan masalah dengannya.
LINQ membuat orang takut karena terlihat seperti sulap. Ini sebenarnya sangat sederhana (Begitu sederhana, Anda harus menjadi jenius untuk membuatnya)
Dikompilasi menjadi:
(Tolong jangan berteriak jika aku salah sintaks, sudah lewat tengah malam dan aku di tempat tidur :))
Di mana ada metode ekstensi pada IEnumerable yang mengambil lambda. Lambda hanya (dalam hal ini) dikompilasi untuk delegasi:
Metode Where hanya menambahkan SEMUA instance item di mana AMethod mengembalikan true ke koleksi yang dikembalikan.
Tidak ada alasan yang lebih lambat daripada melakukan foreach dan menambahkan semua item yang cocok ke koleksi di loop itu. Bahkan metode ekstensi Where mungkin melakukan hal itu. Keajaiban nyata datang ketika Anda harus menyuntikkan kriteria alternatif di mana.
Seperti yang saya sebutkan di atas dalam komentar saya, beberapa contoh terkait sangat salah. Dan informasi yang salah inilah yang menyebabkan masalah.
Akhirnya, jika wawancara memberi Anda kesempatan, Anda bisa mengatakan bahwa:
LINQ mudah dibaca, terutama ketika Anda mulai memperkenalkan proyeksi dan pengelompokan yang menarik. Kode yang mudah dibaca adalah mudah [y | ier] untuk mempertahankan kode yang merupakan kemenangan.
Akan sangat mudah untuk mengukur kinerja jika itu benar-benar hambatan dan menggantinya dengan yang lain.
sumber
Count
untuk beberapa skenario sempit yang bekerja buruk dengan enkapsulasi. Menggabungkan daftar sejuta item dan iterator empat item, danCount
harus membutuhkan sekitar 5 operasi, tetapi sebaliknya akan membutuhkan satu juta. Saya berharap MS akan mendefinisikanIEnhancedEnumerator
denganint Move(int)
metode yang akan mengembalikan 0 jika sukses, atau mengembalikan jumlah kekurangan jika terjadi kegagalan (jadi melakukanMove(1000003)
pada yang baru dibuatList<T>.Enumerator
dari daftar jutaan item akan mengembalikan 3). Implementasi apa pun ...IEnumerable<T>
dapat dibungkus dalam implementasiIEnhancedEnumerator
, tetapi jenis yang mengimplementasikanIEnhancedEnumerator
secara langsung dapat memungkinkan peningkatan pesanan yang sangat besar pada banyak operasi, dan bahkan hal-hal seperti pengembalian dariAppend
dapat memaparkan kemampuan pencarian konstituen yang cepat dicari.