YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/striped64.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include "yb/util/monotime.h"
34
#include "yb/util/random.h"
35
#include "yb/util/striped64.h"
36
#include "yb/util/threadlocal.h"
37
38
using yb::striped64::internal::HashCode;
39
using yb::striped64::internal::Cell;
40
41
namespace yb {
42
43
namespace striped64 {
44
namespace internal {
45
//
46
// HashCode
47
//
48
49
584k
HashCode::HashCode() {
50
584k
  std::mt19937_64 random(MonoTime::Now().GetDeltaSince(MonoTime::Min()).ToNanoseconds());
51
584k
  const uint64_t hash = random();
52
584k
  code_ = (hash == 0) ? 1 : hash;  // Avoid zero to allow xorShift rehash
53
584k
}
54
55
//
56
// Cell
57
//
58
59
Cell::Cell()
60
8.89M
    : value_(0) {
61
8.89M
}
62
} // namespace internal
63
} // namespace striped64
64
65
//
66
// Striped64
67
//
68
namespace {
69
70
const int64_t kNumCpus = sysconf(_SC_NPROCESSORS_ONLN);
71
thread_local std::unique_ptr<HashCode> hashcode_;
72
73
}
74
75
Striped64::Striped64()
76
    : busy_(false),
77
      cell_buffer_(nullptr),
78
      cells_(nullptr),
79
7.71M
      num_cells_(0) {
80
7.71M
}
81
82
478k
Striped64::~Striped64() {
83
  // Cell is a POD, so no need to destruct each one.
84
478k
  free(cell_buffer_);
85
478k
}
86
87
92.1k
void Striped64::RetryUpdate(int64_t x, Rehash contention) {
88
92.1k
  uint64_t h = hashcode_->code_;
89
  // There are three operations in this loop.
90
  //
91
  // 1. Try to add to the Cell hash table entry for the thread if the table exists.
92
  //    When there's contention, rehash to try a different Cell.
93
  // 2. Try to initialize the hash table.
94
  // 3. Try to update the base counter.
95
  //
96
  // These are predicated on successful CAS operations, which is why it's all wrapped in an
97
  // infinite retry loop.
98
182k
  while (true) {
99
182k
    int32_t n = base::subtle::Acquire_Load(&num_cells_);
100
182k
    if (n > 0) {
101
107k
      if (contention == kRehash) {
102
        // CAS failed already, rehash before trying to increment.
103
16.7k
        contention = kNoRehash;
104
90.6k
      } else {
105
90.6k
        Cell *cell = &(cells_[(n - 1) & h]);
106
90.6k
        int64_t v = cell->value_.Load();
107
90.6k
        if (cell->CompareAndSet(v, Fn(v, x))) {
108
          // Successfully CAS'd the corresponding cell, done.
109
90.5k
          break;
110
90.5k
        }
111
16.8k
      }
112
      // Rehash since we failed to CAS, either previously or just now.
113
16.8k
      h ^= h << 13;
114
16.8k
      h ^= h >> 17;
115
16.8k
      h ^= h << 5;
116
75.5k
    } else if (n == 0 && CasBusy()) {
117
      // We think table hasn't been initialized yet, try to do so.
118
      // Recheck preconditions, someone else might have init'd in the meantime.
119
73.8k
      n = base::subtle::Acquire_Load(&num_cells_);
120
73.8k
      if (n == 0) {
121
73.7k
        n = 1;
122
        // Calculate the size. Nearest power of two >= NCPU.
123
        // Also handle a negative NCPU, can happen if sysconf name is unknown
124
368k
        while (kNumCpus > n) {
125
295k
          n <<= 1;
126
295k
        }
127
        // Allocate cache-aligned memory for use by the cells_ table.
128
73.7k
        int err = posix_memalign(&cell_buffer_, CACHELINE_SIZE, sizeof(Cell)*n);
129
0
        CHECK_EQ(0, err) << "error calling posix_memalign" << std::endl;
130
        // Initialize the table
131
73.7k
        cells_ = new (cell_buffer_) Cell[n];
132
73.7k
        base::subtle::Release_Store(&num_cells_, n);
133
73.7k
      }
134
      // End critical section
135
73.8k
      busy_.Store(0);
136
1.12k
    } else {
137
      // Fallback to adding to the base value.
138
      // Means the table wasn't initialized or we failed to init it.
139
1.12k
      int64_t v = base_.value_.Load();
140
1.85k
      if (CasBase(v, Fn(v, x))) {
141
1.85k
        break;
142
1.85k
      }
143
1.12k
    }
144
182k
  }
145
  // Record index for next time
146
92.1k
  hashcode_->code_ = h;
147
92.1k
}
148
149
1
void Striped64::InternalReset(int64_t initialValue) {
150
1
  const int32_t n = base::subtle::Acquire_Load(&num_cells_);
151
1
  base_.value_.Store(initialValue);
152
1
  for (int i = 0; i < n; i++) {
153
0
    cells_[i].value_.Store(initialValue);
154
0
  }
155
1
}
156
157
289M
void LongAdder::IncrementBy(int64_t x) {
158
289M
  if (!hashcode_) {
159
584k
    hashcode_ = std::make_unique<HashCode>();
160
584k
  }
161
  // Use hash table if present. If that fails, call RetryUpdate to rehash and retry.
162
  // If no hash table, try to CAS the base counter. If that fails, RetryUpdate to init the table.
163
289M
  const int32_t n = base::subtle::Acquire_Load(&num_cells_);
164
289M
  if (n > 0) {
165
188M
    Cell *cell = &(cells_[(n - 1) & hashcode_->code_]);
166
0
    DCHECK_EQ(0, reinterpret_cast<const uintptr_t>(cell) & (sizeof(Cell) - 1))
167
0
        << " unaligned Cell not allowed for Striped64" << std::endl;
168
188M
    const int64_t old = cell->value_.Load();
169
188M
    if (!cell->CompareAndSet(old, old + x)) {
170
      // When we hit a hash table contention, signal RetryUpdate to rehash.
171
16.7k
      RetryUpdate(x, kRehash);
172
16.7k
    }
173
100M
  } else {
174
100M
    int64_t b = base_.value_.Load();
175
100M
    if (!base_.CompareAndSet(b, b + x)) {
176
      // Attempt to initialize the table. No need to rehash since the contention was for the
177
      // base counter, not the hash table.
178
75.2k
      RetryUpdate(x, kNoRehash);
179
75.2k
    }
180
100M
  }
181
289M
}
182
183
//
184
// LongAdder
185
//
186
187
15.5M
int64_t LongAdder::Value() const {
188
15.5M
  int64_t sum = base_.value_.Load();
189
15.5M
  const int32_t n = base::subtle::Acquire_Load(&num_cells_);
190
17.8M
  for (int i = 0; i < n; i++) {
191
2.38M
    sum += cells_[i].value_.Load();
192
2.38M
  }
193
15.5M
  return sum;
194
15.5M
}
195
196
} // namespace yb