[Java] 랜덤함수 사용 및 속도 비교
- 프로그램언어/자바(Java)
- 2020. 12. 8.
불현듯, 자바에서 제공하는 랜덤함수의 속도 차이가 있을까 궁금해서 테스트를 해보기로 했다. 자바는 기본적으로 java.util.Random 클래스와 Math 클래스에서 제공하는 random이 있는데 둘의 차이점은 Math는 별도로 import를 할 필요가 없고, Random은 import하는 대신 좀 더 자유롭게 커스터마이징 할 수 있다는 정도가 있을 수 있겠다.
Random 클래스로 1000만번 nextInt 호출하기
우선 가장 많이 사용되는 Random 클래스의 nextInt를 1000만번 호출해보도록 한다. 다만 예제에서 nextInt값을 total로 담은 후, 최종적으로 total값을 뿌린다. 이렇게 한 이유는 보편적으로 Random을 사용할 때 정수값을 많이 호출하기 때문이다.
import java.util.Random;
public class main {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
Random rnd = new Random();
long total = 0;
// 천만번 비교
for(int i = 0; i < 10000000; i++) {
total += rnd.nextInt(100);
}
long endTime = System.currentTimeMillis();
System.out.println("total : " + total + ", elapsed : " + (endTime-startTime) + "(ms)");
}
}
위 예제를 정확히 3번 실행해 보았다.
total : 495041741, elapsed : 182(ms)
total : 494932789, elapsed : 187(ms)
total : 495085285, elapsed : 242(ms)
결과는 약 200ms(0.2초) 정도가 걸렸다. 이정도의 속도면 Random 함수를 사용해서 서비스에 영향을 주는건 티도 안날 수준이라 말할 수 있겠다.
Math.random으로 정수 랜덤값 호출
이번에는 Math함수에게 불리할 수 있는 정수 랜덤값을 호출한다. Math는 실수값만 호출할 수 있기 때문에 이 방식에는 추가적인 곱셈과 추가적인 형변환이 들어가 있다.
public static void main(String[] args) {
for(int loop = 0; loop < 3; loop++) {
long startTime = System.currentTimeMillis();
long total = 0;
// 천만번 비교
for(int i = 0; i < 10000000; i++) {
total += (int)(Math.random()*100);
}
long endTime = System.currentTimeMillis();
System.out.println(loop+1 + "번째, total : " + total + ", elapsed : " + (endTime-startTime) + "(ms)");
}
}
3번 호출하기 귀찮아서(?) 한번더 전체를 감싸 3번 호출하게 변경하였다.
1번째, total : 494994469, elapsed : 381(ms)
2번째, total : 494983775, elapsed : 318(ms)
3번째, total : 495043921, elapsed : 286(ms)
결과는 이처럼, Random 클래스의 nextInt보다 오래 걸린다. 다만 이 값은 형변환과 곱셈이 들어가 있기 때문에 Random 클래스도 동일한 페널티를 줘보기로 했다.
Random 클래스의 nextDouble로 동일한 비교
속도차이를 명확하게 하기 위해서 Random 클래스에서 nextDouble로 호출하고 곱셈에 형변환을 수행한다. 즉, 계급장을 동일하게 떼고(정확히는 동일한 계급장을 달고...) Math.random과 속도를 비교하는 것이다.
import java.util.Random;
public class main {
public static void main(String[] args) {
for(int loop = 0; loop < 3; loop++) {
long startTime = System.currentTimeMillis();
Random rnd = new Random();
long total = 0;
// 천만번 비교
for(int i = 0; i < 10000000; i++) {
total += (int)(rnd.nextDouble()*100);
}
long endTime = System.currentTimeMillis();
System.out.println(loop+1 + "번째, total : " + total + ", elapsed : " + (endTime-startTime) + "(ms)");
}
}
}
위 실행 결과는
1번째, total : 495093666, elapsed : 346(ms)
2번째, total : 494810294, elapsed : 324(ms)
3번째, total : 494802449, elapsed : 273(ms)
최종결과는 Math.random과 비슷한 속도가 나왔다. 즉 Math.random이든 Random 객체든 속도차이가 없다고 봐도 무방하며 사실 천만번 호출하는데 1초도 안걸리는 속도가 나오는 상황에서 뭐가 더 효과가 좋은지 더 좋은 방법이 있는지 고민할 필요는 없다고 본다.
다만 아래와 같은 차이점은 있다.
- Math.random 는 별도의 객체를 생성할 필요가 없기 때문에, 순간적인 난수값을 생성하기에 유리
- 정수값으로 랜덤 서비스를 자주하는 상황이라면, 확실히 Random 객체를 쓰는 것이 퍼포먼스 적으로 유리 다만 이 차이는 크지 않음
- 즉 정수로 생성할 땐 Random 객체 승, 실수값의 단순 난수를 할 땐 Math.random이 승
성능 외적으로 둘의 차이
그러나 Random 객체는 정수만 유리한 것은 아니다. 컴퓨터는 난수를 만들지 못하는 기계이기 때문에 난수를 만들기 위해서는 어떤 기준 값이 필요하고 이 기준값을 Seed라고 한다. 일반적으로 Seed값을 기준으로 잡는데 Math.random은 별도의 시드값을 지정할 수 없고 컴퓨터의 현재 시간을 기준으로 난수를 생성한다. 한마디로 Math.random은 난수를 내가 핸들링하기 어렵다는 의미이다. 그에 반면 Random 객체는 생성자에 시드값을 long형태로 전달할 수 있다.
예를 들어 동일한 확률을 주고 싶을 때가 있다. 문제가 100문제를 보유하고 있고 사람마다 동일한 문제를 랜덤으로 10문제를 낸다고 가정해보자 Math.random을 쓰게 되면 사람마다 난이도가 다른 문제 10개가 나오겠지만 Random 객체에서 시드값을 핸들링하면 모두 동일한 문제들을 랜덤 서비스로 볼 수 있다.
그럼에도 Math.random을 사용해야 될 상황이 있는 경우는 랜덤값을 뿌리는 상황이 서비스에 미비하고 매우 제한적이며 유지보수에 용이하기 위해서 사용될 때이다. Random은 new로 생성을 해야 하고 math.random에 비해 소스 코드가 지저분해 보일 수 있다.
결론은 Random 객체만 써도 상관이 없으나, 소스의 유지보수성, 가독성등을 위해서 코드를 줄이고 싶으면(몇개가 줄이나 싶겠지만...) Math.random을 쓰는 것도 나쁘지 않다 정도가 될 것 같다.
'프로그램언어 > 자바(Java)' 카테고리의 다른 글
[Java] 네이버 검색 API 등록 및 호출하기 (0) | 2021.02.24 |
---|---|
[Java] 자바에서 Map 반복 시키는 방법들 (0) | 2021.01.02 |
포조(Plain Old Java Object, POJO) 이해하기 (0) | 2020.09.28 |
[Java] 소수점 반올림하는 3가지 방법 (0) | 2020.05.26 |
[Java] 영상에서 썸네일(Thumbnail) 추출하기 (0) | 2020.03.16 |