Ответ 1
Это очень хороший вопрос в отношении потоковой передачи и его ссылки на реальную работу, что означает доступный физический процессор (CPU) и его ядра и гиперпотоки.
- Несколько потоков могут позволить вам делать что-то параллельно, если ваш процессор имеет более одного ядра. Таким образом, в идеальном мире, например, калорирование некоторых простых чисел, может быть в 4 раза быстрее, используя 4 потока, если ваш процессор имеет 4 ядра, и ваш алгоритм работает действительно параллельно.
- Если вы запускаете больше потоков по мере того, как ядра доступны, управление потоками вашей ОС будет тратить все больше времени на Thread-Switches и в такой эффективности, когда ваш CPU (CPU) становится хуже.
- Если компилятор, кеш процессора и/или среда выполнения реализовали то, что вы запускаете более одного потока, обращаясь к одной и той же области данных в памяти, работает в другом режиме оптимизации: пока компиляция/время выполнения уверены, что только один поток доступа к данным, может избежать слишком часто записывать данные в extenral RAM и может эффективно использовать кэш L1 вашего CPU. Если нет: необходимо активировать семафоры, а также кэшировать данные чаще из кэша L1/L2 в оперативную память.
Итак, мои уроки, извлеченные из многопоточности с большим количеством парраллелей, были следующими:
- Если возможно, использование однопоточных, совместно используемых процессов будет более эффективным.
- Если нужны потоки, отделите доступ к общим данным как можно больше
- Не пытайтесь выделять больше загруженных рабочих потоков, чем доступные ядра, если возможно
Здесь небольшая программа (javafx) для игры. Это:
- Выделяет массив байтов размером 100.000.000, заполненный случайными байтами
- Предоставляет метод, подсчитывающий количество бит, заданных в этом массиве
- Метод позволяет считать каждый бит n-го байта
- count (0,1) будет считать все байтовые байты
- count (0,4) будет считать 0 ', 4', 8 'байтовые биты, позволяющие проводить параллельный чередование
Использование MacPro (4 ядра) приводит к:
- Запуск одного потока, count (0,1) требует 1326ms для подсчета всех 399993625 бит.
- Выполнение двух потоков, подсчет (0,2) и подсчет (1,2) в параллельном режиме требуют 920 мс
- Запуск четырех потоков, требуется 618 мс
- Запуск восьми потоков, требуется 631 мс
Изменение способа подсчета, например. приращение общего общего целого числа (AtomicInteger или synchronized) значительно изменяет производительность многих потоков.
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); }
}