Skip to content

Instantly share code, notes, and snippets.

@iaveryanov
Last active August 29, 2015 13:58
Show Gist options
  • Select an option

  • Save iaveryanov/9949603 to your computer and use it in GitHub Desktop.

Select an option

Save iaveryanov/9949603 to your computer and use it in GitHub Desktop.
CAS and idling
package ru.inlinetelecom.vn2.util;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* вариант с uncontended lock (getAvailableProcessors >= worker_count
*/
public class AtomicDecUntilZeroMain {
// счетчик холостого хода
private static AtomicLong idlingCounter = new AtomicLong(0);
/**
* 1) вызывается из многих потоков
* 2) не должно быть меньше 0
* 3) если уже 0 и вызывают этот метод, то должно оставаться 0
*
* @param counter
* @return 0 or value after decrement
*/
private static int decUntilZero(AtomicInteger counter) {
while (true) {
int current = counter.get();
if (current == 0) {
return current;
}
int next = current - 1;
if (counter.compareAndSet(current, next)) {
return next;
}
idlingCounter.incrementAndGet();
}
}
public static void main(String[] args) throws InterruptedException {
final int initialValue = 400000000;
final int countOfThreads = 4; // всего потоков
final int countOfDecPerThread = initialValue/countOfThreads; // количество декрементов на один поток
final AtomicInteger counter = new AtomicInteger(initialValue);
ExecutorService executor = Executors.newFixedThreadPool(countOfThreads);
long startedAt = System.currentTimeMillis();
final CountDownLatch latch = new CountDownLatch(countOfThreads);
for (int i = 0; i < countOfThreads; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < countOfDecPerThread; i++) {
decUntilZero(counter);
}
latch.countDown();
// System.out.println("task in progress..." + (latch.getCount()));
}
});
}
System.out.println("всех ждёмс, однако...");
latch.await();
long elapsed = System.currentTimeMillis() - startedAt;
executor.shutdown(); // за ненадобностью более!
System.out.println("Ну наконец-ТА дождалИСА!!! Урра товариСЧи, уРРа!");
System.out.println("================================================");
int now = counter.get();
System.out.println("== INPUT DATA");
System.out.println(String.format("%s: %d", "initial ", initialValue));
System.out.println(String.format("%s: %d", "threads ", countOfThreads));
System.out.println(String.format("%s: %d", "dec per threads ", countOfDecPerThread));
System.out.println("== RESULTS");
System.out.println(String.format("%s: %d", "еще осталось ", now));
System.out.println(String.format("%s: %d", "раз выполнилось ", (initialValue - now)));
System.out.println(String.format("%s: %d", "холостых ходов ", idlingCounter.get()));
System.out.println(String.format("%s: %s", "elapsed, millis ", elapsed));
}
}
@LexaChebara
Copy link

Profile

java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)

Linux 2.6.38-13-server #57 SMP Sun Feb 19 22:11:49 UTC 2012 x86_64 GNU/Linux

2 x Intel(R) Xeon(R) CPU E5645 @ 2.40GHz, т.е. 12 hardware cpu core или 24 потока

Results

Initial value - 400 000 000

Pool size Ops per thread Final value Op count Misses count Time elapsed, ms
4 100 000 000 0 400 000 000 105 621 882 23 303
24 16 666 666 16 399 999 984 367 097 933 40 331
100 4 000 000 0 400 000 000 383 702 094 41 787

@iaveryanov
Copy link
Author

Интрересно, почему время увеличилось в 2 раза?

@LexaChebara
Copy link

Время увеличилось в 2 раза вот почему.
Кол-во промахов стало в 3 раза больше.
Кол-во одновременно работающих потоков (так как 12 ядер) стало в 3 раза больше, а, следовательно, конфликтов стало больше. Вероятность конфликта растет с количеством одновременно работающих потоков. Операция cmpxchg блокирует шину данных при чтении из памяти.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment