for 루프와 for 루프 사이에는 성능 차이가 있습니까?
다음 두 루프의 성능 차이는 무엇입니까?
for (Object o: objectArrayList) {
o.DoSomething();
}
그리고.
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
Jooshua Bloch의 효과적인 Java 항목 46에서:
릴리스 1.5에서 도입된 각 루프는 반복기 또는 인덱스 변수를 완전히 숨김으로써 혼란과 오류의 가능성을 제거합니다.그 결과 나타나는 관용구는 컬렉션과 배열에도 동일하게 적용됩니다.
// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }
콜론(:)이 표시되면 "in"으로 읽습니다.따라서 위의 루프는 "요소의 각 요소 e에 대하여"로 읽힙니다.어레이에서도 각 루프를 사용해도 성능 저하가 발생하지 않습니다.실제로 어레이 인덱스의 한도를 한 번만 계산하기 때문에 일반적인 for 루프에 비해 약간의 퍼포먼스가 향상될 수 있습니다.이것은 수작업(항목 45)으로 실행할 수 있지만 프로그래머가 항상 실행하는 것은 아닙니다.
이 모든 루프가 똑같이 작용합니다. 제 의견을 말하기 전에 이것들을 보여드리고 싶습니다.
먼저, 목록을 루프하는 전형적인 방법:
for (int i=0; i < strings.size(); i++) { /* do something using strings.get(i) */ }
둘째, 오류 발생률이 낮기 때문에 권장되는 방법(루프 내에서 "oops, mixed variables i and j in this loops?"를 몇 번이나 실행했습니까?)
for (String s : strings) { /* do something using s */ }
셋째, 마이크로 최적화 루프:
int size = strings.size();
for (int i = -1; ++i < size;) { /* do something using strings.get(i) */ }
이제 실제 2센트:적어도 테스트했을 때는 간단한 조작으로 각 루프 타입에 걸리는 시간을 밀리초 단위로 계산했을 때 세 번째 루프가 가장 빨랐습니다.이것은 Java 5와 jre1을 사용한 것입니다.6u10 (Windows) (관심있는 사용자용)
적어도 세 번째 프로그램이 가장 빠른 것처럼 보이지만 실제 루프는 실제 프로그램 중 가장 시간이 많이 걸리는 부분이 아니기 때문에 루프 코드의 모든 부분에 이 핍홀 최적화를 구현해야 하는지 자문해 보십시오.엘드, 누가 알아?또, Java for-loop(일부에서는 반복 루프, 다른 경우에는 for-in 루프라고 부릅니다)의 핑계로 언급했듯이, 이 버그를 사용할 때 특정 버그가 발생할 가능성은 낮아집니다.그리고 어떻게 하면 다른 것보다 더 빠를 수 있는지에 대해 토론하기 전에 javac은 바이트 코드를 전혀 최적화하지 않고 컴파일만 한다는 것을 기억하십시오.
마이크로 최적화에 관심이 있거나 소프트웨어가 많은 재귀 루프를 사용한다면 세 번째 루프 타입에 관심이 있을 수 있습니다.루프용 루프를 마이크로 최적화한 이 기묘한 루프로 변경하기 전후에 소프트웨어를 벤치마킹하는 것을 잊지 마십시오.
퍼포먼스에 미치는 영향은 대부분 미미하지만 제로라고는 할 수 없습니다.,RandomAccess
★★★★★★★★★★★★★★★★★★:
일반적인 클래스의 인스턴스에서 이 루프가 다음과 같은 경우 List 구현은 이 인터페이스를 구현해야 합니다.
for (int i=0, n=list.size(); i < n; i++) list.get(i);
이 루프보다 고속으로 동작합니다.
for (Iterator i=list.iterator(); i.hasNext();) i.next();
, 각 함께 에, 「 」의 경우는, 「 」입니다.ArrayList
예를 들어, 각 루프가 가장 빠르지 않습니다.
일반적으로는 각 루프가 우선됩니다.사용 중인 목록 구현이 랜덤 액세스를 지원하지 않는 경우 "get" 접근 방식이 느려질 수 있습니다.예를 들어 LinkedList를 사용하면 트래버설비용이 발생하지만 각 접근법에서는 목록 내 위치를 추적하는 반복기를 사용합니다.각 루프의 차이점에 대한 자세한 내용은 다음과 같습니다.
기사는 여기 있을 겁니다.새로운 장소
여기에 표시된 링크는 비활성 상태입니다.
유감스럽게도 차이가 있는 것 같습니다.
두 종류의 루프에 대해 생성된 바이트코드를 보면 서로 다릅니다.
Log4j 소스 코드의 예를 다음에 나타냅니다.
/log4j-api/src/main/java/org/apache/logg4j/MarkerMarker.java에는 Log4jMarker라는 이름의 정적 내부 클래스가 있습니다.이 클래스는 다음을 정의합니다.
/*
* Called from add while synchronized.
*/
private static boolean contains(final Marker parent, final Marker... localParents) {
//noinspection ForLoopReplaceableByForEach
for (final Marker marker : localParents) {
if (marker == parent) {
return true;
}
}
return false;
}
표준 루프 사용 시:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
Code:
0: iconst_0
1: istore_2
2: aload_1
3: arraylength
4: istore_3
5: iload_2
6: iload_3
7: if_icmpge 29
10: aload_1
11: iload_2
12: aaload
13: astore 4
15: aload 4
17: aload_0
18: if_acmpne 23
21: iconst_1
22: ireturn
23: iinc 2, 1
26: goto 5
29: iconst_0
30: ireturn
각각 포함:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
Code:
0: aload_1
1: astore_2
2: aload_2
3: arraylength
4: istore_3
5: iconst_0
6: istore 4
8: iload 4
10: iload_3
11: if_icmpge 34
14: aload_2
15: iload 4
17: aaload
18: astore 5
20: aload 5
22: aload_0
23: if_acmpne 28
26: iconst_1
27: ireturn
28: iinc 4, 1
31: goto 8
34: iconst_0
35: ireturn
Oracle은 어떻게 된 거죠?
Windows 7에서 Java 7과 Java 8을 사용해 본 적이 있습니다.
Forach는 코드의 의도를 명확하게 하며, 일반적으로 매우 작은 속도 향상(있는 경우)보다 선호됩니다.
인덱스된 루프가 발견될 때마다 조금 더 해석하여 내가 생각하는 대로 동작하는지 확인해야 합니다.0부터 시작합니까, 끝점을 포함합니까, 제외합니까?
대부분의 시간은 코드(저나 다른 사람이 쓴 것)를 읽는 데 소비되는 것 같습니다.또한 거의 항상 퍼포먼스보다 명료성이 중요합니다.요즘은 핫스팟이 놀라운 기능을 하기 때문에 성능을 무시하기가 쉽습니다.
다음 코드:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
interface Function<T> {
long perform(T parameter, long x);
}
class MyArray<T> {
T[] array;
long x;
public MyArray(int size, Class<T> type, long x) {
array = (T[]) Array.newInstance(type, size);
this.x = x;
}
public void forEach(Function<T> function) {
for (T element : array) {
x = function.perform(element, x);
}
}
}
class Compute {
int factor;
final long constant;
public Compute(int factor, long constant) {
this.factor = factor;
this.constant = constant;
}
public long compute(long parameter, long x) {
return x * factor + parameter + constant;
}
}
public class Main {
public static void main(String[] args) {
List<Long> numbers = new ArrayList<Long>(50000000);
for (int i = 0; i < 50000000; i++) {
numbers.add(i * i + 5L);
}
long x = 234553523525L;
long time = System.currentTimeMillis();
for (int i = 0; i < numbers.size(); i++) {
x += x * 7 + numbers.get(i) + 3;
}
System.out.println(System.currentTimeMillis() - time);
System.out.println(x);
x = 0;
time = System.currentTimeMillis();
for (long i : numbers) {
x += x * 7 + i + 3;
}
System.out.println(System.currentTimeMillis() - time);
System.out.println(x);
x = 0;
numbers = null;
MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
for (int i = 0; i < 50000000; i++) {
myArray.array[i] = i * i + 3L;
}
time = System.currentTimeMillis();
myArray.forEach(new Function<Long>() {
public long perform(Long parameter, long x) {
return x * 8 + parameter + 5L;
}
});
System.out.println(System.currentTimeMillis() - time);
System.out.println(myArray.x);
myArray = null;
myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
for (int i = 0; i < 50000000; i++) {
myArray.array[i] = i * i + 3L;
}
time = System.currentTimeMillis();
myArray.forEach(new Function<Long>() {
public long perform(Long parameter, long x) {
return new Compute(8, 5).compute(parameter, x);
}
});
System.out.println(System.currentTimeMillis() - time);
System.out.println(myArray.x);
}
}
시스템에 다음과 같은 출력이 표시됩니다.
224
-699150247503735895
221
-699150247503735895
220
-699150247503735895
219
-699150247503735895
Oracle JDK 1.7 업데이트 6에서 Ubuntu 12.10 alpha를 실행하고 있습니다.
일반적으로 HotSpot은 많은 지시사항과 간단한 재지정 작업을 최적화하므로 일반적으로 이러한 작업이 많거나 많이 중첩되지 않는 한 걱정할 필요가 없습니다.
한편, LinkedList의 인덱스화된 get은 LinkedList의 리테이터에서 다음에 호출하는 것보다 훨씬 느리기 때문에 리테이터를 사용할 때(각 루프에 대해 명시적 또는 암묵적으로) 가독성을 유지하면서 퍼포먼스 저하를 방지할 수 있습니다.
다음은 Android 개발팀이 제시한 차이점에 대한 간단한 분석입니다.
https://www.youtube.com/watch?v=MZOf3pOAM6A
그 결과 차이가 있으며, 목록이 매우 많은 매우 제한된 환경에서는 현저한 차이가 발생할 수 있습니다.테스트에서는 각 루프에 대해2배의 시간이 걸렸습니다.하지만, 그들의 테스트는 400,000개의 정수로 이루어진 배열 목록을 넘었습니다.어레이의 요소당 실제 차이는 6마이크로초였습니다.아직 테스트도 하지 않았지만, 원형이 아닌 오브젝트를 사용하는 경우 차이가 조금 클 것으로 예상되지만, 반복하도록 요구받는 것의 규모를 모르는 라이브러리 코드를 구축하지 않는 한 그 차이는 강조할 가치가 없다고 생각합니다.
확실히 알 수 있는 유일한 방법은 벤치마킹하는 것입니다.그것조차 들리는 것처럼 간단하지 않습니다.JIT 컴파일러는 코드에 매우 예기치 않은 작업을 수행할 수 있습니다.
"get"이 단순한 어레이 룩업인 ArrayList나 Vector를 사용하더라도 두 번째 루프에는 첫 번째 루프에는 없는 추가 오버헤드가 있습니다.처음보다 조금 더 느릴 것으로 예상합니다.
이름 " " " " 에 objectArrayList
그 일 거예요.java.util.ArrayList
이 경우 성능 차이는 눈에 띄지 않습니다.
편, 약, 약, 약, on, on, 약, 약, on, on, on, on, on, on, on, on의 라면,java.util.LinkedList
은, 의 List#get(int)
연산입니다.
따라서 루프의 논리에 의해 인덱스가 필요하지 않는 한 첫 번째 접근법이 항상 선호됩니다.
다른 답변은 잘못된 벤치마크를 기반으로 하고 있어 Hotspot의 컴파일 및 최적화 프로세스를 고려하지 않은 것 같습니다.
단답
가능한 한 enhanced-loop을 사용합니다.대부분이 가장 빠르기 때문입니다.할 수 없는 경우 가능하면 전체 배열을 로컬 변수로 가져옵니다.
int localArray = this.array;
for (int i = 0; i < localArray.length; i++) {
methodCall(localArray[i]);
}
장답
Hotspot은 Java가 해야 하는 체크의 최적화와 제거에 매우 능숙하기 때문에 보통 차이가 없습니다.
다만, 최적화를 실시할 수 없는 경우도 있습니다.보통 루프 내에 인라인을 할 수 없는 가상 콜이 있기 때문입니다.이 경우 일부 루프가 다른 루프보다 더 빠를 수 있습니다.
Java가 수행해야 할 몇 가지 작업은 다음과 같습니다.
- reload this.array - (콜 또는 다른 스레드에 의해) 변경될 수 있기 때문에
- IndexOutOfBoundsException을 슬로우하지 않으면 어레이 경계 내에 있는지 확인합니다.
- 액세스된 개체 참조가 null인지 여부를 확인합니다(예인 경우 Null Pointer를 던집니다).예외
다음 c-style 루프를 고려합니다.
for (int i = 0; i < this.array.length; i++) { //now java knows i < this.array.length
methodCall(this.array[i]);// no need to check
}
루프 조건 i < this . array . length를 평가함으로써 Java는 내가 경계 내에 있어야 함을 인식하기 때문에(i는 콜 후에만 변경되므로) 다음 행에서 다시 확인할 필요가 없습니다.단, 이 경우 Java는 이.array.length를 새로고침해야 합니다.
로컬 변수 내에서 this.array.length 값을 풀함으로써 루프를 "최적화"하려고 할 수 있습니다.
int len = this.array.length;//len is now a local variable
for (int i = 0; i < len; i++) { //no reload needed
methodCall(this.array[i]); //now java will do the check
}
로컬 변수는 methodCall 또는 다른 스레드로 변경할 수 없기 때문에 Java는 매번 새로고침할 필요가 없습니다.로컬 변수는 메서드 자체 내에서만 변경할 수 있으며, 이제 Java는 변수 len이 변경할 수 없음을 증명할 수 있습니다.
그러나 루프 조건 i < this . array . length가 i <렌으로 변경되어 이전의 최적화가 실패하여 Java는 이.array의 경계 내에 있는i가 있는지 여부를 확인해야 합니다.
보다 나은 최적화는 어레이 전체를 로컬 변수로 가져오는 것입니다.
int localArray = this.array;
for (int i = 0; i < localArray.length; i++) {
methodCall(localArray[i]);
}
이제 Java는 어레이를 새로고침할 필요가 없으며 "i in bounds" 검사도 필요하지 않습니다.
확장 루프는요?보통 컴파일러는 확장 루프를 마지막으로 표시한 루프와 같은 것으로 다시 씁니다(더 좋지는 않더라도).
인덱싱 대신 반복기를 사용하는 것이 항상 좋습니다.이는 반복기가 목록 구현에 최적화되어 있을 가능성이 높지만 색인화(콜링 get)되지 않을 수 있기 때문입니다.예를 들어 LinkedList는 목록이지만 해당 요소를 통한 인덱싱은 반복기를 사용하여 반복하는 것보다 느립니다.
foreach는 메모리를 (반복기의 형태로) 할당하는 반면, 일반 for 루프는 메모리를 할당하지 않는 것은 이상합니다.Android의 게임에서는, 이것은 문제가 됩니다.이는 가비지 콜렉터가 정기적으로 실행된다는 것을 의미하기 때문입니다.게임에서는 쓰레기 수집기가 작동하지 않도록...EVER. 따라서 그리기(또는 렌더링) 방식에서 foreach 루프를 사용하지 마십시오.
1. for(Object o: objectArrayList){
o.DoSomthing();
}
and
2. for(int i=0; i<objectArrayList.size(); i++){
objectArrayList.get(i).DoSomthing();
}
둘 다 동일하지만 쉽고 안전한 프로그래밍 사용을 위해 두 번째 사용 방법에서 오류가 발생할 수 있습니다.
Array List의 예외적인 경우를 제외하고 답변이 받아들여지면 질문에 답변할 수 있습니다.
대부분의 개발자가 Array List에 의존하기 때문에 (적어도 그렇다고 생각합니다)
그래서 나는 여기에 정답을 추가할 의무가 있다.
개발자 매뉴얼에서 직접 확인:-
확장 for 루프('각' 루프라고도 함)는 Itherable 인터페이스를 구현하는 컬렉션 및 어레이에 사용할 수 있습니다.컬렉션에서는 인터페이스 콜을 hasNext() 및 next()에 발신하기 위해 반복기가 할당됩니다.ArrayList를 사용하면 수기 카운트 루프가 약 3배 고속(JIT 유무에 관계없이)이지만, 그 외의 컬렉션에서는 확장 for 루프 구문이 명시적인 반복기 사용과 완전히 동일합니다.
어레이를 통해 반복하는 방법에는 다음과 같은 몇 가지가 있습니다.
static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
제로()는 루프를 통과하는 모든 반복에 대해 어레이 길이를 1회 취득하는 비용을 최적화할 수 없기 때문에 가장 느립니다.
1()이 더 빠릅니다.모든 것을 로컬 변수로 추출하여 검색을 회피합니다.어레이의 길이만이 퍼포먼스의 이점을 가져옵니다.
2()는 JIT가 없는 디바이스에서는 가장 빠르고, JIT가 있는 디바이스에서는 1()과 구별할 수 없습니다.Java 프로그래밍 언어 버전 1.5에서 도입된 확장 루프 구문을 사용합니다.
따라서 디폴트로는 루프용으로 확장기능을 사용해야 하지만 퍼포먼스가 중요한 ArrayList 반복에는 수기 카운트루프를 고려해 주십시오.
언급URL : https://stackoverflow.com/questions/256859/is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop
'sourcecode' 카테고리의 다른 글
C에 유용한 GCC 플래그는 무엇입니까? (0) | 2022.08.10 |
---|---|
부모 빌드 툴을 사용하는 Vue 컴포넌트를 올바르게 작성하는 방법 (0) | 2022.08.10 |
@ManyToOne 속성에서는 열을 사용할 수 없습니다. (0) | 2022.08.10 |
Java에서의 Array Lists의 교차로 및 결합 (0) | 2022.08.09 |
vue 부트스트랩 드롭다운 목록 항목을 동적으로 추가하는 방법 (0) | 2022.08.09 |