如何确定高延迟网络请求的最佳线程数?

2022-09-04 08:14:47

我正在编写一个实用程序,该实用程序必须发出数千个网络请求。每个请求仅收到一个小数据包作为响应(类似于 ping),但可能需要几秒钟才能完成。处理每个响应在一行(简单)代码中完成。

这样做的最终效果是,计算机不受 IO 限制、文件系统绑定或 CPU 绑定,它仅受响应延迟的约束。

这与有一种方法可以确定理想的线程数?和Java确定最佳线程数的最佳方法[重复]类似,但不相同。主要区别在于我只受延迟的约束。

我正在使用一个对象来运行线程,并使用一个对象来跟踪需要检索结果的线程:ExecutorServiceQueue<Future<Integer>>

ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
Queue<Future<Integer>> futures = new LinkedList<Future<Integer>>();

for (int quad3 = 0 ; quad3 < 256 ; ++quad3) {
    for (int quad4 = 0 ; quad4 < 256 ; ++quad4) {
        byte[] quads = { quad1, quad2, (byte)quad3, (byte)quad4 };
        futures.add(executorService.submit(new RetrieverCallable(quads)));
    }
}

...然后,我将队列中的所有元素排出队列,并将结果放在所需的数据结构中:

int[] result = int[65536]
while(!futures.isEmpty()) {
    try {
        results[i] = futures.remove().get();
    } catch (Exception e) {
        addresses[i] = -1;
    }
}

我的第一个问题是:这是跟踪所有线程的合理方法吗?如果线程 X 需要一段时间才能完成,则许多其他线程可能会在 X 完成之前完成。线程池是否会在等待空插槽时耗尽自身,或者对象是否会以这样一种方式管理池,即已完成但尚未处理的线程将移出可用插槽,以便其他线程开始?ExecutorService

我的第二个问题是,我可以使用什么准则来查找进行这些调用的最佳线程数?我甚至不知道这里的数量级指导。我知道它与256个线程一起工作得很好,但对于1024个线程,它似乎花费了大致相同的总时间。CPU利用率徘徊在5%左右,因此这似乎不是问题。有这么多的线程,我应该查看哪些指标来比较不同的数字?显然,处理批处理的总时间,每个线程的平均时间...还有什么?内存在这里是一个问题吗?


答案 1

它会让您感到震惊,但您不需要任何线程进行I / O(从数量上讲,这意味着0个线程)。您已经研究过多线程不会增加网络带宽,这很好。现在,是时候知道线程进行计算了。他们没有进行(高延迟)通信。通信由网络适配器执行,这是另一个进程,与CPU并行运行。分配一个线程(看看分配的资源是由这位声称你需要1个线程的绅士列出的只是为了睡觉,直到网络适配器完成其工作是愚蠢的。I/O 不需要线程 = 需要 0 个线程。

分配用于计算的线程以与 I/O 请求并行进行是有意义的。线程数将取决于计算与通信的比率,并受 CPU 中内核数量的限制

对不起,我不得不说,尽管你肯定暗示了阻止I / O的承诺,但很多人并不了解这个基本的东西。接受建议,使用异步 I/O,您会发现问题不存在。


答案 2

正如你提到的一个链接答案中提到的,Brian Goetz在他的文章中很好地涵盖了这一点。

他似乎暗示,在你的情况下,建议你在承诺线程计数之前收集指标。

调整池大小

调整线程池的大小很大程度上是为了避免两个错误:线程太少或线程太多。...

线程池的最佳大小取决于可用处理器的数量以及工作队列上任务的性质。...

对于可能等待 I/O 完成的任务(例如,从套接字读取 HTTP 请求的任务),您需要将池大小增加到可用处理器数之外,因为并非所有线程都始终正常工作。使用分析,可以估计典型请求的等待时间 (WT) 与服务时间 (ST) 的比率。如果我们将此比率称为WT/ST,对于N处理器系统,您将需要大约N*(1 + WT / ST)线程以保持处理器充分利用。

我的强调。