Global Metrics

path: .metrics.mi.mi_sei
old: 54.07937291037533
new: 13.12109826463199

path: .metrics.mi.mi_visual_studio
old: 40.53965047491376
new: 20.7905433157674

path: .metrics.mi.mi_original
old: 69.32280231210254
new: 35.55182906996225

path: .metrics.cyclomatic.sum
old: 12.0
new: 30.0

path: .metrics.cyclomatic.average
old: 2.0
new: 2.3076923076923075

path: .metrics.loc.sloc
old: 52.0
new: 199.0

path: .metrics.loc.cloc
old: 8.0
new: 48.0

path: .metrics.loc.lloc
old: 11.0
new: 49.0

path: .metrics.loc.ploc
old: 34.0
new: 132.0

path: .metrics.loc.blank
old: 10.0
new: 19.0

path: .metrics.nom.functions
old: 4.0
new: 9.0

path: .metrics.nom.total
old: 4.0
new: 9.0

path: .metrics.nexits.sum
old: 6.0
new: 9.0

path: .metrics.nexits.average
old: 1.5
new: 1.0

path: .metrics.halstead.n2
old: 36.0
new: 57.0

path: .metrics.halstead.estimated_program_length
old: 261.17595007788486
new: 460.8566933658039

path: .metrics.halstead.level
old: 0.06349206349206349
new: 0.017025089605734768

path: .metrics.halstead.n1
old: 18.0
new: 27.0

path: .metrics.halstead.purity_ratio
old: 1.8264052452998945
new: 0.785105099430671

path: .metrics.halstead.N2
old: 63.0
new: 248.0

path: .metrics.halstead.difficulty
old: 15.75
new: 58.73684210526316

path: .metrics.halstead.time
old: 720.0802987082039
new: 12244.315804453168

path: .metrics.halstead.N1
old: 80.0
new: 339.0

path: .metrics.halstead.bugs
old: 0.18392793766640725
new: 1.216232945833544

path: .metrics.halstead.effort
old: 12961.445376747672
new: 220397.68448015704

path: .metrics.halstead.vocabulary
old: 54.0
new: 84.0

path: .metrics.halstead.length
old: 143.0
new: 587.0

path: .metrics.halstead.volume
old: 822.948912809376
new: 3752.290327171133

path: .metrics.nargs.sum
old: 2.0
new: 6.0

path: .metrics.nargs.average
old: 0.5
new: 0.6666666666666666

path: .metrics.cognitive.sum
old: 9.0
new: 15.0

path: .metrics.cognitive.average
old: 2.25
new: 1.6666666666666667

Spaces Data

Minimal test - lines (23, 197)

path: .spaces[0].metrics.loc.lloc
old: 11.0
new: 49.0

path: .spaces[0].metrics.loc.cloc
old: 4.0
new: 39.0

path: .spaces[0].metrics.loc.sloc
old: 40.0
new: 175.0

path: .spaces[0].metrics.loc.ploc
old: 29.0
new: 122.0

path: .spaces[0].metrics.loc.blank
old: 7.0
new: 14.0

path: .spaces[0].metrics.halstead.effort
old: 12995.010783425609
new: 221982.8288140469

path: .spaces[0].metrics.halstead.purity_ratio
old: 1.7251689711770757
new: 0.765620491942144

path: .spaces[0].metrics.halstead.n2
old: 33.0
new: 55.0

path: .spaces[0].metrics.halstead.volume
old: 794.1395478760094
new: 3706.452818692343

path: .spaces[0].metrics.halstead.N2
old: 60.0
new: 244.0

path: .spaces[0].metrics.halstead.vocabulary
old: 51.0
new: 82.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: 241.52365596479055
new: 446.3567468022699

path: .spaces[0].metrics.halstead.time
old: 721.9450435236448
new: 12332.379378558158

path: .spaces[0].metrics.halstead.N1
old: 80.0
new: 339.0

path: .spaces[0].metrics.halstead.length
old: 140.0
new: 583.0

path: .spaces[0].metrics.halstead.difficulty
old: 16.363636363636363
new: 59.89090909090909

path: .spaces[0].metrics.halstead.n1
old: 18.0
new: 27.0

path: .spaces[0].metrics.halstead.level
old: 0.06111111111111111
new: 0.016697024893746207

path: .spaces[0].metrics.halstead.bugs
old: 0.1842453382001581
new: 1.2220575718771538

path: .spaces[0].metrics.nom.total
old: 4.0
new: 9.0

path: .spaces[0].metrics.nom.functions
old: 4.0
new: 9.0

path: .spaces[0].metrics.cognitive.sum
old: 9.0
new: 15.0

path: .spaces[0].metrics.cognitive.average
old: 2.25
new: 1.6666666666666667

path: .spaces[0].metrics.mi.mi_original
old: 73.98840500961064
new: 37.92774818213768

path: .spaces[0].metrics.mi.mi_visual_studio
old: 43.26807310503546
new: 22.179969697156537

path: .spaces[0].metrics.mi.mi_sei
old: 55.68866357103072
new: 15.363449845332454

path: .spaces[0].metrics.nargs.average
old: 0.5
new: 0.6666666666666666

path: .spaces[0].metrics.nargs.sum
old: 2.0
new: 6.0

path: .spaces[0].metrics.cyclomatic.average
old: 2.2
new: 2.4166666666666665

path: .spaces[0].metrics.cyclomatic.sum
old: 11.0
new: 29.0

path: .spaces[0].metrics.nexits.sum
old: 6.0
new: 9.0

path: .spaces[0].metrics.nexits.average
old: 1.5
new: 1.0

Code

namespace mozilla {

// Array-based LRU cache, thread-safe.
// Optimized for cases where `FetchOrAdd` is used with the same key most
// recently, and assuming the cost of running the value-builder function is much
// more expensive than going through the whole list.
// Note: No time limits on keeping items.
// TODO: Move to more public place, if this could be used elsewhere; make sure
// the cost/benefits are highlighted.
template 
class SmallArrayLRUCache {
 public:
  static_assert(std::is_default_constructible_v);
  static_assert(std::is_trivially_constructible_v);
  static_assert(std::is_trivially_copyable_v);
  static_assert(std::is_default_constructible_v);
  static_assert(LRUCapacity >= 2);
  static_assert(LRUCapacity <= 1024,
                "This seems a bit big, is this the right cache for your use?");

  // Reset all stored values to their default, and set cache size to zero.
  void Clear() {
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);
    if (mSize == ShutdownSize) {
      return;
    }
    Clear(lock);
  }

  // Clear the cache, and then prevent further uses (other functions will do
  // nothing).
  void Shutdown() {
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);
    if (mSize == ShutdownSize) {
      return;
    }
    Clear(lock);
    mSize = ShutdownSize;
  }

  // Add a key-value.
  template 
  void Add(Key aKey, ToValue&& aValue) {
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);

    if (mSize == ShutdownSize) {
      return;
    }

    // Quick add to the front, don't remove possible duplicate handles later in
    // the list, they will eventually drop off the end.
    KeyAndValue* const item0 = &mLRUArray[0];
    mSize = std::min(mSize + 1, LRUCapacity);
    if (MOZ_LIKELY(mSize != 1)) {
      // List is not empty.
      // Make a hole at the start.
      std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
    }
    item0->mKey = aKey;
    item0->mValue = std::forward(aValue);
    return;
  }

  // Look for the value associated with `aKey` in the cache.
  // If not found, run `aValueFunction()`, add it in the cache before returning.
  // After shutdown, just run `aValueFunction()`.
  template 
  Value FetchOrAdd(Key aKey, ValueFunction&& aValueFunction) {
    Value value;
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);

    if (mSize == ShutdownSize) {
      value = std::forward(aValueFunction)();
      return value;
    }

    KeyAndValue* const item0 = &mLRUArray[0];
    if (MOZ_UNLIKELY(mSize == 0)) {
      // List is empty.
      value = std::forward(aValueFunction)();
      item0->mKey = aKey;
      item0->mValue = value;
      mSize = 1;
      return value;
    }

    if (MOZ_LIKELY(item0->mKey == aKey)) {
      // This is already at the beginning of the list, we're done.
#ifdef SMALLARRAYLRUCACHE_STATS
      ++mCacheFoundAt[0];
#endif  // SMALLARRAYLRUCACHE_STATS
      value = item0->mValue;
      return value;
    }

    for (KeyAndValue* item = item0 + 1; item != item0 + mSize; ++item) {
      if (item->mKey == aKey) {
        // Found handle in the middle.
#ifdef SMALLARRAYLRUCACHE_STATS
        ++mCacheFoundAt[unsigned(item - item0)];
#endif  // SMALLARRAYLRUCACHE_STATS
        value = item->mValue;
        // Move this item to the start of the list.
        std::rotate(item0, item, item + 1);
        return value;
      }
    }

    // Handle was not in the list.
#ifdef SMALLARRAYLRUCACHE_STATS
    ++mCacheFoundAt[LRUCapacity];
#endif  // SMALLARRAYLRUCACHE_STATS
    {
      // Don't lock while doing the potentially-expensive ValueFunction().
      // This means that the list could change when we lock again, but
      // it's okay because we'll want to add the new entry at the beginning
      // anyway, whatever else is in the list then.
      // In the worst case, it could be the same handle as another `FetchOrAdd`
      // in parallel, it just means it will be duplicated, so it's a little bit
      // less efficient (using the extra space), but not wrong (the next
      // `FetchOrAdd` will find the first one).
      mozilla::OffTheBooksMutexAutoUnlock unlock(mMutex);
      value = std::forward(aValueFunction)();
    }
    // Make a hole at the start, and put the value there.
    mSize = std::min(mSize + 1, LRUCapacity);
    std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
    item0->mKey = aKey;
    item0->mValue = value;
    return value;
  }

#ifdef SMALLARRAYLRUCACHE_STATS
  ~SmallArrayLRUCache() {
    if (mSize != 0 && mSize != ShutdownSize) {
      fprintf(stderr,
              "***** SmallArrayLRUCache stats: (position -> hit count)\n");
      for (unsigned i = 0; i < mSize; ++i) {
        fprintf(stderr, "***** %3u -> %6u\n", i, mCacheFoundAt[i]);
      }
      fprintf(stderr, "***** not found -> %6u\n", mCacheFoundAt[LRUCapacity]);
    }
  }
#endif  // SMALLARRAYLRUCACHE_STATS

 private:
  void Clear(const mozilla::OffTheBooksMutexAutoLock&) {
    for (KeyAndValue* item = &mLRUArray[0]; item != &mLRUArray[mSize]; ++item) {
      item->mValue = Value{};
    }
    mSize = 0;
  }

  struct KeyAndValue {
    Key mKey;
    Value mValue;

    KeyAndValue() = default;
    KeyAndValue(KeyAndValue&&) = default;
    KeyAndValue& operator=(KeyAndValue&&) = default;
  };

  // Special size value that indicates the cache is in shutdown mode.
  constexpr static unsigned ShutdownSize = unsigned(-1);

  mozilla::OffTheBooksMutex mMutex{"LRU cache"};
  unsigned mSize = 0;
  KeyAndValue mLRUArray[LRUCapacity];
#ifdef SMALLARRAYLRUCACHE_STATS
  // Hit count for each position in the case. +1 for counting not-found cases.
  unsigned mCacheFoundAt[LRUCapacity + 1] = {0u};
#endif  // SMALLARRAYLRUCACHE_STATS
};

}  // namespace mozilla

Minimal test - lines (33, 195)

path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.5454545454545454

path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 28.0

path: .spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 9.0

path: .spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 9.0

path: .spaces[0].spaces[0].metrics.cognitive.average
old: 0.0
new: 1.6666666666666667

path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 15.0

path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 120.36819247706724
new: 15.699447339817125

path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 79.4351632867568
new: 23.0485347669935

path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 135.83412922035413
new: 39.41299445155889

path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 0.6666666666666666

path: .spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 6.0

path: .spaces[0].spaces[0].metrics.nexits.sum
old: 1.0
new: 9.0

path: .spaces[0].spaces[0].metrics.halstead.effort
old: 67.5
new: 217964.04309917337

path: .spaces[0].spaces[0].metrics.halstead.length
old: 9.0
new: 573.0

path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.005526047247960579
new: 1.2072632541666437

path: .spaces[0].spaces[0].metrics.halstead.n1
old: 5.0
new: 27.0

path: .spaces[0].spaces[0].metrics.halstead.n2
old: 3.0
new: 54.0

path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.81828088628892
new: 0.7663977097299144

path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.4
new: 0.016666666666666666

path: .spaces[0].spaces[0].metrics.halstead.N2
old: 3.0
new: 240.0

path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 2.5
new: 60.0

path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 8.0
new: 81.0

path: .spaces[0].spaces[0].metrics.halstead.time
old: 3.75
new: 12109.113505509633

path: .spaces[0].spaces[0].metrics.halstead.N1
old: 6.0
new: 333.0

path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 16.36452797660028
new: 439.14588767524094

path: .spaces[0].spaces[0].metrics.halstead.volume
old: 27.0
new: 3632.73405165289

path: .spaces[0].spaces[0].metrics.loc.sloc
old: 3.0
new: 163.0

path: .spaces[0].spaces[0].metrics.loc.lloc
old: 1.0
new: 49.0

path: .spaces[0].spaces[0].metrics.loc.blank
old: 0.0
new: 12.0

path: .spaces[0].spaces[0].metrics.loc.cloc
old: 0.0
new: 32.0

path: .spaces[0].spaces[0].metrics.loc.ploc
old: 3.0
new: 119.0

Code

class SmallArrayLRUCache {
 public:
  static_assert(std::is_default_constructible_v);
  static_assert(std::is_trivially_constructible_v);
  static_assert(std::is_trivially_copyable_v);
  static_assert(std::is_default_constructible_v);
  static_assert(LRUCapacity >= 2);
  static_assert(LRUCapacity <= 1024,
                "This seems a bit big, is this the right cache for your use?");

  // Reset all stored values to their default, and set cache size to zero.
  void Clear() {
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);
    if (mSize == ShutdownSize) {
      return;
    }
    Clear(lock);
  }

  // Clear the cache, and then prevent further uses (other functions will do
  // nothing).
  void Shutdown() {
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);
    if (mSize == ShutdownSize) {
      return;
    }
    Clear(lock);
    mSize = ShutdownSize;
  }

  // Add a key-value.
  template 
  void Add(Key aKey, ToValue&& aValue) {
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);

    if (mSize == ShutdownSize) {
      return;
    }

    // Quick add to the front, don't remove possible duplicate handles later in
    // the list, they will eventually drop off the end.
    KeyAndValue* const item0 = &mLRUArray[0];
    mSize = std::min(mSize + 1, LRUCapacity);
    if (MOZ_LIKELY(mSize != 1)) {
      // List is not empty.
      // Make a hole at the start.
      std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
    }
    item0->mKey = aKey;
    item0->mValue = std::forward(aValue);
    return;
  }

  // Look for the value associated with `aKey` in the cache.
  // If not found, run `aValueFunction()`, add it in the cache before returning.
  // After shutdown, just run `aValueFunction()`.
  template 
  Value FetchOrAdd(Key aKey, ValueFunction&& aValueFunction) {
    Value value;
    mozilla::OffTheBooksMutexAutoLock lock(mMutex);

    if (mSize == ShutdownSize) {
      value = std::forward(aValueFunction)();
      return value;
    }

    KeyAndValue* const item0 = &mLRUArray[0];
    if (MOZ_UNLIKELY(mSize == 0)) {
      // List is empty.
      value = std::forward(aValueFunction)();
      item0->mKey = aKey;
      item0->mValue = value;
      mSize = 1;
      return value;
    }

    if (MOZ_LIKELY(item0->mKey == aKey)) {
      // This is already at the beginning of the list, we're done.
#ifdef SMALLARRAYLRUCACHE_STATS
      ++mCacheFoundAt[0];
#endif  // SMALLARRAYLRUCACHE_STATS
      value = item0->mValue;
      return value;
    }

    for (KeyAndValue* item = item0 + 1; item != item0 + mSize; ++item) {
      if (item->mKey == aKey) {
        // Found handle in the middle.
#ifdef SMALLARRAYLRUCACHE_STATS
        ++mCacheFoundAt[unsigned(item - item0)];
#endif  // SMALLARRAYLRUCACHE_STATS
        value = item->mValue;
        // Move this item to the start of the list.
        std::rotate(item0, item, item + 1);
        return value;
      }
    }

    // Handle was not in the list.
#ifdef SMALLARRAYLRUCACHE_STATS
    ++mCacheFoundAt[LRUCapacity];
#endif  // SMALLARRAYLRUCACHE_STATS
    {
      // Don't lock while doing the potentially-expensive ValueFunction().
      // This means that the list could change when we lock again, but
      // it's okay because we'll want to add the new entry at the beginning
      // anyway, whatever else is in the list then.
      // In the worst case, it could be the same handle as another `FetchOrAdd`
      // in parallel, it just means it will be duplicated, so it's a little bit
      // less efficient (using the extra space), but not wrong (the next
      // `FetchOrAdd` will find the first one).
      mozilla::OffTheBooksMutexAutoUnlock unlock(mMutex);
      value = std::forward(aValueFunction)();
    }
    // Make a hole at the start, and put the value there.
    mSize = std::min(mSize + 1, LRUCapacity);
    std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
    item0->mKey = aKey;
    item0->mValue = value;
    return value;
  }

#ifdef SMALLARRAYLRUCACHE_STATS
  ~SmallArrayLRUCache() {
    if (mSize != 0 && mSize != ShutdownSize) {
      fprintf(stderr,
              "***** SmallArrayLRUCache stats: (position -> hit count)\n");
      for (unsigned i = 0; i < mSize; ++i) {
        fprintf(stderr, "***** %3u -> %6u\n", i, mCacheFoundAt[i]);
      }
      fprintf(stderr, "***** not found -> %6u\n", mCacheFoundAt[LRUCapacity]);
    }
  }
#endif  // SMALLARRAYLRUCACHE_STATS

 private:
  void Clear(const mozilla::OffTheBooksMutexAutoLock&) {
    for (KeyAndValue* item = &mLRUArray[0]; item != &mLRUArray[mSize]; ++item) {
      item->mValue = Value{};
    }
    mSize = 0;
  }

  struct KeyAndValue {
    Key mKey;
    Value mValue;

    KeyAndValue() = default;
    KeyAndValue(KeyAndValue&&) = default;
    KeyAndValue& operator=(KeyAndValue&&) = default;
  };

  // Special size value that indicates the cache is in shutdown mode.
  constexpr static unsigned ShutdownSize = unsigned(-1);

  mozilla::OffTheBooksMutex mMutex{"LRU cache"};
  unsigned mSize = 0;
  KeyAndValue mLRUArray[LRUCapacity];
#ifdef SMALLARRAYLRUCACHE_STATS
  // Hit count for each position in the case. +1 for counting not-found cases.
  unsigned mCacheFoundAt[LRUCapacity + 1] = {0u};
#endif  // SMALLARRAYLRUCACHE_STATS
};