Java 哪个更快?在更多可运行程序中减少工作量,还是在更少可运行程序中增加工作量?(执行人服务)
我正试图找出如何从多线程应用程序中获得最大性能。Java 哪个更快?在更多可运行程序中减少工作量,还是在更少可运行程序中增加工作量?(执行人服务),java,multithreading,performance,concurrency,threadpool,Java,Multithreading,Performance,Concurrency,Threadpool,我正试图找出如何从多线程应用程序中获得最大性能。 我创建了一个线程池,如下所示: ExecutorService executor = Executors.newFixedThreadPool(8); // I have 8 CPU cores. 我的问题是,我应该只将工作划分为8个可运行/可调用项(与线程池中的线程数相同),还是应该将其划分为1000000个可运行/可调用项 for (int i = 0; i < 1000000; i++) { Callable<L
我创建了一个线程池,如下所示:
ExecutorService executor = Executors.newFixedThreadPool(8); // I have 8 CPU cores.
我的问题是,我应该只将工作划分为8个可运行/可调用项(与线程池中的线程数相同),还是应该将其划分为1000000个可运行/可调用项
for (int i = 0; i < 1000000; i++)
{
Callable<Long> worker = new MyCallable(); // Each worker does little work.
Future<Long> submit = executor.submit(worker);
}
long sum = 0;
for (Future<Long> future : list)
sum += future.get(); // Much more overhead from the for loops
for(int i=0;i<1000000;i++)
{
Callable worker=new MyCallable();//每个worker只做很少的工作。
未来提交=执行人提交(工人);
}
长和=0;
用于(未来:列表)
sum+=future.get();//for循环的开销要大得多
或
for (int i = 0; i < 8; i++)
{
Callable<Long> worker = new MyCallable(); // Each worker does much more work.
Future<Long> submit = executor.submit(worker);
}
long sum = 0;
for (Future<Long> future : list)
sum += future.get(); // Negligible overhead from the for loops
for(int i=0;i<8;i++)
{
Callable worker=new MyCallable();//每个worker都要做更多的工作。
未来提交=执行人提交(工人);
}
长和=0;
用于(未来:列表)
sum+=future.get();//for循环的开销可以忽略不计
划分成1000000个可调用项对我来说似乎比较慢,因为实例化所有这些可调用项并在for循环中从中收集结果会带来开销。另一方面,如果我有8个可调用项,这个开销可以忽略不计。由于我只有8个线程,我不能同时运行1000000个可调用线程,因此没有性能提升
我是对还是错
顺便说一句,我可以测试这些情况,但操作非常简单,我猜编译器会意识到这一点并进行一些优化。因此,结果可能具有误导性。我想知道哪种方法更适合像图像处理应用程序这样的应用程序。这个问题没有直接的答案,因为它取决于很多因素,如您的代码、应用程序loigc、最大值、可能的并发性、硬件等
但是考虑并发时,你应该考虑下面的事情,
总而言之,您的第二种方法对我来说很好,但如果可能的话,可以找到更多的并行性,您可以将其扩展到20-30。也许这段代码有帮助。它将使用fork-join池计算斐波那契数。使用fork-join,我们可以递归地细分一个问题,并组合每个递归级别的结果。从理论上讲,我们可以在fork-join池中递归到fib(0),但这将是低效的。因此,我们引入了一个递归限制,在这里我们停止细分任务,并计算当前任务中的其余部分。此代码将记录fib(x)所用的时间,并计算n到x的每个fib(n)的单线程时间。对于每个递归限制,它将测量创建了多少任务以及每个任务平均运行了多长时间 通常,最佳点是任务大小大于1µs,但是我们这里的简单斐波那契任务几乎不需要内存/缓存。对于具有更高缓存污染的数据密集型任务,交换机成本更高,并发任务可能会污染共享缓存
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class FibonacciFork extends RecursiveTask<Long> {
private static final long serialVersionUID = 1L;
public FibonacciFork( long n) {
super();
this.n = n;
}
static ForkJoinPool fjp = new ForkJoinPool( Runtime.getRuntime().availableProcessors());
static long fibonacci0( long n) {
if ( n < 2) {
return n;
}
return fibonacci0( n - 1) + fibonacci0( n - 2);
}
static int rekLimit = 8;
private static long stealCount;
long n;
private long forkCount;
private static AtomicLong forks = new AtomicLong( 0);
static class Result {
long durMS;
int rekLimit;
}
public static void main( String[] args) {
int fiboArg = 49;
BenchLogger.sysinfo( "Warmup");
long singleNS[] = getSingleThreadNanos( 20, 5e9);
BenchLogger.sysinfo( "Warmup complete");
singleNS = getSingleThreadNanos( fiboArg, 1e9);
BenchLogger.sysinfo( "Single Thread Times complete");
Result[] results = new Result[ fiboArg + 1];
for ( int rekLimit = 2; rekLimit <= fiboArg; rekLimit++) {
results[ rekLimit] = new Result();
runWithRecursionLimit( rekLimit, fiboArg, singleNS[ rekLimit], results[ rekLimit]);
}
System.out.println( "CSV results for Fibo " + fiboArg + "\n" + "RekLimit\t" + "Jobs ns\t" + "time ms");
for ( int rekLimit = 2; rekLimit <= fiboArg; rekLimit++) {
System.out.println( rekLimit + "\t" + singleNS[ rekLimit] + "\t" + results[ rekLimit].durMS);
}
}
private static long[] getSingleThreadNanos( final int n, final double minRuntimeNS) {
final long timesNS[] = new long[ n + 1];
ExecutorService es = Executors.newFixedThreadPool( Math.max( 1, Runtime.getRuntime().availableProcessors() / 8));
for ( int i = 2; i <= n; i++) {
final int arg = i;
Runnable runner = new Runnable() {
@Override
public void run() {
long start = System.nanoTime();
long result = fibonacci0( arg);
long end = System.nanoTime();
double durNS = end - start;
long ntimes = 1;
double fact = 1;
while ( durNS < minRuntimeNS) {
long oldNTimes = ntimes;
if ( durNS > 0) {
ntimes = Math.max( 1, ( long) ( oldNTimes * fact * minRuntimeNS / durNS));
} else {
ntimes *= 2;
}
start = System.nanoTime();
for ( long i = 0; i < ntimes; i++) {
result = fibonacci0( arg);
}
end = System.nanoTime();
durNS = end - start;
fact *= 1.1;
}
timesNS[ arg] = ( long) ( durNS / ntimes);
System.out.println( "Single Fib(" + arg + ")=" + result + " in " + ( timesNS[ arg] / 1e6) + "ms (" + ntimes + " loops in " + (durNS / 1e6)
+ " ms)");
}
};
es.execute( runner);
}
es.shutdown();
try {
es.awaitTermination( 1, TimeUnit.HOURS);
} catch ( InterruptedException e) {
BenchLogger.sysinfo( "Single Timeout");
}
return timesNS;
}
private static void runWithRecursionLimit( int r, int arg, long singleThreadNanos, Result result) {
rekLimit = r;
long start = System.currentTimeMillis();
long fiboResult = fibonacci( arg);
long end = System.currentTimeMillis();
// Steals zählen
long currentSteals = fjp.getStealCount();
long newSteals = currentSteals - stealCount;
stealCount = currentSteals;
long forksCount = forks.getAndSet( 0);
final long durMS = end-start;
System.out.println( "Fib(" + arg + ")=" + fiboResult + " in " + durMS + "ms, recursion limit: " + r +
" at " + ( singleThreadNanos / 1e6) + "ms, steals: " + newSteals + " forks " + forksCount);
result.durMS = durMS;
result.rekLimit = r;
}
static long fibonacci( final long arg) {
FibonacciFork task = new FibonacciFork( arg);
long result = fjp.invoke( task);
forks.set( task.forkCount);
return result;
}
@Override
protected Long compute() {
if ( n <= rekLimit) {
return fibonacci0( n);
}
FibonacciFork ff1 = new FibonacciFork( n-1);
FibonacciFork ff2 = new FibonacciFork( n-2);
ff1.fork();
long r2 = ff2.compute();
long r1 = ff1.join();
forkCount = ff2.forkCount + ff1.forkCount + 1;
return r1 + r2;
}
}
import java.util.concurrent.*;
导入java.util.concurrent.atomic.*;
公共类FibonacciFork扩展递归任务{
私有静态最终长serialVersionUID=1L;
公共腓肠神经分叉(长n){
超级();
这个,n=n;
}
静态ForkJoinPool fjp=新的ForkJoinPool(Runtime.getRuntime().availableProcessors());
静态长光纤通道0(长n){
if(n<2){
返回n;
}
返回fibonacci0(n-1)+fibonacci0(n-2);
}
静态int-rekLimit=8;
私人静态长时间抢购;
长n;
私人长叉计数;
私有静态原子长分叉=新原子长(0);
静态类结果{
长杜姆斯;
int rekLimit;
}
公共静态void main(字符串[]args){
int-fiboArg=49;
BenchLogger.sysinfo(“预热”);
long singleNS[]=getSingleThreadNanos(20,5e9);
BenchLogger.sysinfo(“预热完成”);
singleNS=getSingleThreadNanos(fiboArg,1e9);
sysinfo(“单线程完成时间”);
结果[]结果=新结果[fiboArg+1];
对于(int-rekLimit=2;rekLimit,这个问题有两个方面
首先是Java技术方面的知识。由于您对此有一些答案,我将总结以下基本知识:
- 如果您有N个内核,那么N个线程将为您提供最佳结果,只要每个任务仅限于CPU(即不涉及i/O)
- 每个
线程
所做的工作应该比任务所需的工作要多,即N个线程计数到10会慢得多,因为创建和管理额外线程
的开销比并行计数到10的好处要高
- 您需要确保任何同步开销都低于正在完成的工作,即使用N个
线程
调用同步
增量方法会慢得多
线程
确实会占用资源,最常见的是内存。线程越多,就越难估计内存使用情况,可能会影响GC计时(很少,但我见过这种情况发生)
其次,你有调度理论。你需要考虑你的程序在做什么?
- 通常使用
线程