2009-03-05 28 views
13

Trước hết, một sự từ bỏ: tôi có kinh nghiệm trong các ngôn ngữ khác, nhưng tôi vẫn đang học hỏi sự tinh tế của C#Dưới C# bao nhiêu một hit hiệu suất là một thử, ném và khối catch

On cho vấn đề ... Tôi đang xem xét một số mã, sử dụng các khối try/catch theo cách mà tôi quan tâm. Khi một thói quen phân tích cú pháp được gọi là, chứ không phải là trả lại một mã lỗi, các lập trình viên sử dụng logic sau

catch (TclException e) { 
    throw new TclRuntimeError("unexpected TclException: " + e.Message,e); 
} 

này được đánh bắt bởi người gọi, mà ném những lỗi tương tự ...
... được bắt bởi người gọi, mà ném cùng một lỗi ...
..... bị người gọi bắt, phát hiện lỗi tương tự ...

sao lưu khoảng 6 cấp.

Tôi có suy nghĩ đúng về tất cả các khối catch/throw này đang gây ra vấn đề về hiệu năng hay đây là một sự triển khai hợp lý trong C#?

+1

Khối catch được hiển thị ở đây sẽ thêm thông tin bổ sung vào đối tượng ngoại lệ, điều này có thể biện minh cho sự hiện diện của nó. Nhưng nếu khối catch là vô dụng, loại bỏ nó. – Qwertie

Trả lời

10

Ném (thay vì bắt) là tốn kém.

Không đặt khối bắt vào trừ khi bạn định làm điều gì đó hữu ích (ví dụ: chuyển đổi thành một ngoại lệ hữu ích hơn, xử lý lỗi).

Chỉ cần làm lại ngoại lệ (tuyên bố ném mà không có đối số) hoặc, thậm chí tệ hơn, ném cùng một đối tượng như vừa mới bắt được chắc chắn là điều sai trái.

EDIT: Để tránh sự nhập nhằng:

rethrow:

catch (SomeException) { 
    throw; 
} 

Tạo ngoại lệ từ đối tượng ngoại lệ trước, nơi tất cả các thời gian chạy cung cấp nhà nước (đặc biệt là stack trace) được ghi đè:

catch (SomeException e) { 
    throw e; 
} 

Trường hợp thứ hai là một cách vô nghĩa để vứt bỏ thông tin về ngoại lệ. và không có bất cứ điều gì trước khi ném trong khối catch là gấp đôi vô nghĩa. Điều này có thể tồi tệ hơn:

catch (SomeException e) { 
    throw new SomeException(e.Message); 
} 

mất hầu hết thông tin trạng thái hữu ích (bao gồm nội dung ban đầu được ném.)

+0

Có điều gì hữu ích không bao gồm ghi nhật ký lỗi trước khi khởi động lại, hoặc điều này sẽ tốt hơn được chăm sóc ở nơi khác (ví dụ như trên Lỗi Ứng dụng)? – Nick

+0

Rethrowing không phải là đắt tiền, như không có đối tượng ngoại lệ đã được tạo ra (trong đó bao gồm thư giãn stack) –

+0

Nhưng ở mọi cấp độ, không phải là họ unwinding stack? – DevinB

14

Đó là thiết kế kém theo bất kỳ ngôn ngữ nào.

Ngoại lệ được thiết kế để bị bắt ở cấp độ mà bạn có thể xử lý chúng. Bắt một ngoại lệ, chỉ để ném nó một lần nữa chỉ là một sự lãng phí vô nghĩa của thời gian (nó cũng làm cho bạn mất thông tin có giá trị về vị trí của lỗi ban đầu).

Rõ ràng, người đã viết mã đó, được sử dụng để sử dụng mã lỗi và sau đó chuyển sang ngoại lệ mà không thực sự hiểu cách chúng hoạt động. Trường hợp ngoại lệ tự động "bong bóng lên" ngăn xếp nếu không có bắt ở một cấp độ.

Ngoài ra, lưu ý rằng ngoại lệ là dành cho xuất hiện đặc biệt. Điều đó nên không bao giờ xảy ra. Chúng không nên được sử dụng để kiểm tra tính hợp lệ thông thường (tức là, không bắt được ngoại lệ chia cho số không; hãy kiểm tra xem ước số đó có bằng không trước đó không).

+0

Không có thông tin ban đầu nào bị mất trong ví dụ được cung cấp trong câu hỏi, vì ngoại lệ ban đầu được chuyển thành đối số innerException cho hàm tạo TclRuntimeError. – yfeldblum

+0

Điều đó đúng.Tuy nhiên, ngoại lệ thực tế được chôn cất sáu cấp độ sâu bên trong ngoại lệ ném vào mặt người dùng. –

+2

Nói chung, bạn chỉ nên bao gồm các ngoại lệ tại các điểm trừu tượng và thậm chí chỉ khi bạn sẽ cung cấp thêm thông tin về lỗi. Ví dụ gói một thành phần của bên thứ 3 và gói bất kỳ ngoại lệ nào là lẻ (IndexOutOfBounds-> KeyNotFound) – Guvante

1

Thử/Catch/Throw chậm - triển khai tốt hơn là kiểm tra giá trị trước khi bắt nó, nhưng nếu bạn không thể tiếp tục, bạn tốt hơn khi chỉ ném và bắt khi đếm. Kiểm tra và đăng nhập là hiệu quả hơn nếu không.

+1

Bản thân khối thử có rất ít hoặc không có phí. Đó là trường hợp ngoại lệ (ném/bắt) làm chậm hệ thống. –

+0

Right - Điểm trong ví dụ của anh ta, sử dụng try/catch chậm hơn vì chúng dẫn đến một ngoại lệ bị ném, có thể tránh được bằng cách sử dụng một kiểm tra rồi thực hiện một hành động khác thay vì ném liên tục. – Fry

14

Theo msdn:Performance Tips and Tricks bạn có thể sử dụng tính năng try and catch mà không gặp bất kỳ sự cố nào về hiệu suất cho đến khi xảy ra tình huống ném thực.

+2

Bạn đã tiết kiệm cho tôi nhiều cuộc tranh luận đồng nghiệp với liên kết đó :) Đây là trích dẫn trực tiếp từ bài báo, nếu bạn không muốn bận tâm kiểm tra qua nó: _ "Tìm và thiết kế mã có thể có ngoại lệ Hãy nhớ rằng điều này không liên quan gì đến các khối try/catch: ** bạn chỉ phải chịu chi phí khi ngoại lệ thực sự bị ném. Bạn có thể sử dụng nhiều khối try/catch như bạn muốn. * Ví dụ, bạn nên tránh xa những thứ như sử dụng ngoại lệ cho luồng điều khiển. "_ –

+1

@kayleeFrye_onDeck vâng, bạn có thể sử dụng thử và nắm bắt mà không gặp bất kỳ vấn đề nào, càng nhiều càng tốt. vấn đề duy nhất nếu kịch bản ném được sử dụng trong dòng chảy thường xuyên của chương trình. – Avram

2

Nói chung, việc ném ngoại lệ tốn kém trong .NET. Chỉ cần có một khối try/catch/finally thì không. Vì vậy, có, mã hiện tại là xấu từ một quan điểm hiệu suất bởi vì khi nó ném, nó ném 5-6 cồng kềnh trường hợp ngoại lệ mà không cần thêm bất kỳ giá trị hơn chỉ đơn giản là để cho ngoại lệ ban đầu tự nhiên bong bóng lên 5-6 ngăn xếp khung.

Tệ hơn nữa, mã hiện tại thực sự tệ từ quan điểm thiết kế. Một trong những lợi ích chính của việc xử lý ngoại lệ (so với các mã lỗi trả về) là bạn không cần phải kiểm tra các trường hợp ngoại lệ/mã trả về ở mọi nơi (trong ngăn xếp cuộc gọi). Bạn chỉ cần bắt chúng ở số lượng nhỏ các địa điểm bạn thực sự muốn xử lý chúng. Bỏ qua một ngoại lệ (không giống như bỏ qua một mã trả về) không bỏ qua hoặc ẩn vấn đề. Nó chỉ có nghĩa là nó sẽ được xử lý cao hơn ngăn xếp cuộc gọi.

+0

Rõ ràng tất cả điều này cần phải được viết. Nhưng như một bước đầu tiên, một sự cải tiến nhỏ có thể đơn giản như thay đổi bắt (TclException e) {throw e;} để bắt (TclException e) {throw;} để cho phép ngoại lệ bong bóng lên ngăn xếp? – Noah

+1

Có, nhưng nó thậm chí còn dễ hơn thế. Chỉ cần xóa toàn bộ khối catch (TclException e). Nếu bạn không bắt được ngoại lệ, nó sẽ tự bong bóng lên toàn bộ ngăn xếp cuộc gọi. –

0

Nếu mọi lớp của ngăn xếp chỉ trả về cùng loại với cùng thông tin, không thêm gì mới, thì hoàn toàn vô lý.

Nếu nó xảy ra ở ranh giới giữa các thư viện được phát triển độc lập, điều đó là dễ hiểu. Đôi khi một tác giả thư viện muốn kiểm soát những ngoại lệ nào thoát khỏi thư viện của họ, để họ có thể thay đổi việc triển khai sau này mà không cần phải tìm hiểu cách mô phỏng hành vi ném ngoại lệ của phiên bản trước.

Thường là một ý tưởng tồi để bắt và quay lại trong bất kỳ trường hợp nào mà không có lý do rất tốt. Điều này là bởi vì ngay sau khi một khối catch được tìm thấy, tất cả các khối cuối cùng giữa ném và bắt được thực hiện. Điều này chỉ xảy ra nếu ngoại lệ có thể được phục hồi. Không sao trong trường hợp này vì một loại cụ thể đang bị bắt, vì vậy tác giả của mã (hy vọng) biết rằng họ có thể an toàn hoàn tác bất kỳ thay đổi trạng thái nội bộ nào của họ theo kiểu ngoại lệ cụ thể đó.

Vì vậy, các khối try/catch đó có thể có chi phí vào thời gian thiết kế - chúng làm cho chương trình trở nên phức tạp hơn. Nhưng vào thời gian chạy chúng sẽ chỉ áp đặt một chi phí đáng kể khi ngoại lệ được ném, bởi vì việc truyền ngoại lệ lên ngăn xếp đã được thực hiện phức tạp hơn.

-1

Trường hợp ngoại lệ chậm, hãy cố gắng không sử dụng chúng.

Xem câu trả lời tôi đã cung cấp here. Về cơ bản, Chris Brumme (người trong nhóm CLR) cho biết họ được thực hiện như ngoại lệ SEH, vì vậy bạn có một hit lớn khi họ bị ném, phải chịu các hình phạt khi họ bong bóng thông qua ngăn xếp hệ điều hành. Nó thực sự là excellent article và đi sâu trong những gì xảy ra khi bạn ném một ngoại lệ. Eg .:


Tất nhiên, chi phí lớn nhất của trường hợp ngoại lệ là khi bạn thực sự ném một. Tôi sẽ quay lại trang này gần cuối của blog.


Performance. Trường hợp ngoại lệ có chi phí trực tiếp khi bạn thực sự ném và bắt một ngoại lệ. Họ cũng có thể có chi phí gián tiếp liên quan đến việc đẩy các trình xử lý vào mục nhập phương thức. Và họ thường có thể có một chi phí ngấm ngầm bởi hạn chế các cơ hội codegen.


Tuy nhiên, có một vấn đề dài hạn hiệu suất nghiêm trọng với những ngoại lệ và điều này phải được tính vào quyết định của bạn.

Hãy xem xét một số trong những điều mà xảy ra khi bạn ném một ngoại lệ:

  • Grab một vết đống bằng cách giải thích siêu dữ liệu phát ra bởi trình biên dịch để hướng dẫn chồng Thư giãn của chúng tôi.

  • Chạy qua một chuỗi các trình xử lý lên ngăn xếp, gọi mỗi bộ xử lý hai lần.

  • Bù đắp cho sự không khớp giữa SEH, C++ và quản lý ngoại lệ.

  • Phân bổ một trường hợp ngoại lệ được quản lý và chạy hàm tạo của nó. Rất có thể, điều này liên quan đến việc tìm kiếm các tài nguyên cho các lỗi khác nhau thư.

  • Có thể thực hiện chuyến đi qua hạt nhân hệ điều hành. Thường lấy phần cứng ngoại lệ.

  • Thông báo cho bất kỳ trình gỡ rối đính kèm, trình thu thập dữ liệu, trình xử lý ngoại lệ có véc tơ và các bên quan tâm khác.

Đây là năm ánh sáng cách trả về -1 từ hàm gọi. Ngoại lệ vốn là không phải là địa phương và nếu có rõ ràng là và xu hướng lâu dài cho kiến ​​trúc của hôm nay, đó là bạn phải vẫn là địa phương để có hiệu suất tốt.

Một số người sẽ cho rằng ngoại lệ không phải là vấn đề, không có vấn đề về hiệu suất và thường là điều tốt. Những người này nhận được rất nhiều phiếu bầu phổ biến, nhưng họ chỉ đơn giản là sai. Tôi đã thấy nhân viên của Microsoft đưa ra các tuyên bố tương tự (thường là với kiến ​​thức kỹ thuật thường dành cho bộ phận tiếp thị), nhưng điểm mấu chốt từ miệng của con ngựa là sử dụng chúng một cách tiết kiệm.

Các câu hỏi cũ, ngoại lệ chỉ nên được sử dụng cho các trường hợp đặc biệt, cũng đúng với C# vì nó là ngôn ngữ khác.

+0

"Cố gắng không sử dụng chúng"? Đó là một sự miêu tả sai lầm khá lớn về lời khuyên của Brumme. –

+0

không, đó là lời khuyên của tôi, và là một khái quát để làm cho mọi người nghĩ về những gì họ đang làm. Chris nói để sử dụng chúng, nhưng rất ít: "... Bằng cách đảm bảo rằng các trường hợp lỗi là cực kỳ hiếm" – gbjbaanb

+0

Vì vậy ..... bạn đang đề xuất để kiểm tra các giá trị hơn là chỉ sử dụng ngoại lệ tất cả "willy-nilly", vì vậy thay vì "Cố gắng không sử dụng ngoại lệ", lời khuyên của bạn là "Cố gắng không tạo ngoại lệ"? – Fry

2

Đó không phải là khủng khiếp trong và của chính nó, nhưng người ta thực sự nên xem làm tổ.

Các trường hợp sử dụng chấp nhận được là như vậy:

Tôi là một thành phần thấp ish mà có thể gặp phải một số lỗi khác nhau tuy nhiên người tiêu dùng của tôi là chỉ quan tâm đến một loại hình cụ thể của ngoại lệ. Vì vậy, tôi có thể làm điều này:

catch(IOException ex) 
{ 
    throw new PlatformException("some additional context", ex); 
} 

Bây giờ điều này cho phép người tiêu dùng để làm:

try 
{ 
    component.TryThing(); 
} 
catch(PlatformException ex) 
{ 
    // handle error 
} 

Vâng, tôi biết một số người sẽ nói nhưng người tiêu dùng nên bắt IOException nhưng điều này phụ thuộc vào cách trừu tượng mã tiêu thụ thực sự là. Điều gì sẽ xảy ra nếu Impl đang lưu một thứ gì đó vào đĩa và người tiêu dùng không có lý do hợp lệ nào để nghĩ rằng hoạt động của họ sẽ chạm vào đĩa? Trong một kịch bản như vậy nó sẽ làm cho không có ý nghĩa để đặt xử lý ngoại lệ này trong mã tiêu thụ.

Những gì chúng tôi thường cố tránh bằng cách sử dụng mẫu này là đặt trình xử lý ngoại lệ "bắt tất cả" vào mã logic nghiệp vụ bởi vì chúng tôi muốn tìm hiểu tất cả các loại ngoại lệ có thể. cần được điều tra. Nếu chúng tôi không phát hiện, nó sẽ phát sáng, chạm vào trình xử lý cấp "trên cùng" và sẽ tạm dừng ứng dụng để tiếp tục. Điều này có nghĩa là khách hàng sẽ báo cáo ngoại lệ đó và bạn sẽ có cơ hội xem xét nó. Điều này rất quan trọng khi bạn đang cố gắng xây dựng phần mềm mạnh mẽ. Bạn cần phải tìm tất cả các trường hợp lỗi này và viết mã cụ thể để xử lý chúng.

Điều gì không phải là rất đẹp là làm tổ một số tiền quá mức và đó là vấn đề bạn nên giải quyết với mã này.

Như một áp phích khác đã nêu ngoại lệ là hành vi ngoại lệ hợp lý nhưng không thực hiện quá xa. Về cơ bản mã nên thể hiện hoạt động "bình thường" và ngoại lệ nên xử lý các vấn đề tiềm năng mà bạn có thể gặp phải. Trong điều kiện ngoại lệ hiệu suất là tốt, bạn sẽ nhận được kết quả khủng khiếp nếu bạn perf thử nghiệm với một trình gỡ lỗi cho một thiết bị nhúng nhưng trong bản phát hành mà không có một trình gỡ lỗi chúng thực sự khá nhanh chóng.

Điều chính mọi người quên khi thảo luận về hiệu suất của ngoại lệ là trong trường hợp lỗi mọi thứ đều chậm lại vì người dùng đã gặp sự cố. Chúng ta có thực sự quan tâm đến tốc độ khi mạng bị ngắt và người dùng không thể lưu công việc của họ không? Tôi rất nghi ngờ nhận được báo cáo lỗi trở lại cho người dùng một vài ms nhanh hơn sẽ tạo sự khác biệt.

Hướng dẫn chính cần nhớ khi thảo luận ngoại lệ là Ngoại lệ không được xảy ra trong luồng ứng dụng thông thường (thường không có lỗi). Mọi thứ khác bắt nguồn từ tuyên bố đó.

Trong ví dụ chính xác bạn cho là tôi không chắc chắn. Dường như với tôi rằng không có lợi ích nào thực sự thu được từ việc gói những gì có vẻ là một ngoại lệ tcl chung trong một ngoại lệ tcl âm thanh chung chung khác. Nếu bất cứ điều gì tôi sẽ đề nghị theo dõi xuống các tác giả ban đầu của mã và tìm hiểu xem có bất kỳ logic cụ thể đằng sau suy nghĩ của mình. Rất có thể là mặc dù bạn chỉ có thể giết bắt.

+0

thường bạn quan tâm đến hiệu suất - trong máy chủ, nếu bạn ném ngoại lệ chỉ 1% thời gian, nhưng bạn đang dùng 100 cuộc gọi mỗi giây, hơn 100 người dùng - tức là 100 ngoại lệ mỗi giây!Đó có thể là một hit hiệu suất nghiêm trọng đôi khi. – gbjbaanb

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