programing tip

Java if vs. try / catch 오버 헤드

itbloger 2021. 1. 7. 07:43
반응형

Java if vs. try / catch 오버 헤드


if 블록 과 반대로 try / catch 블록 을 사용하는 데 Java에 오버 헤드가 있습니까 (동봉 된 코드가 요청하지 않는다고 가정)?

예를 들어, 문자열에 대한 "안전 트림"메소드의 다음 두 가지 간단한 구현을 사용하십시오.

public String tryTrim(String raw) {
    try {
        return raw.trim();
    } catch (Exception e) {
    }
    return null;
}

public String ifTrim(String raw) {
    if (raw == null) {
        return null;
    }
    return raw.trim();
}

는 IF raw입력은 거의 없다 null, 어떤 성능 차이가 두 방법은?

또한 코드 레이아웃을 단순화하기 위해 접근 방식 을 사용 하는 것이 좋은 프로그래밍 패턴 입니까? 특히 하나의 try / catch 블록에 코드를 포함하여 드문 오류 조건을 검사하는 tryTrim()많은 if 블록을 피할 수있는 경우 특히 그렇습니다 .

예를 들어 N parameters를 사용 하는 메서드 를 사용 하는 것은 일반적인 경우입니다.이 메서드 M <= N는 시작 근처에서 사용하고, 이러한 매개 변수가 "유효하지 않은"경우 (예 : null 또는 빈 문자열) 나머지 부분에 영향을주지 않고 신속하고 결정적으로 실패합니다. 암호.

이러한 경우, k * M if 블록 을 작성 하는 대신 (여기서는 k매개 변수 당 평균 검사 수, 예 k = 2들어 null 또는 빈 문자열) try / catch 블록은 코드를 크게 줄이고 1-2 줄 주석을 사용할 수 있습니다. "비 전통적인"논리를 명시 적으로 기록합니다.

이러한 패턴은 특히 오류 조건이 거의 발생하지 않는 경우 방법의 속도를 높일 수 있으며 프로그램 안전성을 손상시키지 않고 수행 할 수 있습니다 (예 : null 또는 빈 값이있는 문자열 처리 방법에서와 같이 오류 조건이 "정상"이라고 가정). 거의 존재하지 않지만 허용됩니다.)


난 당신이 오버 헤드 성능에 대한 요구하는지 알고,하지만 당신은 정말 사용하지 말아야 try/ catchif상호 교환.

try/ catch는 정상적인 프로그램 흐름이 아닌 제어 할 수없는 잘못된 일을위한 것입니다. 예를 들어, 파일에 쓰려고하는데 파일 시스템이 가득 찼습니까? 이러한 상황은 일반적으로 try/ 로 처리해야합니다 catch.

if문은 정상적인 흐름과 일반적인 오류 검사 여야합니다. 예를 들어 사용자가 필수 입력 필드를 채우지 못합니까? /이 if아닌 사용하십시오 .trycatch

귀하의 예제 코드가 강하게 올바른 접근 방식이 있다는 것을 제시하는 것이이 날 것으로 보인다 if문이 아니라 try/ catch.

귀하의 질문에 대답하기 위해, 나는 일반적으로 더 많은 오버 헤드가 있다는 것을 추측 할 것입니다 try/ catch보다 if. 확실하게 알고 싶다면 Java 프로파일 러를 구하고 관심있는 특정 코드를 찾으십시오. 상황에 따라 답변이 다를 수 있습니다.


이 질문은 거의 "죽음에 대한 답변"이되었지만 유용하게 사용할 수있는 몇 가지 사항이 더 있다고 생각합니다.

  • 사용 try / catch비 뛰어난 제어 흐름은 (자바) 나쁜 스타일이다. ( "비 예외적"이 무엇을 의미하는지에 대한 논쟁이 자주 있습니다. 그러나 그것은 다른 주제입니다.)

  • 그 이유의 일부는 나쁜 스타일 즉이다 try / catch입니다 크기의 주문 일반 제어 흐름 문보다 더 비싼 1 . 실제 차이는 프로그램과 플랫폼에 따라 다르지만 1000 배 이상 비쌀 것으로 예상됩니다. 무엇보다도 예외 객체 생성 은 스택 추적을 캡처하여 스택의 각 프레임에 대한 정보를 찾고 복사합니다. 스택이 깊을수록 더 많이 복사해야합니다.

  • 나쁜 스타일 인 이유의 또 다른 부분은 코드를 읽기가 더 어렵 기 때문입니다.

1-Java 7 최신 버전의 JIT는 경우에 따라 오버 헤드를 크게 줄이기 위해 예외 처리를 최적화 할 수 있습니다. 그러나 이러한 최적화는 기본적으로 활성화되어 있지 않습니다.

예제를 작성한 방식에도 문제가 있습니다.

  • 잡기 Exception실수로 다른 체크되지 않은 예외를 잡을 것이라는 가능성이 있기 때문에 아주 나쁜 관행이다. 예를 들어, 전화를 raw.substring(1)면서 그렇게했다면 잠재적 인 문제를 포착 StringIndexOutOfBoundsException하고 버그를 숨길 수도 있습니다 .

  • 귀하의 예가 시도하는 것은 (아마도) null문자열 을 처리하는 데있어서 좋지 않은 결과입니다 . 일반적으로 null문자열 사용을 최소화 하고 (의도적 인) 확산을 제한해야합니다. 가능한 경우 null"값 없음"을 의미하는 대신 빈 문자열을 사용하십시오 . 그리고 문자열 을 전달하거나 반환 해야하는 경우가 있으면 nulljavadocs 메소드에 명확하게 문서화하십시오. 메서드가 호출 null되지 않아야 할 때 호출 되면 ... 버그입니다. 예외를 발생 시키십시오. (이 예에서는)를 반환하여 버그를 보상하려고하지 마십시오 null.


후속 조치

내 질문은 null 값뿐만 아니라 더 일반적이었습니다.

... 내 대답의 대부분의 요점은 null 값에 관한 것이 아닙니다!

그러나 가끔 null 값 또는 예외를 생성 할 수있는 다른 값을 허용하고 무시하고 싶은 경우가 많다는 점을 명심하십시오. 예를 들어 어딘가에서 키 / 쌍 값을 읽고 위의 tryTrim ()과 같은 메서드에 전달할 때입니다.

예, null가치가 예상되는 상황이 있으며이를 처리해야합니다.

그러나 나는 tryTrim()하고있는 것이 (일반적으로) null. 다음 세 가지 코드를 비교하십시오.

// Version 1
String param = httpRequest.getParameter("foo");
String trimmed = tryTrim(param);
if (trimmed == null) {
    // deal with case of 'foo' parameter absent
} else {
    // deal with case of 'foo' parameter present
}

// Version 2
String param = httpRequest.getParameter("foo");
if (param == null) {
    // deal with case of 'foo' parameter absent
} else {
    String trimmed = param.trim();
    // deal with case of 'foo' parameter present
}

// Version 3
String param = httpRequest.getParameter("foo");
if (param == null) {
    // treat missing and empty parameters the same
    param = "";
}
String trimmed = param.trim();

Ultimately you have to deal with the null differently from a regular string, and it is usually a good idea do this as soon as possible. The further the null is allowed to propagate from its point origin, the more likely it is that the programmer will forget that a null value is a possibility, and write buggy code that assumes a non-null value. And forgetting that an HTTP request parameter could be missing (i.e. param == null) is a classic case where this happens.

I'm not saying that tryTrim() is inherently bad. But the fact that a programmer feels the need write methods like this is probably indicative of less than ideal null handling.


Use the second version. Never use exceptions for control flow when other alternatives are available, as that is not what they are there for. Exceptions are for exceptional circumstances.

While on the topic, do not catch Exception here, and especially do not swallow it. In your case, you would expect a NullPointerException. If you were to catch something, that is what you would catch (but go back to paragraph one, do not do this). When you catch (and swallow!) Exception, you are saying "no matter what goes wrong, I can handle it. I don't care what it is." Your program might be in an irrevocable state! Only catch what you are prepared to deal with, let everything else propogate to a layer that can deal with it, even if that layer is the top layer and all it does is log the exception and then hit the eject switch.


Otherwise exceptions are fast to throw and catch (though an if is probably still faster), but the slow thing is creating the exception's stack trace, because it needs to walk through all of the current stack. (In general it's bad to use exceptions for control flow, but when that really is needed and the exceptions must be fast, it's possible to skip building the stack trace by overriding the Throwable.fillInStackTrace() method, or to save one exception instance and throw it repeatedly instead of always creating a new exception instance.)


As far as overhead goes, you can test for yourself:

public class Overhead {

public static void main(String[] args) {
    String testString = "";

    long startTimeTry = System.nanoTime();
    tryTrim(testString);
    long estimatedTimeTry = System.nanoTime() - startTimeTry;

    long startTimeIf = System.nanoTime();
    ifTrim(testString);
    long estimatedTimeIf = System.nanoTime() - startTimeIf;

    System.out.println("Try time:" + estimatedTimeTry);
    System.out.println("If time:" + estimatedTimeIf);

}

public static String tryTrim(String raw) {
    try {
        return raw.trim();
    } catch (Exception e) {
    }
    return null;
}

public static String ifTrim(String raw) {
    if (raw == null) {
        return null;
    }
    return raw.trim();
}

}

The numbers I got are:

Try time:8102
If time:1956

As far as what style - it is a whole separate question. The if statement looks pretty natural, but the try looks really strange for multiple reason: - you caught Exception even though you are checking for NULL value, are you expecting something "exceptional" to happen (otherwise catch NullException)? - you caught that Exception, are you going to report it or swallow? etc. etc. etc.

Edit: See my comment for why this is an invalid test, but I really didn't want to leave this standing here. Just by swapping tryTrim and ifTrim, we suddenly get the following results (on my machine):

Try time:2043
If time:6810

Instead of starting to explain all of this, just read this for the beginning - Cliff also has some great slides about the whole topic, but I can't find the link at the moment.

Knowing how exception handling works in Hotspot, I'm fairly certain that in a correct test try/catch without an exception) would be the baseline performance (because there's no overhead whatsoever), but the JIT can play some tricks with Nullpointer checks followed by method invocations (no explicit check, but catch the hw exception if the object is null) in which case we'd get the same result. Also don't forget: We're talking about the difference of one easily predictable if which would be ONE CPU cycle! The trim call will cost a million times that.

ReferenceURL : https://stackoverflow.com/questions/8621762/java-if-vs-try-catch-overhead

반응형