Java 성능 좋은 분기문을 쓰는 방법, if문에서 연산 순서 (if문에서 좋은 퍼포먼스를 만들기, && || 연산자 우선 순위)
Java에서 좋은 if문 코드를 작성하는 방법
코드 분석을 하다가 우연히 if문의 '||' 연산자와 '&&' 연산자를 보면서 최적화에 대해 생각해보게 되었다.
여러 참고 사이트를 보면서 나름대로 정리를 하게되었다.
신입 개발자들도 한 번 정도 고려하면서 코딩을 하면 좋을 듯 하다.
먼저, 좋은 if문 코드를 작성하기에 앞서서 자바 연산자 우선 순위를 알고 있어야 한다.
< 이미지 출처 : http://noritersand.tistory.com/38 >
결합 방향에 대해서는 최신 자바 기준으로 정확하지 않을 수 있으므로 우선 순위만 참고하면 된다.
다양한 프로그래밍 언어들에서 연산자 우선 순위가 비슷하다보니 한 번 보고 넘기는 경우가 많은데 다시 한 번 상기시킬 필요가 있다.
||, && 연산자 우선 순위와 단축 평가
위에 표에서 본 것과 같이 '&&'이 '||'보다 우선순위가 높다.
1 | if( condition1 || condition2 && condition3 || condition4 ) |
따라서 위와 같은 if문에서는 아래 처럼 연산을 할 것이다.
1 | if( condition1 || (condition2 && condition3) || condition4 ) |
가운데 &&으로 연산된 condition2와 condition3의 true, false 를 &&으로 연산하고 condition1 부터 순서대로 || 연산을 수행할 것이다.
이 정도는 사칙연산에서 곱셉, 나눗셈이 덧셈, 뺄셈보다 먼저 이뤄진다는 것처럼 이해가 쉽다.
다음으로 알아야 할 것은 단축 평가다.
단축 평가는 and(&&)연산과 or(||)연산의 원리를 이해하면 쉽게 이해가 된다.
1 | if( condition1 && condition2 && condition3 && condition4 ) |
위와 같이 &&연산자로 여러 boolean값의 상황이 있을 때, &&연산은 하나의 상황이라도 false면 결과가 false이기 때문에 condition1이 false라면 굳이 뒤에있는 condition2, condition3, condition4가 false인지 true인지 검사할 필요가 없다.
1 | if( condition1 || condition2 || condition3 || condition4 ) |
마찬가지로 위와 같이 ||연산자로 여러 boolean값의 상황이 있을 때는 하나의 상황이라도 true값이면 결과가 true이기 때문에 condition1이 true면 뒤에 condition2~4가 false인지 true인지 검사할 필요가 없다.
따라서 검사할 필요가 없다는 것을 컴파일러가 알고 뒤에 연산을 무시하는 것을 "단축 평가"라고 한다.
이런 단축 평가라는 기능이 있기 때문에 프로그래머는 이를 활용해서 성능을 높이는 작업을 해줘야한다.
예를들면 true, false값이 간단하게 나오는 상황을 앞 쪽에다가 적용하면 이미 앞에서 boolean값이 결정되기 때문에 빠르게 처리할 수 있는 것이다.
단축 평가가 좋은 것만은 아니다.
단축 평가가 무서운 것은 뒤에 true, false값을 판단하지 않기 때문에 단위 테스트를 할 때 위험할 수 있다.
테스트를 할 때 모든 조건을 테스트를 해줘야하는데 앞에서 true, false를 결정해버려서 뒤에 조건을 정확하게 테스트할 수 없을 수도 있기 때문이다.
또한 단축 평가를 한다고 해서 무조건 빠른건 아니다.
어떠한 경우에는 오히려 연산의 순서를 정하기 때문에 병렬처리를 막아 성능 저하를 시킬 수도 있다고 한기 때문이다.
1 | if( a() + b() * c() - d() ) |
그리고 단축평가와는 조금 다른 이야기지만, 위와 같은 경우에 연산 순서는 b() * c()를 먼저하겠지만 a() 메소드를 먼저 실행할지 b(), c(), d()메소드를 먼저 실행할지는 컴파일러가 알아서 정하기 때문에 그런 것들을 잘 고려해서 if문을 처리할 필요가 있다.
결과적으로 if문에서 퍼포먼스를 높이려면 괄호를 적절히 사용해서 프로그래머가 원하는 연산 순서로 진행되게 하면서 간단한 조건을 먼저 연산할 수 있게 두는 방법을 사용하면 된다.
가독성을 높이는 if문 쓰는 방법
1 2 3 4 5 6 7 | void initialize() { if (isInitialized()) { return; } //... //초기화 } |
초기화 코드를 예로들면 메소드 앞에 if문에서 예외 처리해야할 부분을 잡아서 분기해주는 것이 좋다.
무슨 얘기냐하면 if문을 중첩으로 쓰지 않게 잘못된 로직을 먼저 if문으로 잡아주어 제거한 후에 아래 코드를 쓰는 것이 가독성이 높다. 아래 예제를 본다.
1 2 3 4 5 6 7 8 9 10 11 12 | void compute() { Server server = getServer(); if (server != null) { Client client = server.getClient(); if (client != null) { Request current = client.getRequest(); if (current != null) { // 실제 처리할 로직 } } } } |
[if문이 중첩되는 코드]
1 2 3 4 5 6 7 8 9 10 11 12 | void compute() { Server server = getServer(); if (server == null) return; Client client = server.getClient(); if (client == null) return; Request current = client.getRequest(); if (current == null) return; // 실제 처리할 로직 } |
[if문 개선한 코드]
실제로도 이렇게 보는 것이 편리해진다.
이렇게 작성하는 요령은 부정적인 상황을 if문에 넣는다. 라고 생각하고 코딩하면 된다.
if/else문이 있을 때는 조금 다른데 if/else문일 때는 if문에 간단하고 긍정적인 코드를 작성하는 것이 가독성이 높아진다.
물론 if/else문에서도 if문이 더 길어진다면 앞서 사용한 것처럼 부정적인 상황을 넣는 것이 나을 수 있다.
1 2 3 4 5 6 7 8 9 10 | // 가능하면 긍정적인 조건 ex:hasAccount if (...) { // 간단하고 긍정적인 내용 } else { /* * 상대적으로 복잡함 * ... * ... */ } | cs |
이와 같이 사용하면 다른 사람이 코드 분석을 할 때도 가독성이 좋아진다.
이런식으로 코드를 적어가는 연습을 하다보면 내 것으로 만들고 누가봐도 좋은 코드를 만들 수 있게 될 것이다.
참고 사이트
http://kldp.org/node/116229
http://redutan.github.io/2016/04/01/good-if
http://noritersand.tistory.com/38