LongAdder Striped64 был неконтролируемая деталь реализации

Это вопрос не о том, как работает LongAdder, а о интригующей детализации, которую я не могу понять.

Вот код из Striped64 (я вырезал некоторые части и оставил соответствующие части для вопроса):

    final void longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended) {
    int h;
    if ((h = getProbe()) == 0) {
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
        wasUncontended = true;
    }
    boolean collide = false;  // True if last slot nonempty
    for (;;) {
        Cell[] as; Cell a; int n; long v;
        if ((as = cells) != null && (n = as.length) > 0) {
            if ((a = as[(n - 1) & h]) == null) {
                //logic to insert the Cell in the array
            }
            // CAS already known to fail
            else if (!wasUncontended)   {
                wasUncontended = true;      // Continue after rehash
            }
            else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))){
                break;
            }

Мне многое понятно из кода, кроме:

        // CAS already known to fail
        else if (!wasUncontended)   {
            wasUncontended = true;      // Continue after rehash
        }

Откуда такая уверенность, что следующий CAS потерпит неудачу? По крайней мере, это действительно сбивает меня с толку, потому что эта проверка имеет смысл только для одного случая: когда какой-то поток входит в метод longAccumulate в n-й раз (n > 1), и вращение занято в его первом цикле.

Это похоже на код, который говорит: если вы (какой-то поток) были здесь раньше, и у вас есть некоторая конкуренция в конкретном слоте ячейки, не пытайтесь CAS ваше значение к уже существующему, а вместо этого перефразируйте зонд.

Я искренне надеюсь, что у меня будет смысл для кого-то.

1 ответ

Решение

Дело не в том, что оно провалится, а в том, что оно провалилось. Вызов этого метода выполняется LongAdderadd метод.

public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;
    if ((as = cells) != null || !casBase(b = base, b + x)) {
        boolean uncontended = true;
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[getProbe() & m]) == null ||
            !(uncontended = a.cas(v = a.value, v + x)))
            longAccumulate(x, null, uncontended);
    }
}
  1. Первый набор условий связан с существованием длинных ячеек. Если нужной ячейки не существует, она будет пытаться накапливать непреднамеренное (как не было попытки добавить) путем атомарного добавления необходимой ячейки и последующего добавления.
  2. Если ячейка существует, попробуйте добавить (v + x). Если при добавлении произошел сбой, возникла некоторая форма разногласий, в этом случае попытайтесь выполнить накопление оптимистично / атомарно (вращение до успешного завершения)

Так почему это имеет

wasUncontended = true;      // Continue after rehash

Мое лучшее предположение состоит в том, что при сильном конфликте он попытается дать время работающему потоку наверстать упущенное и принудительно повторить существующие ячейки.

Другие вопросы по тегам