2016-12-13 6 views
2

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

Вот код из 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 для уже существующего, но вместо этого перефразируйте зонд.

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

ответ

3

Это не значит, что он потерпит неудачу, но это еще не все. Вызов этого метода выполняется методом 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 

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

+3

1) Я знаю, откуда это происходит, но THX для добавления его в любом случае. 2) Черт возьми! Я думал, что * CAS, уже известный с ошибкой *, означает, что следующая операция CAS завершится неудачно, а не предыдущая. Мне нравится ваша догадка, у них, вероятно, были тесты, которые доказывают это как оптимальное дополнение к коду. Ваш вклад очень ценится. – Eugene

+0

Да, я на 99% уверен в вашей теории (посмотрев код снова). Это противоречит интуиции для меня, но может быть, это имеет смысл для кого-то, кто поддерживает этот код. Еще раз спасибо, и я соглашусь с этим. – Eugene

+0

Да, это самое разумное объяснение. Попробуйте сжатый контур с инкрементами целого числа с CAS. Под тяжелым соперничеством вы можете легко получить 90 +% неудачный CAS. – Voo