Pertanyaan "Jangan gunakan StringBuilder atau foreach di jalur kode panas ini"


Saya sedang menjelajah kode sumber dari Open Source SignalR proyek, dan saya melihat kode diff ini yang berhak "Jangan gunakan StringBuilder atau foreach di jalur kode panas ini" :

-           public static string MakeCursor(IEnumerable<Cursor> cursors)
+           public static string MakeCursor(IList<Cursor> cursors)
            { 
-               var sb = new StringBuilder();
-               bool first = true;
-               foreach (var c in cursors)
+               var result = "";
+               for (int i = 0; i < cursors.Count; i++)
                {
-                   if (!first)
+                   if (i > 0)
                    {
-                       sb.Append('|');
+                       result += '|';
                    }
-                   sb.Append(Escape(c.Key));
-                   sb.Append(',');
-                   sb.Append(c.Id);
-                   first = false;
+                   result += Escape(cursors[i].Key);
+                   result += ',';
+                   result += cursors[i].Id;
                }
-               return sb.ToString();
+               return result;
            }

Saya mengerti mengapa foreach terkadang kurang efisien, dan mengapa itu digantikan oleh untuk.

Namun, saya belajar dan merasakan bahwa StringBuilder adalah cara paling efisien untuk menggabungkan string. Jadi saya bertanya-tanya mengapa penulis memutuskan untuk menggantinya dengan penggabungan standar.

Apa yang salah di sini dan secara umum tentang menggunakan StringBuilder?


32
2017-09-11 14:39


asal


Jawaban:


Saya membuat perubahan kode dan ya itu membuat perbedaan besar dalam jumlah alokasi (GetEnumerator ()) panggilan vs tidak. Bayangkan kode ini jutaan kali per detik. Jumlah pencacah yang dialokasikan konyol dan dapat dihindari.

edit: Kami sekarang membalikkan kontrol untuk menghindari alokasi (menulis ke penulis secara langsung): https://github.com/SignalR/SignalR/blob/2.0.2/src/Microsoft.AspNet.SignalR.Core/Messaging/Cursor.cs#L36


30
2017-09-11 21:37



Hanya berharap pria yang mengubah ini sebenarnya mengukur perbedaannya.

  • Ada overhead dalam instantiating pembuat string baru setiap saat. Ini juga memberi tekanan pada pengumpulan memori / sampah.
  • Compiler dapat menghasilkan kode 'stringbuilderlike' untuk penggabungan sederhana
  • The FOR mungkin sebenarnya lebih lambat karena mungkin memerlukan pemeriksaan batas yang tidak dilakukan dengan loop foreach sebagai compiler 'tahu' mereka dalam batas.

3
2017-09-11 14:48



Itu tergantung pada jumlah Cursors diberikan ke fungsi.

Sebagian besar perbandingan antara kedua apporaches tampaknya menguntungkan StringBuilder over string concatination ketika berkeringat 4-10 string. Saya kemungkinan besar akan mendukung StringBuilder jika saya tidak memiliki alasan eksplisit tidak juga (misalnya perbandingan kinerja dari dua pendekatan untuk masalah / aplikasi saya). Saya akan mempertimbangkan untuk mengalokasikan pra-buffer di StringBuilder untuk menghindari (banyak) realokasi.

Lihat Rangkaian string vs String Builder. Kinerja dan Berkumpul dengan StringBuilders vs. Strings untuk beberapa diskusi tentang subjek.


2
2017-09-11 16:47



Berapa banyak rangkai yang Anda lakukan? Jika banyak, gunakan StringBuilder. Jika hanya beberapa, maka overhead menciptakan StringBuilder akan melebihi keuntungan apa pun.


1
2017-09-11 14:43



Saya akan menaruh uang saya

           StringBuilder sb = new StringBuilder();
           bool first = true;
           foreach (Cursor c in cursors)
           {
                if (first)
                {
                   first = false;  // only assign it once
                }
                else
                {
                    sb.Append('|');
                }
                sb.Append(Escape(c.Key) + ',' + c.Id);
            }
            return sb.ToString();

Tapi saya akan menaruh uang saya dan pembaruan dari dfowler. Lihat tautan di jawabannya.


1
2017-09-11 15:46