Java 并行计算开销

Java 并行计算开销,java,multithreading,compiler-construction,parallel-processing,Java,Multithreading,Compiler Construction,Parallel Processing,我使用以下代码作为编译器阶段类的一部分。该方法由编译器的主方法调用(并进行基准测试) 并行编译阶段: private Consumer<ICompilationUnit> apply; // ... @Override public void apply(Collection<ICompilationUnit> units) { this.count = units.size(); for (ICompilationUnit unit : units)

我使用以下代码作为
编译器阶段
类的一部分。该方法由编译器的主方法调用(并进行基准测试)

并行编译阶段:

private Consumer<ICompilationUnit> apply;
// ...

@Override
public void apply(Collection<ICompilationUnit> units)
{
    this.count = units.size();
    for (ICompilationUnit unit : units)
    {
        new Thread()
        {
            @Override
            public void run()
            {
                ParallelCompilerPhase.this.apply.accept(unit);
                ParallelCompilerPhase.this.count--;
            }
        }.start();
    }

    long now = System.currentTimeMillis();
    while (this.count > 0)
    {
        long l = System.currentTimeMillis() - now;
        if (l >= 1000L)
        {
            DyvilCompiler.logger.warning(this.name + " is taking too long! " + l + " ms");
            try
            {
                Thread.sleep(1000L);
            }
            catch (InterruptedException ex)
            {
                ex.printStackTrace();
            }
        }
    }
}
private Consumer<Collection<ICompilationUnit>> apply;
//...

@Override
public void apply(Collection<ICompilationUnit> units)
{
    this.apply.accept(units);
}
但是,如果我将
ParallelCompilerPhase
的实现更改为:

@Override
public void apply(Collection<ICompilationUnit> units)
{
    for (ICompilationUnit unit : units)
    {
        this.apply.accept(unit);
    }
}
是什么导致2000毫秒的开销


作为一种可能的修复方法,将使用

units.parallelStream().forEach(this.apply);

使用线程方法执行我最初希望执行的操作?

您正在为每个工作单元启动一个新线程。这是个糟糕的主意。对于任何受计算限制的任务(一个不会花费大部分时间阻塞等待IO的任务),没有任何理由拥有比CPU核更多的线程。超过这个阈值只会浪费时间在线程之间切换上下文(并浪费大量时间旋转和拆卸线程,这并不便宜)。无论有多少线程在运行,处理器实际上不能同时完成比它拥有的处理器资源更多的事情


相反,您应该考虑使用来为您管理,只需让工作线程从队列中弹出工作单元并执行它们即可

  • 将创建新线程
  • 主线程一直忙于检查
    这个。计数
    和时间,因此新线程不会运行
  • 1000毫秒后,打印消息,主线休眠1000毫秒
  • 其他线程执行并完成
  • 主线醒了,2005.1ms过去了
问题在于忙循环。
尝试:

@覆盖
申请公开作废(收集单位)
{
this.count=units.size();
用于(ICompilationUnit单位:单位)
{
新线程()
{
@凌驾
公开募捐
{
ParallelCompilerPhase.this.apply.accept(单位);
ParallelCompilerPhase.this.count--;
}
}.start();
}
long now=System.currentTimeMillis();
而(this.count>0)
{
long l=System.currentTimeMillis()-现在;
如果(l>=1000L)
{
DyvilCompiler.logger.warning(this.name+“花费的时间太长了!”+l+“ms”);
尝试
{
睡眠(1000L);
}
捕获(中断异常例外)
{
例如printStackTrace();
}
}
尝试
{
睡眠(10L);
}
捕获(中断异常例外)
{
例如printStackTrace();
}
}
}

但是,等待线程的最佳方式是使用@rici建议的
Thread.join()
,因为这是执行此操作的“官方方式”,不会造成处理器时间的浪费。在上面的解决方案中,主线程在worker完成后等待额外的时间,最长为10ms,使用
join()
当工作人员完成时,主线程会立即唤醒。

对于并行程序员来说,疯狂的定义是反复做同样的事情,并期望得到相同的结果:请注意,我每次至少有2000毫秒的开销(在Eclipse中运行时),而40-60毫秒的测量值在最后一次是准确的[插入荒谬的数字]在使用
ParallelCompilerPhase
之前进行测试…在不知道apply在做什么的情况下很难判断…但是如果这是一个很短的任务,并且您有数千个测试,那么启动新线程的开销可能高于并行的好处。。。
[2015-03-04 23:21:36] [INFO]: Dyvil Compiler 1.0.0 for Dyvil 1.0.0

[2015-03-04 23:21:36] [INFO]: Loaded 2 Libraries (245.6 ms, 122.8 ms/L, 8.14 L/s)
[2015-03-04 23:21:36] [INFO]: Compiling 'src/test' to 'dbin'
[2015-03-04 23:21:36] [INFO]: Applying 8 States: [TOKENIZE, PARSE, RESOLVE_TYPES, RESOLVE, CHECK, PRINT, COMPILE, TEST]
[2015-03-04 23:21:36] [INFO]: Compiling 2 Packages, 2 Files (1 Compilation Unit)

[2015-03-04 23:21:36] [INFO]: Applying State TOKENIZE
[2015-03-04 23:21:36] [INFO]: Finished State TOKENIZE (0.6 ms, 0.6 ms/CU, 1721.17 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State PARSE
[2015-03-04 23:21:36] [INFO]: Finished State PARSE (20.6 ms, 20.6 ms/CU, 48.59 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State RESOLVE_TYPES
[2015-03-04 23:21:36] [INFO]: Finished State RESOLVE_TYPES (8.5 ms, 8.5 ms/CU, 117.34 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State RESOLVE
[2015-03-04 23:21:36] [INFO]: Finished State RESOLVE (15.9 ms, 15.9 ms/CU, 63.07 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State CHECK
[2015-03-04 23:21:36] [INFO]: Finished State CHECK (0.2 ms, 0.2 ms/CU, 4587.16 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State PRINT
[2015-03-04 23:21:36] [INFO]: src/test/dyvil/test/Main.dyvil:
// ...
[2015-03-04 23:21:36] [INFO]: Finished State PRINT (2.1 ms, 2.1 ms/CU, 479.39 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State COMPILE
[2015-03-04 23:21:36] [INFO]: Finished State COMPILE (4.0 ms, 4.0 ms/CU, 251.76 CU/s)
[2015-03-04 23:21:36] [INFO]: Applying State TEST
[2015-03-04 23:21:36] [INFO]: Finished State TEST (0.6 ms, 0.6 ms/CU, 1686.34 CU/s)

[2015-03-04 23:21:36] [INFO]: Compilation finished (57.5 ms, 57.5 ms/CU, 17.40 CU/s)
// ...
[2015-03-04 23:21:36] [INFO]: Test completed without Errors (2 ms)
units.parallelStream().forEach(this.apply);
@Override
public void apply(Collection<ICompilationUnit> units)
{
    this.count = units.size();
    for (ICompilationUnit unit : units)
    {
        new Thread()
        {
            @Override
            public void run()
            {
                ParallelCompilerPhase.this.apply.accept(unit);
                ParallelCompilerPhase.this.count--;
            }
        }.start();
    }

    long now = System.currentTimeMillis();
    while (this.count > 0)
    {
        long l = System.currentTimeMillis() - now;
        if (l >= 1000L)
        {
            DyvilCompiler.logger.warning(this.name + " is taking too long! " + l + " ms");
            try
            {
                Thread.sleep(1000L);
            }
            catch (InterruptedException ex)
            {
                ex.printStackTrace();
            }
        }
        try
        {
            Thread.sleep(10L);
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
    }
}