2012-12-01 4 views
0

이것은 아주 큰 프로젝트에서 처음으로 작업 한 것이므로 최상의 성능을 얻으라는 질문을 받았습니다.최적화 : for 루프를 ListIterator로 바꿉니다.

약 15 개의 요소가있는 목록에 list.get(i)을 호출하는 약 180 개의 루프가 있기 때문에 for 루프를 ListIterator으로 바꾸는 대신에 thouhgt를 사용했습니다.

그래서 두 가지 질문이 있습니다.

1) 해당 스 니펫은 동일한가요? 내말은, 그들도 같은 산출물을 산출합니까? 아니라면, ListIterator 것을 어떻게 수정할 수 있습니까? 나는 이런 식으로 뭔가있어 경우 첫 번째 조각이 얼마나

for (int i = 1; i < rides.size(); i++) { 
    if (rides.get(i).getOP() < d.getFP() && rides.get(i - 1).getOA() > d.getIP() && rides.get(i).getOP() - rides.get(i - 1).getOA() > DP) { 
     doSomething(); 
      break; 
     } 
    } 

2) __

ListIterator<Corsa> ridesIterator = rides.listIterator(); 
    while (ridesIterator.hasNext()) { 
     ridesIterator.next(); 
     Corsa previous = ridesIterator.previous(); //rides.get(i-1) 
     Corsa current = ridesIterator.next(); //rides.get(i) 
     if (current.getOP() < d.getFFP() && previous.getOA() > d.getIP() && current.wait(previous) > DP) { 
      doSomething(); 
      break; 
     } 
    } 

? (변경 전 및 종료 조건)

for (int i = 0; i < rides.size() - 1; i++) { 
    if (rides.get(i).getOP() < d.getFP() && rides.get(i + 1).getOA() > d.getIP() && rides.get(i).getOP() - rides.get(i + 1).getOA() > DP) { 
     doSomething(); 
      break; 
     } 
    } 

가 나는 ListIterator을 사용하고 처음이고 나는 지금 그것을 시도 할 수 없기 때문에 내가 부탁 해요!

편집 : 나는 ArrayList에를 사용하지 않는, 그것은 EDIT 2

LinkedList의 기반으로 사용자 정의 목록입니다 : 나는 좀 더 많은 정보를 정기적으로 추가 해요. 일관성없는 데이터를 처리해야하므로 evry 반복에서 내 데이터가 변경되고 캐시 관리가 어려워 캐싱 시스템을 사용할 수 없습니다. 이 루프의 일부를 하나의 큰 루프로 병합 할 수도 없습니다. 다른 방법을 사용하기 때문에 여러 가지 작업을 많이해야하기 때문입니다.

그래서이 특별한 경우를 고수하면서 가장 좋은 징계는 무엇이라고 생각하십니까? ListIterator가 내 사건을 다루는 가장 좋은 방법입니까? for 루프가 0에서 size-1 사이에서 작동하면 어떻게 ListIterator를 사용할 수 있습니까?

+0

(!) 참고 : '놀이기구 .get (i-1)'은 첫 번째 요소에 대해 예외를 던질 것이고 반복자를 사용하는 접근법은 똑같이 할 것입니다. 또한, 반복자를 사용하는 코드는'ridesIterator.next()'와 함께 라인의 마지막 요소에 대한 예외를 던집니다. –

+0

실수로, 두 번째는'rides.get (i-1)'대신'rides.get (i + 1)'을 가져야합니다. Fixed – StepTNT

+1

'rides'가'ArrayList'의 인스턴스라면,'rides.get (i)'는 O (1)에서 끝납니다. 이 경우 변환을 통해 프로그램을 훨씬 빠르게 작성하지 못할 수 있습니다. – reprogrammer

답변

0

몇 가지 제안 사항이 있습니다. 귀하의 상황에 하나 또는 두 가지가 적용될 수 있기를 바랍니다.

  1. 루프 당 하나의 rides.get (x) 만 실행하십시오.
  2. 캐시 방법은 코드에 맞게 로컬 변수를 사용합니다.

는 어떤 경우 컴파일러는 한 번만 대신 그 일을 같은 일을 여러 번 호출을 최적화하며, 항상은 아니지만 많은 미묘한 이유로 수 있습니다. 프로그래머가 동일한 값을 제공해야한다는 사실을 알고 있다면 로컬 변수에 캐시하십시오. 예를 들어

,

int sz = rides.size(); 
float dFP = d.getFP(); // wasn't sure of the type, so just called if float.. 
float dIP = d.getIP(); 
Corsa lastRide = rides.get (0); 
for (int i = 1; i < sz; i++) { 
    Corsa = rides.get (i); 
    float rOP = r.getOP(); 
    if (rOP < dFP) { 
     float lastRideOA = lastRide.getOA(); // only get OA if rOP < dFP 
     if (lastRideOA > dIP && rOP - lastRideOA > DP) { 
      doSomething(); 
      // maybe break; 
     } 
    } 
    lastRide = r; 
} 

는이 모든 경우에 작동하지 않을 수 있습니다 최적화합니다. 예를 들어 doSomething이 목록을 확장 한 경우 sz를 다시 계산하거나 각 반복을 rides.size()로 되돌릴 수 있습니다. 또한 이러한 최적화는 요소가 get..() '동안 변경되지 않는다는 점에서 목록이 안정적이라고 가정합니다. doSomething이 목록을 변경하면 캐시를 줄여야합니다. 바라기를 당신은 아이디어를 얻는다. 루프의 이터레이터 양식에도 이러한 기술 중 일부를 적용 할 수 있습니다.

+0

답장을 보내 주셔서 감사합니다.내 질문에 일부 정보를 놓친 것을 알고 있으므로 지금 추가하고 있습니다. 사실, 캐싱 시스템을 추가 할 방법이 없습니다. 왜냐하면 각각의 (180 개 이상!)이 경험적 최적화 기술과 같은 작업을하고 있기 때문에 모든 반복에서 데이터가 변경되기 때문입니다! 그렇기 때문에 동일한 루프에서 모든 것을 처리 할 수없는 이유가 있습니다. 하나의 루프를 완료하고 새 솔루션이 이전보다 우수한 지 확인한 다음 다음 단계로 이동해야하기 때문입니다! – StepTNT

1

최대 크기를 알고있는 경우 ArrayList과 같은 컬렉션에서 사임하면 최상의 성능을 얻을 수 있습니다.

대신 ArrayList<Corsa>을 5000 개의 요소로 생성하려면 Corsa[] rides = new Corsa[5000]을 수행하십시오. 코드에서 magic number을 피하려면 5000을 하드 코딩하는 대신 final static int MAX_RIDES = 5000으로 사용하십시오. 그런 다음 rides[i]을 참조하여 정상 반복으로합니다.

일반적으로 성능을 찾으면 C/C++ (마치 할 수있는 곳) 인 것처럼 Java로 코딩해야합니다. 코드는 객체 지향적이면서도 아름답지 만 빠릅니다. 항상 최적화를 수행해야한다는 것을 잊지 마십시오. 확실 할 때 병목 현상을 발견했습니다. 그렇지 않으면 코드를 읽기 쉽고 유지 보수하기가 쉽지 않은 당신의 노력은 쓸모 없습니다. 또한 profiler을 사용하여 변경 사항이 다운 그레이드가 아닌 업그레이드인지 확인하십시오.

ListIterator을 사용하는 또 다른 단점은 내부적으로 메모리를 할당한다는 것입니다. 따라서 GC (Garbage Collector)가 더 자주 깨어나고 전반적인 성능에 영향을 줄 수 있습니다.

+0

'ArrayList'를 사용할 수 없어서 첫 글을 편집했습니다. 나는 당신의 해결책을 좋아하지만 목록의 최대 크기를 모른다. 그래서 나는 이런 식으로 갈 수 없다! 그래도 좋은 제안! – StepTNT

+0

@StepTNT'ArrayList'를 사용하지 마십시오. 일반적인 자바 배열을 사용하십시오. 당신은 귀하의 게시물에 "약 5000 요소 목록", 그래서 최대 10000 요소를 설정할 수 있습니다, 한 번 큰 배열을 할당 (당신은 모든 할당 된 공간을 사용할 필요가 없습니다) 다음 속도로 행복하게 . –

+0

알아요,하지만 그 순간에 제가 작업하고있는 데이터의 크기입니다. 상한선이 될지 모르겠다.이 소프트웨어가 판매되고 목록 크기를 제한하는 것이 최선책이 아니기 때문에 고정 값으로 설정할 수도 없다. sidenote로서,이 목록은 내가 뭔가를 삽입하는 evertime으로 정렬되어야합니다. 그래서 모든 것을 아주 어렵게 만들 것이기 ​​때문에 배열을 기반으로하는 것을 사용할 수는 없습니다. – StepTNT

1
  1. 아니요, 동일하지 않습니다.

    while (ridesIterator.hasNext()) { 
        ridesIterator.next(); 
        Corsa previous = ridesIterator.previous(); //rides.get(i-1) 
        Corsa current = ridesIterator.next(); //rides.get(i) 
    

    변수는 이전 및 현재 (반복자 위치 "사이에서"임)에 대한 세부 사항을 참조 ListIterator documentation 같은 "코르사"값을 포함 할 것이다.

    으로 보일 것이다 올바른 코드는 다음과 같습니다

    while (ridesIterator.hasNext()) { 
        Corsa previous = ridesIterator.next(); //rides.get(i-1) 
        if(!ridesIterator.hasNext()) 
        break; // We are already at the last element 
        Corsa current = ridesIterator.next(); //rides.get(i) 
        ridesIterator.previous(); // going back 1, to start correctly next time 
    
  2. (주석 참조) 코드는 실제로 정확히 같은, 단지 해석을 보일 것이다 다른 것 :

    while (ridesIterator.hasNext()) { 
        Corsa previous = ridesIterator.next(); //rides.get(i) 
        if(!ridesIterator.hasNext()) 
        break; // We are already at the last element 
        Corsa current = ridesIterator.next(); //rides.get(i+1) 
        ridesIterator.previous(); // going back 1, to start correctly next time 
    

(미숙 한) 최적화 관점에서 볼 때 ListIterator 구현이 더 좋습니다.

  • LinkedList는 각 요소가 선행자 (이전)와 후속자 (다음) 모두에 연결된다는 것을 의미하는 이중 연결 목록입니다. 따라서 루프 당 3 번의 참조가 발생합니다. => 3 * N
  • 각 get (i)는 i 인덱스 위치에 도달하기 위해 모든 이전 요소를 거쳐야합니다. 따라서 루프 당 평균 N/4 추천. (당신은 N/2을 생각 싶지만, LinkedList의 처음 또는 목록의 끝에서 시작됩니다.) => 2 * N * N/4 == N^2/2
관련 문제