gpt4 book ai didi

java - 多线程总是比单线程产生更好的性能吗?

转载 作者:搜寻专家 更新时间:2023-10-30 21:01:08 24 4
gpt4 key购买 nike

我知道答案是,这里有一个例子Why single thread is faster than multithreading in Java? .

因此当在一个线程中处理一个任务是微不足道的时候,创建一个线程的成本将比分配任务产生更多的开销。这是单线程比多线程更快的一种情况。

问题

  • 单线程比多线程快的情况更多吗?​​

  • 我们什么时候应该决定放弃多线程,只使用单线程来实现我们的目标?

尽管问题被标记为 ,也欢迎讨论超越Java。如果我们能在答案中有一个小例子来解释,那就太好了。

最佳答案

这是一个很好的问题,涉及线程及其与实际工作的联系,即可用的物理 CPU 及其内核和超线程。

  1. 如果您的 CPU 有多个可用核心,多线程可能允许您并行执行操作。所以在理想的世界中,例如如果您的 CPU 有 4 个可用内核并且您的算法真正并行工作,则使用 4 个线程计算一些素数可能会快 4 倍。
  2. 如果您在内核可用时启动更多线程,操作系统的线程管理将在线程切换中花费越来越多的时间,这样您使用 CPU 的效率就会变得更差。
  3. 如果编译器、CPU 缓存和/或运行时意识到您运行多个线程,访问内存中的相同数据区域,则以不同的优化模式运行:只要编译/运行时确保只有一个线程访问数据,可以避免过于频繁地将数据写出到外部 RAM,并且可以有效地使用 CPU 的 L1 缓存。如果不是:必须激活信号量并更频繁地将缓存数据从 L1/L2 缓存刷新到 RAM。

所以我从高度并行的多线程中吸取的教训是:

  • 如果可能,使用单线程、无共享进程来提高效率
  • 如果需要线程,尽可能解耦共享数据访问
  • 如果可能,不要尝试分配比可用内核更多的负载工作线程

这是一个可以玩的小程序(javafx)。它:

  • 分配一个大小为 100.000.000 的字节数组,用随机字节填充
  • 提供一个方法,计算这个数组中设置的位数
  • 该方法允许计算每“第 n 个”字节位
  • count(0,1) 将计算所有字节数
  • count(0,4) 将计算 0'、4'、8' 字节位,允许并行交错计数

使用 MacPro(4 核)会导致:

  1. 运行一个线程,count(0,1) 需要 1326ms 来计算所有 399993625 位
  2. 并行运行两个线程,count(0,2) 和 count(1,2) 需要 920ms
  3. 运行四个线程,需要 618 毫秒
  4. 运行八个线程,需要 631 毫秒

enter image description here enter image description here enter image description here enter image description here

改变计数方式,例如递增一个共同共享的整数(AtomicInteger 或同步)将显着改变许多线程的性能。

public class MulithreadingEffects extends Application {
static class ParallelProgressBar extends ProgressBar {
AtomicInteger myDoneCount = new AtomicInteger();
int myTotalCount;
Timeline myWhatcher = new Timeline(new KeyFrame(Duration.millis(10), e -> update()));
BooleanProperty running = new SimpleBooleanProperty(false);

public void update() {
setProgress(1.0*myDoneCount.get()/myTotalCount);
if (myDoneCount.get() >= myTotalCount) {
myWhatcher.stop();
myTotalCount = 0;
running.set(false);
}
}

public boolean isRunning() { return myTotalCount > 0; }
public BooleanProperty runningProperty() { return running; }

public void start(int totalCount) {
myDoneCount.set(0);
myTotalCount = totalCount;
setProgress(0.0);
myWhatcher.setCycleCount(Timeline.INDEFINITE);
myWhatcher.play();
running.set(true);
}

public void add(int n) {
myDoneCount.addAndGet(n);
}
}

int mySize = 100000000;
byte[] inData = new byte[mySize];
ParallelProgressBar globalProgressBar = new ParallelProgressBar();
BooleanProperty iamReady = new SimpleBooleanProperty(false);
AtomicInteger myCounter = new AtomicInteger(0);

void count(int start, int step) {
new Thread(""+start){
public void run() {
int count = 0;
int loops = 0;
for (int i = start; i < mySize; i+=step) {
for (int m = 0x80; m > 0; m >>=1) {
if ((inData[i] & m) > 0) count++;
}
if (loops++ > 99) {
globalProgressBar.add(loops);
loops = 0;
}
}
myCounter.addAndGet(count);
globalProgressBar.add(loops);
}
}.start();
}

void pcount(Label result, int n) {
result.setText("("+n+")");
globalProgressBar.start(mySize);
long start = System.currentTimeMillis();
myCounter.set(0);
globalProgressBar.runningProperty().addListener((p,o,v) -> {
if (!v) {
long ms = System.currentTimeMillis()-start;
result.setText(""+ms+" ms ("+myCounter.get()+")");
}
});
for (int t = 0; t < n; t++) count(t, n);
}

void testParallel(VBox box) {
HBox hbox = new HBox();

Label result = new Label("-");
for (int i : new int[]{1, 2, 4, 8}) {
Button run = new Button(""+i);
run.setOnAction( e -> {
if (globalProgressBar.isRunning()) return;
pcount(result, i);
});
hbox.getChildren().add(run);
}

hbox.getChildren().addAll(result);
box.getChildren().addAll(globalProgressBar, hbox);
}


@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("ProgressBar's");

globalProgressBar.start(mySize);
new Thread("Prepare"){
public void run() {
iamReady.set(false);
Random random = new Random();
random.setSeed(4711);
for (int i = 0; i < mySize; i++) {
inData[i] = (byte)random.nextInt(256);
globalProgressBar.add(1);
}
iamReady.set(true);
}
}.start();

VBox box = new VBox();
Scene scene = new Scene(box,400,80,Color.WHITE);
primaryStage.setScene(scene);

testParallel(box);
GUIHelper.allowImageDrag(box);

primaryStage.show();
}

public static void main(String[] args) { launch(args); }
}

关于java - 多线程总是比单线程产生更好的性能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27319446/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com