2010-07-20 27 views
7

thể trùng lặp:
String concatenation vs String Builder. Performancehoạt động chuỗi nào tốt hơn?

Bất kỳ sự khác biệt (hiệu suất và sử dụng bộ nhớ) giữa hai tùy chọn sau đây?

tùy chọn 1:

StringBuilder msgEntry = new StringBuilder(); 
msgEntry.AppendLine("<" + timeTag + ">" + timeStamp + "</" + timeTag + ">"); 

tùy chọn 2:

StringBuilder msgEntry = new StringBuilder(); 
msgEntry.Append("<"); 
msgEntry.Append(timeTag); 
msgEntry.Append(">"); 
msgEntry.Append(timeStamp); 
msgEntry.Append("</"); 
msgEntry.Append(timeTag); 
msgEntry.Append(">\n"); 
+4

Trừ khi bạn đã phát hiện ra một vấn đề hiệu suất, hoặc mã này được thực hiện hàng ngàn lần bên trong một vòng lặp hoặc một cái gì đó, sự khác biệt là không liên quan. Đi với tùy chọn dễ đọc nhất. – JohnFx

+1

Cũng có thể đáng xem xét việc sử dụng một số lớp XML hiện có.Có vẻ như bạn có thể không quen thuộc với khá nhiều khung công tác (vì bạn không biết về các chức năng định dạng chuỗi), tôi khuyên bạn nên chọn một thứ gì đó giống như một cuốn sách 21 ngày để đọc một cách lười biếng, để tìm hiểu về những thứ đó. – overslacked

+1

Tôi bỏ phiếu cho mở lại: Khi không được sử dụng trong một vòng lặp tình hình thực hiện là khác nhau! Trong trường hợp cụ thể này, chuỗi nối (tùy chọn 1) sẽ sử dụng một cuộc gọi duy nhất tới String.Concat thực hiện tốt hơn bảy lệnh gọi tới StringBuilder.Append. Tôi vừa định viết một câu trả lời chỉ ra điều này ... – MartinStettner

Trả lời

27

Thứ hai là thể tốt hơn một chút trong điều khoản sử dụng bộ nhớ, bởi vì nó không cần phải tính toán chuỗi trung gian ... nhưng nó ít có thể đọc được, IMO.

Cá nhân tôi muốn sử dụng:

msgEntry.AppendFormat("<{0}>{1}</{0}>", timeTag, timeStamp); 

Bạn chưa thể hiện những gì bạn muốn làm với các StringBuilder sau đó. Nếu bạn chỉ cần chuyển đổi nó thành một chuỗi, thì tôi sẽ sử dụng:

string text = string.Format("<{0}>{1}</{0}>", timeTag, timeStamp); 

để bắt đầu.

Hiệu suất như thế nào? Vâng, có lẽ tệ hơn - sau khi tất cả, nó phải phân tích chuỗi định dạng. Nhưng trừ khi bạn đã đo lường điều này và thấy nó là nút cổ chai, tại sao bạn lo lắng?

Nói chung:

  • Hãy chắc chắn rằng kiến ​​trúc của bạn là hợp lý hiệu quả - đó là khó có thể thay đổi sau đó.
  • Cân bằng thiết kế nội bộ giữa hiệu quả và tính đơn giản của bạn, với trọng tâm là khả năng thử nghiệm; việc thay đổi thiết kế sau này có thể mất một lúc, nhưng thường có thể khả thi nếu không có sự cố tương thích.
  • Viết triển khai của bạn để có thể đọc được càng tốt.
  • Đo lường hệ thống để tìm hiểu xem nó có hoạt động tốt hay không và nơi bị tắc nghẽn. Chúng hầu như không bao giờ có mã như thế này. (Chúng tôi không nói về chuỗi nối trong vòng lặp ở đây, sau khi tất cả.)
  • Khi bạn đã tìm thấy nút cổ chai, hãy thử các tối ưu hóa khác nhau và đo chúng quá. Đừng cho rằng một cái gì đó bạn nghĩ sẽ nhanh hơn sẽ thực sự là sẽ nhanh hơn.

Hoặc mảng để vượt qua để Concat ... chúng ta không biết loại timeStamp vì vậy chúng tôi không thể nói chính xác những gì đang xảy ra ở đó; ở dạng thứ hai nó có thể được nối thêm tại chỗ trong khi hình thức đầu tiên có thể cần phải đóng hộp nó và sau đó chuyển đổi nó thành một chuỗi trước khi thực hiện nối.

Việc triển khai chính xác để phân bổ lại vv cũng có thể đã thay đổi giữa .NET 3.5 và .NET 4 (Tôi biết một số bit thực hiện có). Không có điểm chuẩn rất cẩn thận, tôi thực sự không muốn nói nhanh hơn ... nhưng khả năng đọc dễ gọi hơn, mặc dù chủ quan.

+0

Jon Skeet luôn nhanh hơn mọi người khác! Tôi đã đánh máy đó! –

+0

+1 Như mọi khi ... đi trước một bước. –

+0

Đó là cuộc thi đánh máy đôi khi, tôi nói với bạn ...;) –

3

Nói chung, StringBuilder ... nhưng khi bạn nói về hiệu suất, kiểm tra thực tế chỉ là đo. Đặc biệt là cho LOTS của chuỗi thay đổi, StringBuilder chắc chắn là con đường để đi. Đối với một vài chuỗi ... nó có lẽ chỉ dễ dàng hơn để nối chúng với toán tử +.

+0

+1 để chỉ ra rằng StringBuilder luôn tốt hơn khi bạn có nhiều chuỗi nối. – Jagd

+0

Không phải trong trường hợp này! Đối với một kết nối duy nhất (bên ngoài một vòng lặp), một cuộc gọi duy nhất đến String.Concat sẽ được thực hiện cho tùy chọn đầu tiên. Tùy chọn thứ hai sẽ mất bảy cuộc gọi đến StringBuilder.Append. – MartinStettner

+0

@Martin; "nói chung". Jon đã làm cùng một điểm ... Mặc dù hùng hồn hơn tôi và với nhiều chi tiết hơn. Nhưng lực đẩy là giống hệt nhau. – Steve

2

Tôi sẽ sử dụng .AppendFormat(); trong trường hợp đó

StringBuilder msgEntry = new StringBuilder(); 
msgEntry.AppendFormat("<{0}>{1}</{0}>", timeTag , timeStamp); 
+0

+1 Tôi chưa bao giờ nhận thấy phương pháp đó một trình xây dựng chuỗi trước. Thật tuyệt vời! không còn sb.append (định dạng (...)) cho tôi nữa! – JohnFx

2

Cá nhân, tôi sẽ chọn không, và sử dụng

msgEntry.AppendFormat("<{0}>{1}</{0}>", timeTag, timeStamp); 
0

Dòng thứ hai là tốt hơn như bạn không nhận được nhiều nếu có benifit từ StringBuilder trong lần đầu tiên. Tuy nhiên đối với một chuỗi nhỏ concat như thế tôi sẽ không bận tâm với stringbuilder

1

Nếu đó là tất cả những gì bạn đang làm, không sử dụng StringBuilder. Đó là quá nhiều chi phí với một hit dễ đọc.

Hãy thử điều này:

string.Format("<{0}>{1}</{2}>", timeTag, timeStamp, timeTag); 
0

Yes.

Nếu bạn định chọn tùy chọn 1, không có điểm nào khi sử dụng số StringBuilder. Bạn vẫn đang thực hiện chuỗi nối và sẽ kết thúc với nhiều chuỗi tạm thời được tạo và loại bỏ trong bộ nhớ.

Đối với một cái gì đó như thế này, có lẽ bạn nên sử dụng String.Format()

+0

Chỉ có thêm một chuỗi tạm thời được yêu cầu ở đây - trình biên dịch sẽ gọi 'string.Concat (" <", timeTag,"> ", timeStamp," ")' (có chuyển đổi khi cần). –

-1

Luôn luôn sử dụng StringBuilder.Append() cho chuỗi nối. Nhà điều hành chuỗi + gây ra phân bổ mới cho từng mục mới.

+1

Không, không. Nó sẽ gọi 'string.Concat' một lần, vì vậy nó sẽ tạo thêm một chuỗi trung gian. Bạn chỉ nên sử dụng 'StringBuilder.Append()' khi nó thực sự có ý nghĩa để làm như vậy - nếu bạn đang làm tất cả các kết nối trong một biểu thức duy nhất và muốn một chuỗi ở cuối, sử dụng + là hoàn toàn tốt. –

+0

Vâng, chỉ cần làm một bài kiểm tra ngắn. Rõ ràng tôi đã sai. Cảm ơn vì sự đúng đắn của bạn. –

+0

Nếu bạn có các chuỗi tĩnh lân cận được phân tách bằng dấu "+", trình biên dịch sẽ kết hợp chúng tại thời gian biên dịch. Ngoài ra, nếu chỉ ghép nối 2 hoặc 3 chuỗi, "+" sẽ hoạt động tốt hơn ngay lập tức đối tượng trình xây dựng chuỗi. – adam0101

1

Tôi đã dựa vào lời khuyên trong MSDN Performance Tips and Tricks in .NET Applications để tư vấn cho việc sử dụng StringBuilder cho thao tác chuỗi phức tạp.

Nó tiếp tục tư vấn:

Cân bằng Có một số overhead liên quan đến việc tạo ra một đối tượng StringBuilder , cả về thời gian và bộ nhớ. Trên máy có bộ nhớ nhanh, StringBuilder trở nên đáng giá nếu bạn đang thực hiện khoảng năm thao tác. Theo quy tắc chung, tôi có thể nói 10 hoặc hoạt động chuỗi khác là một biện pháp cho chi phí trên bất kỳ máy nào, thậm chí còn chậm hơn.

Tôi cũng sẽ xem xét lời khuyên này từ tối ưu hóa mã.chương trình Net:

Nó đặc biệt quan trọng đối với tiền phân bổ kích thước của chuỗi. Nếu không, StringBuilder vẫn là nhanh hơn, nhưng nếu bạn có thể dự đoán độ dài cuối cùng của chuỗi cuối cùng, hãy đặt trước.

Đó là do dung lượng mặc định của StringBuilder là 16. Nó tự động thay đổi kích thước khi dung lượng bị vượt quá - nó tăng gấp đôi mỗi lần. Vì vậy, bạn có thể có một số resizings không cần thiết nếu bạn không đặt công suất ban đầu. Bạn có thể đếm số lượng ký tự được mong đợi tối đa trong ví dụ của mình và khởi tạo StringBuilder để nó không thay đổi kích thước. Điều đó sẽ tiết kiệm một số CPU.

Và đây là some additional advice from MSDN:

Việc thực hiện một hoạt động nối cho một String hoặc đối tượng StringBuilder phụ thuộc vào cách thường là một phân bổ bộ nhớ xảy ra. A Hoạt động nối chuỗi luôn luôn cấp phát bộ nhớ, trong khi thao tác ghép nối StringBuilder chỉ phân bổ bộ nhớ nếu Bộ đệm đối tượng StringBuilder quá nhỏ để chứa dữ liệu mới. Do đó, lớp Chuỗi là thích hợp hơn cho một phép nối nếu số lượng đối tượng Chuỗi cố định được ghép nối. Trong trường hợp đó , các phép nối riêng lẻ hoạt động thậm chí có thể được kết hợp thành một thao tác đơn lẻ của trình biên dịch. A Đối tượng StringBuilder thích hợp hơn cho thao tác ghép nối nếu số tùy ý của các chuỗi là được ghép nối; ví dụ: nếu vòng lặp nối một số ngẫu nhiên là chuỗi đầu vào của người dùng.

+0

Tôi sẽ bắt đầu bằng cách tìm hiểu xem nó có thực sự quan trọng trước khi lo lắng về việc tính toán kích thước phù hợp để phân bổ trước không. Cũng đáng chú ý là liên kết đầu tiên của bạn là từ tháng 8 năm 2001 ... trước khi .NET 1.0 thậm chí xuất hiện. Tôi biết chắc chắn rằng việc thực hiện StringBuilder đã thay đổi đáng kể giữa .NET 3.5 và .NET 4, và tôi sẽ không ngạc nhiên nếu nó đã thay đổi trong các phiên bản khác nữa. Nó vẫn có giá trị tránh nối trong vòng, chắc chắn ... nhưng các quy tắc của ngón tay cái chắc chắn có thể thay đổi theo thời gian. –

+0

@Jon Skeet Tôi nhận thấy bài viết đầu tiên không phải là gần đây, nhưng nó vẫn được trích dẫn thường xuyên, và nếu nó không còn hợp lệ nữa, tôi nghĩ rằng nhân viên của Microsoft sẽ loại bỏ hoặc cập nhật nó. Và trong bài viết thứ ba, áp dụng cho .Net 4, họ đề cập đến việc thay đổi kích thước trong "Cân nhắc hiệu suất". Và vì đôi khi rất dễ dàng để tính toán dung lượng tối đa cần thiết cho một StringBuilder, vì nó sẽ là trong trường hợp này, tôi chắc chắn sẽ làm điều đó nếu chuỗi nối thường xuyên trong ứng dụng. Đó là một nỗ lực nhỏ như vậy. – DOK

+0

Thường xuyên như thế nào? Tôi sẽ không làm điều đó trừ khi bạn có lý do chính đáng. Bạn sẽ nói bao nhiêu nỗ lực trong trường hợp này? Điều gì về khi bạn đang đọc mã? Làm thế nào bạn sẽ làm việc ra chiều dài 'timeStamp'? –

0

Ví dụ đầu tiên có vẻ sai với tôi. Điểm tạo đối tượng StringBuider khi bạn đang nối chuỗi trước khi đưa chúng vào StringBuilder là gì?

Cũng lưu ý rằng:

  • Nối trả về thể hiện của StringBuilder, vì vậy các cuộc gọi đến Nối và AppendLine có thể được xích lại với nhau
  • Vào cuối ví dụ thứ hai, bạn có thể sử dụng AppendLine (">") thay vì Nối ("> \ n")
 StringBuilder msgEntry = new StringBuilder(); 
    msgEntry.Append("<") 
     .Append(timeTag) 
     .Append(">") 
     .Append(timeStamp) 
     .Append("</") 
     .Append(timeTag) 
     .AppendLine(">"); 

Cá nhân tôi muốn làm

 string.Format("<{0}>{1}</{0}>", timeTag, timeStamp);

trừ khi bạn cần StringBuilder cho một thứ khác.

Các vấn đề liên quan