2

我有一个 Clojure 网络应用程序,基本结构是这样的:

  • 服务器有一个 LinkedBlockingQueue 或 ArrayBlockingQueue(我都试过了)
  • 多个线程接受网络连接,并offer工作到队列
  • 队列中的一个线程take在无限循环中处理所取的每个项目

我注意到take调用的严重性能问题:

  • 线程offer以非常快的速度进入队列,队列很快将它们全部带走
  • 队列中的一个工作线程take以非常慢的速度(比速度慢 200 多倍offer
  • CPU 使用率非常低 - 所以工作人员根本不忙

在不使用队列的情况下,在基准测试情况下,相同的工作负载能够最大化 CPU 使用率并以令人满意的速度完成。

那么在这种情况下使用的最佳排队技术是什么?

这是我的代码(少于 100 行);

https://github.com/HouzuoGuo/Aurinko/blob/master/src/Aurinko/core.clj

编辑,我的观察细节:

  • 我对请求处理速度进行了基准测试,它在不使用队列的情况下每秒处理大约 8,000 个请求。
  • 我让服务器程序在排队请求时打印一条调试消息,并在完成处理请求时打印另一条消息。
  • 我制作了一个简单的客户端程序,每秒向服务器发送大约 1,000 个请求。
  • 服务器及时将所有请求排队,队列变成数千个元素。
  • 根据调试消息,Worker(请求处理器)似乎每秒仅处理大约 150 个请求。

编辑:

感谢大家的帮助。我已经确认阻塞队列不是导致性能问题的原因。虽然我没有发现我的应用程序中的性能瓶颈,但一定有一个地方。

最终编辑:

谢谢大家。性能瓶颈是由网络 IO 而不是阻塞队列引起的。

4

3 回答 3

2

您所看到的最可能的解释是队列为空导致take()等待。如果队列不为空,它可能会非常快。

我假设在 clojure 中的性能类似于 Java 中的性能。

public static void main(String... args) throws InterruptedException {
    int runs = 20000;
    BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(runs + 1);
    BlockingQueue<Integer> queue2 = new LinkedBlockingQueue<Integer>(runs + 1);
    for (int i = 0; i < 10; i++) {
        testQueue(runs, queue);
        testTake(runs, queue);
        testQueue(runs, queue2);
        testTake(runs, queue2);
    }
}

private static void testQueue(int runs, BlockingQueue<Integer> queue) {
    long start = System.nanoTime();
    for (int i = 0; i < runs; i++)
        queue.offer(1);
    long time = System.nanoTime() - start;
    System.out.printf(queue.getClass().getSimpleName() + ": Average time to offer was %,d ns%n", time / runs);
}

private static void testTake(int runs, BlockingQueue<Integer> queue) throws InterruptedException {
    long start = System.nanoTime();
    for (int i = 0; i < runs; i++)
        queue.take();
    long time = System.nanoTime() - start;
    System.out.printf(queue.getClass().getSimpleName() + ": Average time to take was %,d ns%n", time / runs);
}

最后打印

ArrayBlockingQueue: Average time to offer was 34 ns
ArrayBlockingQueue: Average time to take was 39 ns
LinkedBlockingQueue: Average time to offer was 78 ns
LinkedBlockingQueue: Average time to take was 54 ns
于 2012-09-21T07:33:53.410 回答
2

您声明:“CPU 使用率非常低 - 所以工作人员根本不忙”。您还说:“我已经确认阻塞队列不是导致性能问题的原因。虽然我没有发现我的应用程序中的性能瓶颈,但一定有一个地方存在。”

如果这两个陈述都是正确的,则可能是您的工作线程花费大量时间等待 I/O。如果是这样,有一个简单的解决方案:运行多个工作线程!

或者可能存在其他一些并发瓶颈(不是工作队列)。

你为什么不做以下事情:制作一个小测试程序,将大约 1,000 个项目推送到工作队列中,然后开始运行在工作线程上运行的相同代码。当队列为空时,它应该退出。分析该程序。(你的开发机器上设置了分析器吗?我喜欢使用 JIP。)

于 2012-09-24T12:42:07.423 回答
0

你确定 LinkedBlocingQueue 和 ArrayBlockingQueue 的结果是一样的吗?基于这两种数据结构的不同,从队列中提供和获取元素的效率是不同的。

于 2012-09-21T07:28:54.853 回答