当前位置: 首页 > 知识库问答 >
问题:

多线程环境中的基准点

陆建木
2023-03-14

我在学习多线程时发现对象的速度变慢了。hashCode在多线程环境中,因为对于相同数量的对象,计算运行4个线程的默认哈希代码要比运行1个线程的默认哈希代码花费两倍的时间。

但根据我的理解,并行完成这项工作也需要类似的时间。

您可以更改线程数。每个线程都有相同的工作量,因此您希望在我的四核机器上运行4个线程可能需要与运行单个线程相同的时间。

我看到4x大约2.3秒,但是。1x为9秒。

我的理解是否有差距,请帮助我理解这种行为。

public class ObjectHashCodePerformance {

private static final int THREAD_COUNT = 4;
private static final int ITERATIONS = 20000000;

public static void main(final String[] args) throws Exception {
    long start = System.currentTimeMillis();
    new ObjectHashCodePerformance().run();
    System.err.println(System.currentTimeMillis() - start);
 }

private final ExecutorService _sevice =   Executors.newFixedThreadPool(THREAD_COUNT,
        new ThreadFactory() {
            private final ThreadFactory _delegate =   Executors.defaultThreadFactory();

            @Override
            public Thread newThread(final Runnable r) {
                Thread thread = _delegate.newThread(r);
                thread.setDaemon(true);
                return thread;
            }
        });

    private void run() throws Exception {
    Callable<Void> work = new java.util.concurrent.Callable<Void>() {
        @Override
        public Void call() throws Exception {
            for (int i = 0; i < ITERATIONS; i++) {
                Object object = new Object();
                object.hashCode();
            }
            return null;
        }
    };
    @SuppressWarnings("unchecked")
    Callable<Void>[] allWork = new Callable[THREAD_COUNT];
    Arrays.fill(allWork, work);
    List<Future<Void>> futures = _sevice.invokeAll(Arrays.asList(allWork));
    for (Future<Void> future : futures) {
        future.get();
    }
 }

 }

对于线程数4,输出为

~2.3 seconds

对于线程计数1输出为

~.9 seconds

共有3个答案

左丘弘致
2023-03-14

我看到代码的两个问题:

  1. allWork[]数组的大小等于迭代。
  2. 迭代时,在call()方法中,确保每个线程都获得其负载份额。迭代/THREAD_COUNT。

以下是您可以尝试的修改版本:

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;

 public class ObjectHashCodePerformance {

private static final int THREAD_COUNT = 1;
private static final int ITERATIONS = 20000;
private final Object object = new Object();

public static void main(final String[] args) throws Exception {
    long start = System.currentTimeMillis();
    new ObjectHashCodePerformance().run();
    System.err.println(System.currentTimeMillis() - start);
 }

private final ExecutorService _sevice =   Executors.newFixedThreadPool(THREAD_COUNT,
        new ThreadFactory() {
            private final ThreadFactory _delegate =   Executors.defaultThreadFactory();

            @Override
            public Thread newThread(final Runnable r) {
                Thread thread = _delegate.newThread(r);
                thread.setDaemon(true);
                return thread;
            }
        });

    private void run() throws Exception {
    Callable<Void> work = new java.util.concurrent.Callable<Void>() {
        @Override
        public Void call() throws Exception {
            for (int i = 0; i < ITERATIONS/THREAD_COUNT; i++) {
                object.hashCode();
            }
            return null;
        }
    };
    @SuppressWarnings("unchecked")
    Callable<Void>[] allWork = new Callable[ITERATIONS];
    Arrays.fill(allWork, work);
    List<Future<Void>> futures = _sevice.invokeAll(Arrays.asList(allWork));
    System.out.println("Futures size : " + futures.size());
    for (Future<Void> future : futures) {
        future.get();
    }
 }

 }
锺超英
2023-03-14

见帕拉米诺的评论:

您不是在测量hashCode(),而是在单线程时测量2000万对象的实例化,在运行4个线程时测量8000万对象。将新的Object()逻辑移出Callable中的for循环,然后您将测量hashCode()-帕拉米诺

卫皓
2023-03-14

我创建了一个简单的JMH基准来测试各种情况:

@Fork(1)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
public class HashCodeBenchmark {
    private final Object object = new Object();

    @Benchmark
    @Threads(1)
    public void singleThread(Blackhole blackhole){
        blackhole.consume(object.hashCode());
    }

    @Benchmark
    @Threads(2)
    public void twoThreads(Blackhole blackhole){
        blackhole.consume(object.hashCode());
    }

    @Benchmark
    @Threads(4)
    public void fourThreads(Blackhole blackhole){
        blackhole.consume(object.hashCode());
    }

    @Benchmark
    @Threads(8)
    public void eightThreads(Blackhole blackhole){
        blackhole.consume(object.hashCode());
    }
}

结果如下:

Benchmark                       Mode  Cnt  Score   Error  Units
HashCodeBenchmark.eightThreads  avgt   10  5.710 ± 0.087  ns/op
HashCodeBenchmark.fourThreads   avgt   10  3.603 ± 0.169  ns/op
HashCodeBenchmark.singleThread  avgt   10  3.063 ± 0.011  ns/op
HashCodeBenchmark.twoThreads    avgt   10  3.067 ± 0.034  ns/op

所以我们可以看到,只要线程不比内核多,每个哈希码的时间就保持不变。

PS:正如@Tom Cools评论的那样——您在测试中测量的是分配速度,而不是hashCode()速度。

 类似资料:
  • 我的 Web 应用程序中出现随机错误,我迷路了。我创建了一个库来解码代码。我尝试了很多,从未失败过测试。但突然间,它开始随机失败。由于它在单线程测试中运行良好,有时在 servlet 环境中失败时,我能想象的唯一解释是问题与多线程环境中使用的库有关。老实说,我知道多线程是一个非常复杂的问题。我担心我的库可能不是线程安全的。顺便说一下,它非常简单,它是一个具有几种静态方法的正面类。基本上,假设您正在

  • 现在,他提出的下一个问题是为多线程环境编写单例类。然后,我写了双重检查单例类。 然后,他反对使用和双重检查,并说这是没用的。为什么要检查两次,为什么要使用synchronized?我试着用多种方案说服他。但是,他没有。 后来,我在家里尝试了下面的代码,在那里我使用了带有多个线程的简单的单例类。 那么,问题是,在多线程环境中是否有必要使用或/和双重检查方法?似乎我的第一个代码本身(没有添加任何额外的

  • 如果我有多个线程,每个线程使用injector获取EntityManager对象,每个线程使用em对象选择其他类对象的列表。准备好在for循环中使用。 如果一个线程首先完成并调用clear(),这会影响其他线程吗?比如for循环会有异常? 谢谢你。

  • 问题内容: 我正在尝试在多线程环境中对int变量进行经典的递增/递减。这是我的示例代码。 尽管我对所有三个线程都使用了join()方法,但结果仍然不一致。在这里加入不是意味着主线程要等到每个线程都完成执行吗?我什至尝试将同步添加到三个方法签名中的每个;但是我得到不一致的结果。 除了使用该变量的Atomic版本外,我还能如何确保始终获得0? 问题答案: 您的SyncCounter根本不是线程安全的。

  • 我目前正在学习多线程,我发现了一些我无法解释的有趣的东西。据我所知,如果两个线程访问一个静态变量,它们可以将自己的副本复制到缓存中。Thread1对其本地缓存中的静态变量进行的更新不会反映在Thread2缓存的静态变量中。 java java

  • 我想编写一个spring boot批处理应用程序,其中我有一个充满事件的数据库表。我想做的是有一个多线程的spring boot批处理应用程序,它将以这种方式工作: 我想有5个线程运行,每个线程将保留一个偏移量来跟踪它读取的事件,以便没有其他线程再次读取相同的事件。我想怎么做: 所以我希望能够在数据库表中为每个线程保留偏移量。有没有办法让Spring Boot环境以这种方式工作?