2012-11-08 3 views
1

다음은 bumblebee 및 optirun을 사용하여 Ubuntu 12.04 (Precise)로 랩톱에서 실행 한 Nvidia CUDA의 루트 비어 예제 코드입니다. 이 노트북에는 Nvidia Optimus가 탑재되어 있습니다. GPU는 Nvidia 웹 사이트에서 말하는 96 코어의 Nvidia GeForce GT 540M입니다. 나는 거의 처리량 이득을 얻지 못한다. 문제가 무엇입니까? 루트 비어 CUDA 예제 코드 정량 처리량

package com.random.test; 

import java.util.ArrayList; 
import java.util.Formatter; 
import java.util.List; 

import edu.syr.pcpratts.rootbeer.runtime.Kernel; 
import edu.syr.pcpratts.rootbeer.runtime.Rootbeer; 

public class ArraySumApp { 
    final static int numberOfJobs = 1024; // 1024 in the original example 
    final static int sizeOfArray = 512; // 512 in the original example 
    final static int theAnswer = 130816; 

    public int[] sumArrays(List<int[]> arrays) { 

     List<Kernel> jobs = new ArrayList<Kernel>(); 
     int[] ret = new int[arrays.size()]; 
     for (int i = 0; i < arrays.size(); ++i) { 
      jobs.add(new ArraySum(arrays.get(i), ret, i)); 
     } 

     Rootbeer rootbeer = new Rootbeer(); 
     rootbeer.runAll(jobs); 
     return ret; 
    } 

    private static long measureOneJob() { 

     int[] source = new int[ArraySumApp.sizeOfArray]; 
     int[] destination = new int[1]; 
     for (int i = 0; i < ArraySumApp.sizeOfArray; i++) 
      source[i] = i; 
     Kernel job = new ArraySum(source, destination, 0); 

     ElapsedTimer et = new ElapsedTimer(); 
     job.gpuMethod(); 
     long timeInMs = et.stopInMilliseconds(); 
     System.out.println("measureOneJob " + et.stringInMilliseconds()); 

     assert destination[0] == ArraySumApp.theAnswer : "cosmic rays"; 
     return timeInMs; 
    } 

    public static void main(String[] args) { 

     Helper.assertAssertionEnabled(); 

     // measure the time to do one job 
     ArraySumApp.measureOneJob(); 
     long oneJob = ArraySumApp.measureOneJob(); 

     ArraySumApp app = new ArraySumApp(); 
     List<int[]> arrays = new ArrayList<int[]>(); 

     // you want 1000s of threads to run on the GPU all at once for speedups 
     for (int i = 0; i < ArraySumApp.numberOfJobs; ++i) { 
      int[] array = new int[ArraySumApp.sizeOfArray]; 
      for (int j = 0; j < array.length; ++j) { 
       array[j] = j; 
      } 
      arrays.add(array); 
     } 

     ElapsedTimer et = new ElapsedTimer(); 
     int[] sums = app.sumArrays(arrays); 
     long allJobs = et.stopInMilliseconds(); 
     System.out.println("measureAllJobs " + et.stringInMilliseconds()); 

     double gainFactor = ((double) ArraySumApp.numberOfJobs) * oneJob 
       /allJobs; 
     System.out.println(String.format(
       "throughput gain factor %.1f\nthroughput gain %.1f\n", 
       gainFactor, gainFactor - 1.0d)); 

     // check the number of answers is correct 
     assert sums.length == ArraySumApp.numberOfJobs : "cosmic rays"; 

     // check they all have the answer 
     for (int i = 0; i < ArraySumApp.numberOfJobs; i++) 
      assert sums[i] == ArraySumApp.theAnswer : "cosmic rays"; 
    } 
} 

class ArraySum implements Kernel { 

    final static int repetitionFactor = 100000; 

    private int[] source; 
    private int[] ret; 
    private int index; 

    public ArraySum(int[] src, int[] dst, int i) { 
     source = src; 
     ret = dst; 
     index = i; 
    } 

    public void gpuMethod() { 
     for (int repetition = 0; repetition < ArraySum.repetitionFactor; repetition++) { 
      int sum = 0; 
      for (int i = 0; i < source.length; ++i) { 
       sum += source[i]; 
      } 
      ret[index] = sum; 
     } 
    } 
} 

class Helper { 
    private Helper() { 
    } 

    static void assertAssertionEnabled() { 
     try { 
      assert false; 
     } catch (AssertionError e) { 
      return; 
     } 
     Helper.noteCosmicRays(); 
    } 

    static void noteCosmicRays() // programmer design or logic error 
    { 
     throw new RuntimeException("cosmic rays"); 
    } 
} 

class ElapsedTimer { 
    private org.joda.time.DateTime t0; 
    private long savedStopInMilliseconds; 

    public ElapsedTimer() { 
     this.t0 = new org.joda.time.DateTime(); 
    } 

    public long stopInMilliseconds() { 
     return stop(); 
    } 

    public String stringInMilliseconds() // relies on a saved stop 
    { 
     Formatter f = new Formatter(); 
     f.format("%d ms", this.savedStopInMilliseconds); 
     String s = f.toString(); 
     f.close(); 
     return s; 
    } 

    public String stopStringInMilliseconds() { 
     stop(); 
     return stringInMilliseconds(); 
    } 

    public String stringInSecondsAndMilliseconds() // relies on a saved stop 
    { 
     Formatter f = new Formatter(); 
     f.format("%5.3f s", this.savedStopInMilliseconds/1000.0d); 
     String s = f.toString(); 
     f.close(); 
     return s; 
    } 

    public String stopStringInSecondsAndMilliseconds() { 
     stop(); 
     return stringInSecondsAndMilliseconds(); 
    } 

    public long stopInSeconds() { 
     return (stop() + 500L)/1000L; // rounding 
    } 

    public String stringInSeconds() // relies on a saved stop 
    { 
     Formatter f = new Formatter(); 
     long elapsed = (this.savedStopInMilliseconds + 500L)/1000L; // rounding 
     f.format("%d s", elapsed); 
     String s = f.toString(); 
     f.close(); 
     return s; 
    } 

    public String stopStringInSeconds() { 
     stop(); 
     return stringInSeconds(); 
    } 

    /** 
    * This is private. Use the stopInMilliseconds method if this is what you 
    * need. 
    */ 
    private long stop() { 
     org.joda.time.DateTime t1 = new org.joda.time.DateTime(); 
     savedStopInMilliseconds = t1.getMillis() - this.t0.getMillis(); 
     return savedStopInMilliseconds; 
    } 
} 

출력이다

measureOneJob 110 ms 
measureOneJob 26 ms 
CudaRuntime2 ctor: elapsedTimeMillis: 609 
measureAllJobs 24341 ms 
throughput gain factor 1.1 
throughput gain 0.1 
+0

나는 rootbeer의 저자에게 이것에 대해 물어볼 필요가 있다고 생각합니다. – talonmies

답변

1

rootbeer 개발자는 어레이 요소의 합을 취하는 예 코드 가장 예 아니며 다른 예 처리량 향상을 표시 것이라고 말했다.

1

당신은 볼 수 있습니다 https://github.com/pcpratts/rootbeer1/tree/develop/gtc2013/Matrix

이것은 2013 엔비디아 GTC 회의에 대한 예입니다. 전치사를 사용하는 4 코어 Java Matrix Multiply보다 20 배 빨라졌습니다.

이 예제는 GPU에서 공유 메모리를 사용하는 타일 된 매트릭스 곱하기입니다. 엔비디아 문학에서 공유 메모리를 사용하는 것이 좋은 스피드 업을 얻는 가장 중요한 해결책 중 하나입니다. 공유 메모리를 사용하려면 블록의 각 스레드가 공유 배열에 값을로드해야합니다. 그런 다음 이러한 공유 값을 여러 번 재사용해야합니다. 이렇게하면 전역 메모리에서 가져올 시간을 절약 할 수 있습니다.

전역 메모리에서 가져 오기에는 약 200-300 클럭 사이클이 걸리고 공유 메모리에서 가져 오는 데는 Tesla 2.0 아치 메모리에서 약 2-3 클럭 사이클이 걸립니다.