我应该如何并行计算昂贵的for循环并整理迭代结果?

时间:2014-03-11 07:14:55

标签: java multithreading performance parallel-processing

我正在研究一台8核机器,并且正在执行计算量很大的任务。然而,任务的每次执行(即,for循环的迭代)都与前一个独立。只有一些变量从一个执行到下一个执行“总结”。我猜这是并行/线程的一个很好的例子,但我不知道如何去做。

以下是代码的外观。截至目前,它只是我的主执行器类中主要方法的一部分:

double testerPayoffSum = 0.0, developerPayoffSum = 0.0;
Random seed = new Random();

        try {
            for (int i = 0; i < GameConstants.MAX_GAMES; i++) {
                EraserSimulator eraser = new EraserSimulator(GameConstants.MAX_TARGETS, GameConstants.MAX_RESOURCES, GameConstants.NUM_ATTACKER_TYPES, seed.nextInt());
                Map<Set<SingleObjectiveTarget>, Double> gameStrategy = eraser.run();

                assert (gameStrategy != null);

                TestingGameSimulator testingGame = new TestingGameSimulator(GameConstants.MAX_TARGETS, gameStrategy, GameConstants.NUM_GAMES_TO_STORE_FOR_HISTORY, GameConstants.NUM_TESTING_GAMES_TO_PLAY);                
                PlayerPayoffs payoffs = testingGame.run(eraser.getEraserInstance());

                testerPayoffSum += payoffs.getAverageTesterPayoff(GameConstants.NUM_TESTING_GAMES_TO_PLAY);
                developerPayoffSum += payoffs.getAverageDeveloperPayoff(GameConstants.NUM_TESTING_GAMES_TO_PLAY);
                System.out.print("Output: ERASER Games played; Number of developers caught");
                System.out.print(", " + GameConstants.NUM_TESTING_GAMES_TO_PLAY + ", " + payoffs.getNumTimesCaught() + "\n");

            } catch(Exception e){sendEmailAlert("Execution Failed with Exception");}

如果可能,我想并行for-loop计算并继续总结testerPayoffSumdeveloperPayofffSum变量。我怎么能实现这个目标?

注意:每次执行for循环大约需要20-30分钟,具体取决于输入大小(由各种GameConstant设置)。即使是少数MAX_GAMES,上述时间也要接近2-3小时。

3 个答案:

答案 0 :(得分:1)

创建一个实现Callable的线程对象,该对象返回包含FuturetesterPayoffSum的{​​{1}}对象,开始计算并对从{{1}获得的结果求和} s(另见https://blogs.oracle.com/CoreJavaTechTips/entry/get_netbeans_6)。

答案 1 :(得分:1)

你绝对确定你没有依赖吗?

1.使用过的类不得共享任何变量

  • 如果有,则必须添加锁
  • 但会影响效果
  • 如果广泛使用某些共享变量
  • 然后,即使是非并行执行,性能也会显着下降

2.使用过的课程不得使用任何机器学习。

  • 没有解决方案
  • 因为并行化会破坏您的结果

现在该怎么做(我不是JAVA编码器所以我坚持使用C ++代码)。

//--- globals and headers -----------------------------------------------------

unsigned long __stdcall function(LPVOID p);
Random seed = new Random();
const int N=8;          // threads count (<=CPU count)
int id[N];          // thread id
int max[N];         // number of games per thread
double testerPayoffSum[N];  // sum to separate variables to avoid locks need
double developerPayoffSum[N];
volatile int run=0,stop=0;  // thread control variables run is number of running threads and stop force stop...

//--- main code ---------------------------------------------------------------

// init some variables ... may be the seed init will be better here too
int i;                      
for (i = 0; i < N; i++) 
    {
    id[i]=i;
    max[i]=GameConstants.MAX_GAMES / N;
    testerPayoffSum[i]=0.0;
    developerPayoffSum[i]=0.0;
    } 
max[0]=GameConstants.MAX_GAMES % N;

// create threads
for (i = 0; i < N; i++)
    {
    HANDLE hnd=CreateThread(0,0,function,&id[i],0,0); 
    if (hnd!=NULL) CloseHandle(hnd); // this line is important !!! 
    // because if you do not close Handle it will be allocated until the end of app
    // handle leaks are nasty and cause weird OS behaviour
    // I saw many times this bug in commercial drivers
    // it is a nightmare for 24/7 software
    }

// wait for them
while (run) Sleep(200);

// sum the results to [0]
for (i = 1; i < N; i++)
    {
    testerPayoffSum[0]   +=testerPayoffSum[i];
    developerPayoffSum[0]+=developerPayoffSum[i];
    }
// here do what you need to do with the results

//--- thread function ---------------------------------------------------------
unsigned long __stdcall function(LPVOID p)
    {
    run++;
    int ix=((int*)p)[0];
    for (i = 0; i < max[ix]; i++)
        {
        if (stop) break;
        EraserSimulator eraser = new EraserSimulator(GameConstants.MAX_TARGETS, GameConstants.MAX_RESOURCES, GameConstants.NUM_ATTACKER_TYPES, seed.nextInt());
        Map<Set<SingleObjectiveTarget>, Double> gameStrategy = eraser.run();
        assert (gameStrategy != null);
        TestingGameSimulator testingGame = new TestingGameSimulator(GameConstants.MAX_TARGETS, gameStrategy, GameConstants.NUM_GAMES_TO_STORE_FOR_HISTORY, GameConstants.NUM_TESTING_GAMES_TO_PLAY);                
        PlayerPayoffs payoffs = testingGame.run(eraser.getEraserInstance());
        testerPayoffSum[ix] += payoffs.getAverageTesterPayoff(GameConstants.NUM_TESTING_GAMES_TO_PLAY);
        developerPayoffSum[ix] += payoffs.getAverageDeveloperPayoff(GameConstants.NUM_TESTING_GAMES_TO_PLAY);
//      do not call any visual stuff from thread !!! sometimes it can cause a lot of problems ...
//      instead cretae some global string variable and set it to what shoud be printed out
//      and inside wait while loop in main code add if string != "" then System.out.print(string);
//      but in that case you should add lock to it.
//      System.out.print("Output: ERASER Games played; Number of developers caught");
//      System.out.print(", " + GameConstants.NUM_TESTING_GAMES_TO_PLAY + ", " + payoffs.getNumTimesCaught() + "\n");
        //Sleep(100); // well placed sleep
        }
    run--;
    }

[注释]

  • 来自您的代码我假设GameConstants是共享变量!!!
  • 如果只是为了阅读而不是好的
  • 但如果你也在线程内写信(我怀疑是的)
  • 然后你有一个很大的问题,因为你需要在游戏类中添加锁...然后......
  • 如果没有机器学习,那么你可以避免这个
  • 为每个线程创建单独的GameConstants变量,如... GameConstants [N]
  • 但你需要重写代码,以便访问GameConstants [ix]而不是GameConstants

[锁定]

  • 不知道如何在JAVA中实现锁
  • 但你也可以使用自己喜欢的东西

    class _lock
     {
    public:
     volatile bool locked;
     _lock() { locked=false; }
     void lock() { while(locked) Sleep(1); locked=true; }
     void unlock() { locked=false; }
     };
    
    // now for each shared variable (or group of variables) add one global _lock variable
    _lock l1; int sv1; // shared variable 1 and her lock
    
    // any write access and sometimes also read access needs lock
    l1.lock();
    sv1++;
    l1.unlock();
    
  • 请注意锁定有时会导致App冻结,尤其是在重负荷使用时。

  • 无论是自己的锁还是OS锁都无关紧要
  • 这主要发生在混合视觉内容或某些OS调用内部线程而非主线程中
  • 在这种情况下,如果可以
  • ,有时候放置良好的睡眠有助于避免线程内的OS调用
  • 因为它会导致很多其他问题...
  • 也尝试尽可能小的时间锁定,因为如果发生冲突,冲突的线程就会停止!!!
  • 因此,您不能只在循环开始时添加锁定并在结束时解锁
  • 因为并行性加速将会丢失

答案 2 :(得分:1)

声明队列以收集结果并将任务提交给线程池:

    final ArrayBloclingQueue<PlayerPayoffs> queue=new ArrayBloclingQueue<PlayerPayoffs>();
    Executor exec=new Executors.newFixedThreadPool(N); // number of threads depends on hardware
    for (int i = 0; i < GameConstants.MAX_GAMES; i++) {
        exec.execute(new Runnable(){          
            EraserSimulator eraser = new EraserSimulator(GameConstants.MAX_TARGETS, GameConstants.MAX_RESOURCES, GameConstants.NUM_ATTACKER_TYPES, seed.nextInt());
            Map<Set<SingleObjectiveTarget>, Double> gameStrategy = eraser.run();

            assert (gameStrategy != null);

            TestingGameSimulator testingGame = new TestingGameSimulator(GameConstants.MAX_TARGETS, gameStrategy, GameConstants.NUM_GAMES_TO_STORE_FOR_HISTORY, GameConstants.NUM_TESTING_GAMES_TO_PLAY);      
            PlayerPayoffs payoffs = testingGame.run(eraser.getEraserInstance());
            queue.put(payoffs);
        });
    }

然后收集并总结结果:

    double testerPayoffSum = 0.0, developerPayoffSum = 0.0;
    for (int i = 0; i < GameConstants.MAX_GAMES; i++) {
            PlayerPayoffs payoffs=queue.take();
            testerPayoffSum += payoffs.getAverageTesterPayoff(GameConstants.NUM_TESTING_GAMES_TO_PLAY);
            developerPayoffSum += payoffs.getAverageDeveloperPayoff(GameConstants.NUM_TESTING_GAMES_TO_PLAY);
            System.out.print("Output: ERASER Games played; Number of developers caught");
            System.out.print(", " + GameConstants.NUM_TESTING_GAMES_TO_PLAY + ", " + payoffs.getNumTimesCaught() + "\n");
    }