Làm thế nào chậm là ngoại lệ .NET?


143

Tôi không muốn một cuộc thảo luận về khi nào và không nên đưa ra ngoại lệ. Tôi muốn giải quyết một vấn đề đơn giản. 99% thời gian để tranh luận về việc không ném ngoại lệ xoay quanh việc họ bị chậm trong khi phía bên kia tuyên bố (với bài kiểm tra điểm chuẩn) rằng tốc độ không phải là vấn đề. Tôi đã đọc rất nhiều blog, bài viết và bài viết liên quan đến mặt này hay mặt khác. Vậy đó là cái gì?

Một số liên kết từ các câu trả lời: Skeet , Mariani , Brumme .


13
có những lời nói dối, lời nói dối chết tiệt và điểm chuẩn. :)
gbjbaanb

Thật không may, một số câu trả lời được bình chọn cao ở đây đã bỏ lỡ rằng câu hỏi là "ngoại lệ chậm đến mức nào?", Và đặc biệt được hỏi để tránh chủ đề về mức độ thường xuyên sử dụng chúng. Một câu trả lời đơn giản cho câu hỏi mà thực sự được hỏi là ..... Trên Windows CLR, các ngoại lệ chậm hơn 750 lần so với giá trị trả về.
David Jeske

Câu trả lời:


207

Tôi đứng về phía "không chậm" - hay chính xác hơn là "không đủ chậm để khiến nó đáng để tránh chúng trong sử dụng bình thường". Tôi đã viết hai bài báo ngắn về điều này. Có những lời chỉ trích về khía cạnh điểm chuẩn, phần lớn là "trong cuộc sống thực sẽ có nhiều ngăn xếp hơn để đi qua, vì vậy bạn sẽ thổi bộ đệm, v.v." - nhưng sử dụng mã lỗi để làm việc theo cách của bạn cũng sẽ ngăn xếp thổi bộ đệm, vì vậy tôi không thấy đó là một đối số đặc biệt tốt.

Chỉ cần làm rõ - tôi không hỗ trợ sử dụng các ngoại lệ khi chúng không logic. Chẳng hạn, int.TryParsehoàn toàn thích hợp để chuyển đổi dữ liệu từ người dùng. Thật thích hợp khi đọc tệp do máy tạo, trong đó lỗi có nghĩa là "Tệp không có định dạng như vậy, tôi thực sự không muốn xử lý việc này vì tôi không biết điều gì khác có thể sai. "

Khi sử dụng các ngoại lệ trong "chỉ những trường hợp hợp lý", tôi chưa bao giờ thấy một ứng dụng nào có hiệu năng bị suy giảm đáng kể bởi các ngoại lệ. Về cơ bản, các trường hợp ngoại lệ không nên xảy ra thường xuyên trừ khi bạn gặp sự cố chính xác đáng kể và nếu bạn gặp sự cố chính xác đáng kể thì hiệu suất không phải là vấn đề lớn nhất mà bạn gặp phải.


2
thật không may, mọi người được cho là ngoại lệ là miễn phí, hãy sử dụng chúng cho chức năng 'chính xác' tầm thường, chúng nên được sử dụng như bạn nói, khi mọi thứ đã sai - trong trường hợp 'đặc biệt'
gbjbaanb

4
Có, mọi người chắc chắn nên biết rằng có một chi phí hiệu suất liên quan đến việc sử dụng ngoại lệ không phù hợp. Tôi chỉ nghĩ đó không phải là vấn đề khi chúng được sử dụng một cách thích hợp :)
Jon Skeet

7
@PaulLockwood: Tôi sẽ nói rằng nếu bạn có hơn 200 ngoại lệ mỗi giây , bạn đang lạm dụng các ngoại lệ. Đây rõ ràng không phải là một sự kiện "đặc biệt" nếu nó xảy ra 200 lần mỗi giây. Lưu ý câu cuối cùng của câu trả lời: "Về cơ bản, các trường hợp ngoại lệ không nên xảy ra thường xuyên trừ khi bạn gặp vấn đề chính xác đáng kể và nếu bạn gặp vấn đề chính xác đáng kể thì hiệu suất không phải là vấn đề lớn nhất mà bạn gặp phải."
Jon Skeet

4
@PaulLockwood: Quan điểm của tôi là nếu bạn có hơn 200 ngoại lệ mỗi giây, điều đó có thể đã chỉ ra rằng bạn đang lạm dụng ngoại lệ. Điều đó không làm tôi ngạc nhiên rằng đó thường là trường hợp, nhưng điều đó có nghĩa là khía cạnh hiệu suất sẽ không phải là mối quan tâm đầu tiên của tôi - việc lạm dụng các ngoại lệ sẽ xảy ra. Khi tôi đã xóa tất cả các trường hợp sử dụng ngoại lệ không phù hợp , tôi sẽ không hy vọng chúng là một phần quan trọng của hiệu suất.
Jon Skeet

3
@DavidJeske: Bạn đã bỏ lỡ điểm của câu trả lời. Rõ ràng việc ném một ngoại lệ chậm hơn nhiều so với trả về một giá trị bình thường. Không ai tranh cãi rằng. Câu hỏi là liệu chúng có quá chậm không. Nếu bạn đang ở trong một tình huống phù hợp để đưa ra một ngoại lệ điều đó gây ra vấn đề về hiệu suất, thì có lẽ bạn đã gặp vấn đề lớn hơn - bởi vì điều đó cho thấy rằng có một số lượng lớn sai với hệ thống của bạn. Thông thường, vấn đề thực sự là bạn đang sử dụng ngoại lệ khi chúng không phù hợp để bắt đầu.
Jon Skeet

31

Có câu trả lời dứt khoát cho điều này từ anh chàng đã thực hiện chúng - Chris Brumme. Anh ấy đã viết một bài viết tuyệt vời về chủ đề này (cảnh báo - rất dài) (cảnh báo2 - nó được viết rất tốt, nếu bạn là một tín đồ công nghệ, bạn sẽ đọc nó đến cuối cùng và sau đó phải làm việc hàng giờ sau khi làm việc :) )

Tóm tắt điều hành: họ chậm. Chúng được triển khai dưới dạng ngoại lệ Win32 SEH, vì vậy một số thậm chí sẽ vượt qua ranh giới CPU vòng 0! Rõ ràng trong thế giới thực, bạn sẽ thực hiện rất nhiều công việc khác nên ngoại lệ kỳ quặc sẽ không được chú ý chút nào, nhưng nếu bạn sử dụng chúng cho dòng chương trình thì mong muốn ứng dụng của bạn bị cấm. Đây là một ví dụ khác về máy tiếp thị MS làm cho chúng tôi không hài lòng. Tôi nhớ lại một microsoftie cho chúng tôi biết làm thế nào họ phát sinh hoàn toàn không có chi phí, hoàn thành tosh.

Chris đưa ra một trích dẫn thích hợp:

Trong thực tế, CLR sử dụng ngoại lệ ngay cả trong các phần không được quản lý của động cơ. Tuy nhiên, có một vấn đề hiệu suất dài hạn nghiêm trọng với các ngoại lệ và điều này phải được xem xét trong quyết định của bạn.


Tôi có thể đề cập đến điều này trong các thử nghiệm trong thế giới thực, trong đó một loại nullable khiến ngoại lệ được đưa ra nhiều lần trong "đây là luồng chương trình bình thường", kết thúc với các vấn đề hiệu năng đáng kể. Alwyas hãy nhớ rằng, các trường hợp ngoại lệ là cho các trường hợp đặc biệt, đừng tin bất cứ ai nói khác hoặc bạn sẽ kết thúc với một chuỗi github như thế!
gbjbaanb

8

Tôi không biết mọi người đang nói về điều gì khi họ nói rằng họ chậm chỉ khi họ bị ném.

EDIT: Nếu Ngoại lệ không bị ném, thì điều đó có nghĩa là bạn đang thực hiện Ngoại lệ mới () hoặc đại loại như thế. Nếu không, ngoại lệ sẽ làm cho luồng bị treo và ngăn xếp được di chuyển. Điều này có thể ổn trong các tình huống nhỏ hơn, nhưng trong các trang web có lưu lượng truy cập cao, việc dựa vào các ngoại lệ như một quy trình công việc hoặc cơ chế đường dẫn thực thi chắc chắn sẽ gây ra cho bạn các vấn đề về hiệu suất. Ngoại lệ, không có gì xấu, và rất hữu ích để thể hiện các điều kiện đặc biệt

Luồng công việc ngoại lệ trong ứng dụng .NET sử dụng ngoại lệ cơ hội thứ nhất và thứ hai. Đối với tất cả các trường hợp ngoại lệ, ngay cả khi bạn đang bắt và xử lý chúng, đối tượng ngoại lệ vẫn được tạo và khung vẫn phải đi theo ngăn xếp để tìm kiếm một trình xử lý. Nếu bạn bắt và nghĩ lại tất nhiên sẽ mất nhiều thời gian hơn - bạn sẽ có một ngoại lệ cơ hội đầu tiên, bắt nó, suy nghĩ lại, gây ra một ngoại lệ cơ hội đầu tiên, sau đó không tìm thấy một trình xử lý, sau đó gây ra một ngoại lệ cơ hội thứ hai.

Các ngoại lệ cũng là đối tượng trong heap - vì vậy nếu bạn đang ném hàng tấn ngoại lệ, thì bạn đang gây ra cả vấn đề về hiệu năng và bộ nhớ.

Hơn nữa, theo bản sao "Kiểm tra hiệu suất các ứng dụng web Microsoft .NET" của tôi được viết bởi nhóm ACE:

"Xử lý ngoại lệ rất tốn kém. Việc thực thi chuỗi liên quan bị đình chỉ trong khi CLR truy xuất thông qua ngăn xếp cuộc gọi để tìm trình xử lý ngoại lệ phù hợp và khi tìm thấy, trình xử lý ngoại lệ và một số khối cuối cùng phải có cơ hội thực thi trước khi xử lý thường xuyên có thể được thực hiện. "

Kinh nghiệm của riêng tôi trong lĩnh vực này cho thấy việc giảm các ngoại lệ giúp cải thiện đáng kể hiệu suất. Tất nhiên, có những thứ khác bạn tính đến khi kiểm tra hiệu năng - ví dụ: nếu Đĩa I / O của bạn bị bắn hoặc các truy vấn của bạn chỉ trong vài giây, thì đó sẽ là trọng tâm của bạn. Nhưng việc tìm và loại bỏ các ngoại lệ nên là một phần quan trọng trong chiến lược đó.


1
Không có gì bạn đã viết mâu thuẫn với tuyên bố rằng các ngoại lệ chỉ chậm nếu chúng bị ném. Bạn đã chỉ nói về những tình huống mà họ đang ném. Khi bạn đã "thực hiện một cách đáng kể hiệu năng" bằng cách loại bỏ các ngoại lệ: 1) Đây có phải là điều kiện lỗi thực sự hay chỉ là lỗi người dùng ?
Jon Skeet

2) Bạn có chạy theo trình gỡ lỗi hay không?
Jon Skeet

Điều duy nhất có thể mà bạn có thể làm với một ngoại lệ nếu không ném nó là tạo nó như một đối tượng, điều này là vô nghĩa. Ở dưới trình gỡ lỗi hay không không quan trọng - nó vẫn sẽ chậm hơn. Vâng, có những cái móc sẽ xảy ra với trình gỡ lỗi được đính kèm, nhưng nó vẫn chậm
Cory Foy

4
Tôi biết - Tôi là một phần của đội Premier tại MSFT. :) Chúng ta hãy nói, rất nhiều - hàng ngàn giây trong một số trường hợp cực đoan mà chúng ta đã thấy. Không có gì giống như kết nối với trình gỡ lỗi trực tiếp và chỉ cần nhìn thấy các ngoại lệ nhanh như bạn có thể đọc. Ex là chậm - vì vậy kết nối với DB, vì vậy bạn làm điều đó khi nó có ý nghĩa.
Cory Foy

5
Cory, tôi nghĩ rằng vấn đề "chỉ chậm khi chúng bị ném" là bạn không phải lo lắng về hiệu suất vì sự hiện diện đơn thuần của các khối bắt / cuối cùng. Tức là những thứ này tự chúng không gây ra hiệu năng, chỉ xảy ra trường hợp ngoại lệ thực tế.
Ian Horwill

6

Đối số theo tôi hiểu không phải là ném ngoại lệ là xấu mà họ chậm mỗi lần. Thay vào đó, đó là về việc sử dụng cấu trúc ném / bắt như một cách đầu tiên để kiểm soát logic ứng dụng thông thường, thay vì các cấu trúc có điều kiện truyền thống hơn.

Thông thường trong logic ứng dụng thông thường, bạn thực hiện lặp trong đó cùng một hành động được lặp lại hàng nghìn / triệu lần. Trong trường hợp này, với một số hồ sơ rất đơn giản (xem lớp Đồng hồ bấm giờ), bạn có thể tự mình thấy rằng việc ném một ngoại lệ thay vì nói đơn giản nếu câu lệnh có thể trở nên chậm hơn đáng kể.

Trong thực tế, tôi đã từng đọc rằng nhóm .NET tại Microsoft đã giới thiệu các phương thức TryXXXXX trong .NET 2.0 cho nhiều loại FCL cơ bản đặc biệt vì khách hàng phàn nàn rằng hiệu suất của các ứng dụng của họ quá chậm.

Hóa ra trong nhiều trường hợp, điều này là do khách hàng đã cố gắng chuyển đổi loại giá trị trong một vòng lặp và mỗi lần thử đều thất bại. Một ngoại lệ chuyển đổi đã được ném và sau đó được xử lý bởi một trình xử lý ngoại lệ sau đó nuốt ngoại lệ đó và tiếp tục vòng lặp.

Microsoft hiện khuyên dùng các phương pháp TryXXX nên được sử dụng đặc biệt trong tình huống này để tránh các vấn đề hiệu năng có thể xảy ra.

Tôi có thể sai, nhưng có vẻ như bạn không chắc chắn về tính chính xác của "điểm chuẩn" bạn đã đọc. Giải pháp đơn giản: Hãy tự mình thử.


Tôi nghĩ rằng các hàm "thử" trong nội bộ cũng sử dụng ngoại lệ?
greg

1
Các chức năng "Thử" này không ném ngoại lệ vào bên trong do không phân tích giá trị đầu vào. Tuy nhiên, họ vẫn đưa ra các ngoại lệ cho các tình huống lỗi khác, chẳng hạn như ArgumentException.
Ash

Tôi nghĩ rằng câu trả lời này gần với trọng tâm của vấn đề hơn bất kỳ vấn đề nào khác. Nói 'chỉ sử dụng ngoại lệ trong các trường hợp hợp lý' không thực sự trả lời câu hỏi - cái nhìn sâu sắc thực sự là việc sử dụng ngoại lệ C # cho luồng điều khiển chậm hơn nhiều so với các cấu trúc có điều kiện thông thường. Bạn có thể được tha thứ cho suy nghĩ khác. Trong OCaml, các ngoại lệ ít nhiều là một GOTO và cách thức được chấp nhận để thực hiện ngắt khi sử dụng các tính năng bắt buộc. Trong trường hợp cụ thể của tôi, việc thay thế trong một vòng lặp chặt chẽ int.Pude () cộng với thử / bắt so với int.TryPude () đã giúp tăng hiệu suất đáng kể.
Hugh W

4

Máy chủ XMPP của tôi đã tăng tốc độ lớn (xin lỗi, không có số thực tế, hoàn toàn quan sát) sau khi tôi luôn cố gắng ngăn chặn chúng xảy ra (chẳng hạn như kiểm tra xem ổ cắm có được kết nối trước khi thử đọc thêm dữ liệu không) và tự tìm cách tránh chúng (các phương pháp TryX đã đề cập). Đó là chỉ với khoảng 50 người dùng ảo (trò chuyện) hoạt động.


3
Thật không may, con số sẽ rất hữu ích :( Những thứ như hoạt động của ổ cắm sẽ vượt xa chi phí ngoại lệ, chắc chắn khi không gỡ lỗi. Nếu bạn từng điểm chuẩn đầy đủ, tôi thực sự thích thú khi thấy kết quả.
Jon Skeet

3

Chỉ để thêm kinh nghiệm gần đây của tôi vào cuộc thảo luận này: phù hợp với hầu hết những gì được viết ở trên, tôi thấy việc ném ngoại lệ là cực kỳ chậm khi được thực hiện trên cơ sở lặp đi lặp lại, ngay cả khi không có trình gỡ lỗi chạy. Tôi vừa tăng hiệu suất của một chương trình lớn Tôi đang viết lên 60% bằng cách thay đổi khoảng năm dòng mã: chuyển sang mô hình mã trả về thay vì ném ngoại lệ. Cấp, mã trong câu hỏi đã chạy hàng ngàn lần và có khả năng ném hàng ngàn ngoại lệ trước khi tôi thay đổi nó. Vì vậy, tôi đồng ý với tuyên bố trên: đưa ra các ngoại lệ khi một điều quan trọng thực sự gặp trục trặc, không phải là cách kiểm soát luồng ứng dụng trong bất kỳ tình huống "mong đợi" nào.


2

Nếu bạn so sánh chúng để trả về mã thì chúng chậm như địa ngục. Tuy nhiên, như các áp phích trước đã tuyên bố rằng bạn không muốn sử dụng chương trình bình thường để bạn chỉ đạt được thành công khi có sự cố xảy ra và trong phần lớn các trường hợp, hiệu suất không còn là vấn đề nữa (vì dù sao ngoại lệ cũng có nghĩa là chặn đường).

Chúng chắc chắn có giá trị sử dụng trên các mã lỗi, ưu điểm là IMO rất lớn.


2

Tôi chưa bao giờ có bất kỳ vấn đề hiệu suất với ngoại lệ. Tôi sử dụng ngoại lệ rất nhiều - tôi không bao giờ sử dụng mã trả về nếu có thể. Chúng là một thực hành xấu, và theo tôi, có mùi như mã spaghetti.

Tôi nghĩ tất cả đều tập trung vào cách bạn sử dụng các ngoại lệ: nếu bạn sử dụng chúng như mã trả về (mỗi lệnh gọi trong ngăn xếp bắt và trả lại) thì, vâng, chúng sẽ chậm, bởi vì bạn có mỗi lần bắt / ném.

Nhưng nếu bạn ném ở dưới cùng của ngăn xếp và bắt ở đầu (bạn thay thế toàn bộ chuỗi mã trả lại bằng một lần ném / bắt), tất cả các thao tác tốn kém được thực hiện một lần.

Vào cuối ngày, chúng là một tính năng ngôn ngữ hợp lệ.

Chỉ để chứng minh quan điểm của tôi

Vui lòng chạy mã tại liên kết này (quá lớn cho một câu trả lời).

Kết quả trên máy tính của tôi:

marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM

Dấu thời gian là đầu ra ở đầu, giữa mã trả về và ngoại lệ, ở cuối. Nó mất cùng một thời gian trong cả hai trường hợp. Lưu ý rằng bạn phải biên dịch với tối ưu hóa.


2

Nhưng mono ném ngoại lệ nhanh hơn 10 lần so với chế độ độc lập .net và chế độ độc lập .net ném ngoại lệ nhanh hơn 60 lần so với chế độ gỡ lỗi .net. (Máy kiểm tra có cùng kiểu CPU)

int c = 1000000;
int s = Environment.TickCount;
for (int i = 0; i < c; i++)
{
    try { throw new Exception(); }
    catch { }
}
int d = Environment.TickCount - s;

Console.WriteLine(d + "ms / " + c + " exceptions");

1

Trong chế độ phát hành, chi phí tối thiểu.

Trừ khi bạn sẽ sử dụng các ngoại lệ cho kiểm soát dòng chảy (ví dụ: lối thoát không cục bộ) theo cách đệ quy, tôi nghi ngờ bạn sẽ có thể nhận thấy sự khác biệt.


1

Trên Windows CLR, đối với chuỗi cuộc gọi 8 độ sâu, việc ném một ngoại lệ chậm hơn 750 lần so với kiểm tra và truyền giá trị trả về. (xem bên dưới để biết điểm chuẩn)

Chi phí cao cho các trường hợp ngoại lệ này là do các cửa sổ CLR tích hợp với một thứ gọi là Xử lý ngoại lệ có cấu trúc của Windows . Điều này cho phép các ngoại lệ được bắt đúng và ném qua các thời gian và ngôn ngữ khác nhau. Tuy nhiên, nó rất rất chậm.

Các ngoại lệ trong thời gian chạy Mono (trên bất kỳ nền tảng nào) nhanh hơn nhiều, vì nó không tích hợp với SEH. Tuy nhiên, có sự mất chức năng khi chuyển ngoại lệ qua nhiều thời gian chạy vì nó không sử dụng bất cứ thứ gì như SEH.

Dưới đây là kết quả viết tắt từ điểm chuẩn ngoại lệ của tôi so với giá trị trả về cho Windows CLR.

baseline: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.25 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.5 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 0.75 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 1 (0), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0 (0), time elapsed 13.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.25 (249999), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.5 (499999), time elapsed 16.0009 ms
retval_error: recurse_depth 5, error_freqeuncy 0.75 (999999), time elapsed 16.001 ms
retval_error: recurse_depth 5, error_freqeuncy 1 (999999), time elapsed 16.0009 ms
retval_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 20.0011 ms
retval_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 21.0012 ms
retval_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 24.0013 ms
exception_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 31.0017 ms
exception_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 5607.3208     ms
exception_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 11172.639  ms
exception_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 22297.2753 ms
exception_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 22102.2641 ms

Và đây là mã ..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

public class TestIt {
    int value;

    public class TestException : Exception { } 

    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    public bool baseline_null(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            return shouldfail;
        } else {
            return baseline_null(shouldfail,recurse_depth-1);
        }
    }

    public bool retval_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                return false;
            } else {
                return true;
            }
        } else {
            bool nested_error = retval_error(shouldfail,recurse_depth-1);
            if (nested_error) {
                return true;
            } else {
                return false;
            }
        }
    }

    public void exception_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                throw new TestException();
            }
        } else {
            exception_error(shouldfail,recurse_depth-1);
        }

    }

    public static void Main(String[] args) {
        int i;
        long l;
        TestIt t = new TestIt();
        int failures;

        int ITERATION_COUNT = 1000000;


        // (0) baseline null workload
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    t.baseline_null(shoulderror,recurse_depth);
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "baseline: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }


        // (1) retval_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    if (!t.retval_error(shoulderror,recurse_depth)) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "retval_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }

        // (2) exception_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    try {
                        t.exception_error(shoulderror,recurse_depth);
                    } catch (TestException e) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "exception_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));         }
        }
    }
}


}

5
Ngoài việc thiếu điểm chính của câu hỏi, vui lòng không sử dụng DateTime. Bây giờ cho điểm chuẩn - sử dụng Đồng hồ bấm giờ, được thiết kế để đo thời gian đã trôi qua. Đây không phải là một vấn đề ở đây vì bạn đang đo khoảng thời gian hợp lý nhưng đáng để tập thói quen.
Jon Skeet

Ngược lại, câu hỏi là "ngoại lệ chậm", giai đoạn. Nó đặc biệt yêu cầu tránh chủ đề khi nào nên ném ngoại lệ, bởi vì chủ đề đó che khuất sự thật. Hiệu suất của các ngoại lệ là gì?
David Jeske

0

Một lưu ý nhanh ở đây về hiệu suất liên quan đến việc bắt ngoại lệ.

Khi đường dẫn thực thi đi vào khối 'thử', không có phép màu nào xảy ra. Không có hướng dẫn 'thử' và không có chi phí liên quan đến việc nhập hoặc thoát khỏi khối thử. Thông tin về khối thử được lưu trữ trong siêu dữ liệu của phương thức và siêu dữ liệu này được sử dụng trong thời gian chạy bất cứ khi nào có ngoại lệ được đưa ra. Công cụ thực thi đi xuống ngăn xếp tìm kiếm cuộc gọi đầu tiên được chứa trong một khối thử. Bất kỳ chi phí nào liên quan đến xử lý ngoại lệ chỉ xảy ra khi ném ngoại lệ.


1
Tuy nhiên, sự hiện diện của các ngoại lệ có thể ảnh hưởng đến tối ưu hóa - các phương thức với các trình xử lý ngoại lệ rõ ràng khó thực hiện hơn và việc sắp xếp lại lệnh bị giới hạn bởi chúng.
Eamon Nerbonne

-1

Khi viết các lớp / hàm cho người khác sử dụng, có vẻ khó nói khi ngoại lệ là phù hợp. Có một số phần hữu ích của BCL mà tôi đã phải bỏ qua và tìm kiếm pinvoke vì chúng ném ngoại lệ thay vì trả về lỗi. Đối với một số trường hợp, bạn có thể làm việc xung quanh nó nhưng đối với những trường hợp khác như System.Man Management và Performance Counters có những cách sử dụng mà bạn cần thực hiện các vòng lặp trong đó ngoại lệ được BCL ném thường xuyên.

Nếu bạn đang viết thư viện và có khả năng từ xa rằng chức năng của bạn có thể được sử dụng trong một vòng lặp và có khả năng lặp lại số lượng lớn, hãy sử dụng mẫu Thử .. hoặc một số cách khác để hiển thị các lỗi bên cạnh các ngoại lệ. Và thậm chí sau đó, thật khó để nói chức năng của bạn sẽ được gọi bao nhiêu nếu nó được sử dụng bởi nhiều quy trình trong môi trường dùng chung.

Trong mã riêng của tôi, các ngoại lệ chỉ được đưa ra khi mọi thứ quá đặc biệt đến mức cần phải nhìn vào dấu vết ngăn xếp và xem những gì đã sai và sau đó sửa nó. Vì vậy, tôi đã viết lại rất nhiều phần của BCL để sử dụng xử lý lỗi dựa trên mẫu Thử .. thay vì ngoại lệ.


2
Điều này dường như không phù hợp với tuyên bố " Tôi không muốn thảo luận về việc khi nào và không nên ném ngoại lệ ".
hrbrmstr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.