【问题标题】:High-priority threads in JavaJava中的高优先级线程
【发布时间】:2012-08-09 17:25:20
【问题描述】:

我目前正在研究分布式应用程序的性能。我的目标是一个网络组件。当前,对于每个连接,都有一个专用线程以阻塞模式处理套接字。我的目标是减少线程数(不降低性能)并尽可能提高性能。

我重新设计了网络组件以使用异步通信,并尝试使用 1 到 2 个线程进行整个网络处理。我做了一个简单的测试,我从一个节点循环写入并在另一个节点上读取,这是为了测试最大 nw 线程能力,我发现我的繁忙循环实现正在消耗 100% 的 cpu 并且每秒获得更多的操作,然后我们要求。所以我在现有的应用程序中集成了这个繁忙的循环实现。

我发现的问题是其他线程不允许这些异步 nw 线程获取完整的 cpu,即使我有一个 8 核系统并且我们使用的 cpu 不超过 400%。基本上,作为一名 C 程序员,我可以通过将我的 nw 线程绑定到一个核心并提高其调度优先级来解决这个问题,这样其他线程仍然可以在其他核心上运行。我无法在 Java 中做类似的事情。 Java 线程优先级存在冲突的 cmets。另外我不想降低其他线程的优先级,因为它可能有它自己的副作用。

你会如何解决这个问题?

【问题讨论】:

  • See this post,这是一种设置线程处理器亲和性的方法,当您使用 Java 时,虽然它实际上是使用 JNI 来完成这项工作。
  • 你想让两个线程处理所有的网络流量。最多会以 100% 使用两个内核。您正在 100% 运行四个内核,因此从这里的信息来看,您似乎没有问题。是什么让您认为自己有问题?
  • 抱歉没有明确说明。 nw 流量当前由 8 个线程处理,它们一起消耗的 CPU 不超过 100%。 400% 是整个应用程序的负载。我正在考虑通过使用单个(或 2 个)异步 nw 线程来减少线程数。

标签: java multithreading scheduling


【解决方案1】:

我有一个库来支持 Linux 和 Windows 上的 Java 线程关联。 https://github.com/peter-lawrey/Java-Thread-Affinity

如果您隔离 CPU,您可以确保您分配的 CPU 不会用于其他任何事情(除了不可屏蔽的中断)这在 Linux AFAIK 中效果最好。


如果您使用非阻塞 NIO 的忙等待而不是阻塞 IO,您可以获得更低的延迟结果。后者在负载下工作得最好,在较低负载下延迟会增加。

您可能会发现这个库很有趣https://github.com/peter-lawrey/Java-Chronicle 它允许您每秒保存数百万条消息,可选择保存到第二个进程。

顺便说一句:线程优先级只是一个提示,操作系统可以随意忽略它(并且经常这样做)


一个比较热代码和冷代码的简单示例。它所做的只是重复复制一个数组并对其计时。一旦代码和数据升温,您就不会期望它会变慢,但即使在一台安静的机器上,它也需要 10 毫秒的延迟,以显着减慢复制所需的时间。

public static void main(String... args) throws InterruptedException {
    int[] from = new int[60000], to = new int[60000];
    for (int i = 0; i < 10; i++)
        copy(from, to); // warm up
    for (int i = 0; i < 10; i++) {
        long start = System.nanoTime();
        copy(from, to);
        long time = System.nanoTime() - start;
        System.out.printf("Warm copy %,d us%n", time / 1000);
    }
    for (int i = 0; i < 10; i++) {
        Thread.sleep(10);
        long start = System.nanoTime();
        copy(from, to);
        long time = System.nanoTime() - start;
        System.out.printf("Cold copy %,d us%n", time / 1000);
    }
}

private static void copy(int[] a, int[] b) {
    for (int i = 0, len = a.length; i < len; i++)
        b[i] = a[i];
}

打印

Warm copy 20 us
Warm copy 20 us
Warm copy 19 us
Warm copy 23 us
Warm copy 20 us
Warm copy 20 us
Cold copy 100 us
Cold copy 80 us
Cold copy 89 us
Cold copy 92 us
Cold copy 80 us
Cold copy 112 us

【讨论】:

  • 我可以问一下吗?那么有一种方法可以使用 Java 分配一个 CPU 来只做你的工作吗?
  • 在 Linux 上,您可以告诉操作系统不要在 CPU 上调度任何内容,然后您可以将特定线程(或进程)分配给该 CPU 它是间接的,但它工作得非常好.我对 Windows 或 MAC OS 没有相同的控件
  • 这太棒了,我不知道这件事,所以急于测试你写的库。谢谢!
  • 顺便说一句,如果您启用了超线程但不希望一个或两个关键线程与另一个线程共享核心,它支持绑定/保留整个核心。
  • 谢谢,我会试试的。你能详细说明第二部分吗
【解决方案2】:

这对我来说确实有点过早的优化。你有一个 8 核系统,只使用 400% 的 CPU。是什么让您认为这不是 IO 绑定程序的教科书示例?是什么让您认为您的网络 IO 链没有达到极限?

@Peter 知道他的东西,我相信您可以破解处理器关联性并将您的关键线程强制到单个 CPU,但问题是它会让您的程序运行得更快吗?我真诚地怀疑它。 Java VM 模型在线程调度方面非常聪明,我建议它正在适当地完成它的工作。除非你有很好的相反证据,否则我会让它处理日程安排。如果大多数线程都在等待 IO,那么即使是优先级也没什么意义。

另外,是什么让您认为减少线程数会更好。这将大量代码从本机领域(即线程多路复用)转移到 Java 领域(即 NIO 代码)。如果您谈论的是 1000 个线程,那么我同意,但即使是 100 个线程也应该是处理连接的有效方法。

二十多年来,我进行了大量的线程编程,但我从来没有强制要求线程亲和性。当然,确定线程池的大小并正确决定在何处应用线程池与专用线程是一门艺术,但强制 VM 以您认为应该的方式调度线程并不是很好地利用您的时间。花一些时间与分析器一起找出您的程序将时间花在哪里将是一个更好的投资恕我直言。

【讨论】:

  • 在我的实验中,我只有 1 个异步网络线程在所有端点上进行繁忙循环。我期待这个线程将抢占 100% cpu 。当我单独运行此测试时就是这种情况,但是当我打开其他功能并因此打开更多线程时,情况就大不相同了。似乎 linux 调度程序不允许这个线程被调度为 100%,即使在 8 核系统上负载永远不会超过 400%。
  • 您正在对所有端点进行繁忙循环?哇。似乎是一个非常有问题的架构。每个端点一个线程真的站不住脚吗?有多少个端点?如果您不使用 NIO 来替换每个线程的端点,那么我无法相信您的方式会更好。但是不知道细节很难知道@user1588261。
  • @Gray 想知道您是否认为这是过早的优化,即使对于在 NUMA 架构上运行的应用程序,具有 DB 拉管道执行模型(read_From_Cache-> 解压缩 -> 解码 -> build_hashmap -> 加入-> ....)并且我想在每个处理阶段使用一个线程,至少将所有线程保持在一个 Numa 区域内不是更有意义吗?谢谢!
  • 我对 NUMA @SlimBouguerra 的细节了解不多。任何稍微松散耦合的东西(即不在同一个硅片内)都必须为协调付出代价,并且需要将其纳入线程成本/收益分析中。如果你有一个 CPU 绑定的进程,那么很好,但你的工作与 IO 相关的越多,或者线程需要越多的交互来完成工作,当你线程化时你看到显着优化提升的可能性就越小你的申请。希望这会有所帮助。
猜你喜欢
  • 1970-01-01
  • 2014-11-26
  • 1970-01-01
  • 2010-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
  • 1970-01-01
相关资源
最近更新 更多