---
# System prepended metadata

title: WEEK 7

---

# WEEK 7
## 7.1
### 1. Get method
```java
// Return value v associated with key k, or null
  public V get(K k) {
    final int h = getHash(k), stripe = h % lockCount;
    synchronized(locks[stripe]){
      final int hash = h % buckets.length;

      ItemNode<K,V> node = ItemNode.search(buckets[hash], k);
      return node == null ? null : node.v;
    }
  }
```

### 2. Size method
```java
public int size() {
    int sum = 0;
    
    for(int stripe = 0; stripe < lockCount; stripe++){
      synchronized(locks[stripe]){
        sum += this.sizes[stripe];
      }
    }
    
    return sum;
  }
```
_Explain why it is important to lock stripe s when reading its size from sizes[s] :_
- Because an other thread could be doing actions that could impact size in the mean time
### 3 & 4. Put if absent + reallocation method
```java
// Put v at key k only if absent
  public V putIfAbsent(K k, V v) {
    final int h = getHash(k), stripe = h % lockCount;
    synchronized(locks[stripe]){
      if(containsKey(k))
        return get(k);
      else{
        final int hash = h % buckets.length;
        buckets[hash] = new ItemNode<K,V>(k, v, buckets[hash]);
        sizes[stripe]++;
        
        if(sizes[stripe]>(buckets.length/lockCount))
          reallocateBuckets();
        
        return null;
      }
    }
  }
```
### 5. Put if absent method
```java
  // Remove and return the value at key k if any, else return null
  public V remove(K k) {
    final int h = getHash(k), stripe = h % lockCount;

    synchronized(locks[stripe]){
      
      final int hash = h % buckets.length;
      ItemNode<K,V> prev = buckets[hash];
      // No element in the map
      if(prev == null)
        return null;
      // If the given element is the head of map
      else if(k.equals(prev.k)){
        V old = prev.v;
        this.sizes[stripe]--;
        buckets[hash] = prev.next;
        return old;
      }else{
        while (prev.next != null && !k.equals(prev.next.k))
          prev = prev.next;
        if (prev.next != null) {  // Delete ItemNode prev.next
          V old = prev.next.v;
          this.sizes[stripe]--;
          prev.next = prev.next.next;
          return old;
        } else
          return null;
      }
    }
  }
```
### 6. For each method
```java
public void forEach(Consumer<K,V> consumer) {

     for (int hash=0; hash<buckets.length; hash++) {
      ItemNode<K,V> node = buckets[hash];
      if(node != null){
      final int h  = getHash(node.k);
      final int stripe =  h % lockCount;
      synchronized(locks[stripe]){

        while (node != null) {
        consumer.accept(node.k, node.v);
        node = node.next;
        }  
      }  
     }
    }
  }
```

We choose to implement method (1) because ...

### 7 and 8 Test and time measurement
```
# OS:   Mac OS X; 10.14.6; x86_64
# JVM:  Oracle Corporation; 12.0.1
# CPU:  null; 4 "cores"
# Date: 2019-10-10T10:59:51+0200

class SynchronizedMap
        17 maps to B
       117 maps to C
        34 maps to F
       217 maps to E
        17 maps to B
        17 maps to B
       217 maps to E
        34 maps to F
       217 maps to E
        17 maps to B
        34 maps to F

class StripedMap
        17 maps to B
       117 maps to C
        34 maps to F
       217 maps to E
        17 maps to B
        17 maps to B
       217 maps to E
        34 maps to F
       217 maps to E
        17 maps to B
        34 maps to F

class WrapConcurrentHashMap
        17 maps to B
       117 maps to C
        17 maps to B
        34 maps to F
       217 maps to E
        17 maps to B
        34 maps to F
       217 maps to E
        17 maps to B
        34 maps to F
       217 maps to E

SynchronizedMap       16         624550,0 us   26337,13          2
99992.0

StripedMap            16         278238,9 us   88527,71          2
99992.0

WrapConcHashMap       16         270839,5 us  138947,41          2
99992.0

```

The gap between SynchronizedMap (624550,0 us) and StripedMap (278238,9 us) is as expected, since multiple thread can update the map at the same time (unlike SynchronizedMap)

### 9

It may be because having a single thread working on each strip would cause a lot of overhead

### 10
Having 32 threads improve the probability of not having to wait on a lock

### 11
It can be problematic with regards to thread safety because we can risk that one of the threads locks the stripe while the other intervening thread calls reallocateBuckets.

## 7.2
### 1. Get
```java
// Return value v associated with key k, or null
  public V get(K k) {
    final ItemNode<K,V>[] bs = buckets;
    final int h = getHash(k), stripe = h % lockCount, hash = h % bs.length;
    
    // The sizes access is necessary for visibility of bs elements
    if(sizes.get(stripe) != 0){
      if(ItemNode.search(bs[hash], k, null)){
        return bs[hash].v;
      }else
       return null;

    }else
    return  null;
  }
```

### 2. Size
```java
public int size() {
     int sum = 0;
    
    for(int stripe = 0; stripe < lockCount; stripe++)
        sum += sizes.get(stripe);
    return sum;
  }
```

### 3. putIfAbsent
```java
 // Put v at key k only if absent.  
  public V putIfAbsent(K k, V v) {
    final int h = getHash(k), stripe = h % lockCount;
    final Holder<V> old = new Holder<V>();
    ItemNode<K,V>[] bs;
    int afterSize; 
    synchronized (locks[stripe]) {
      if(containsKey(k))
        return get(k);

      bs = buckets;
      final int hash = h % bs.length;
      final ItemNode<K,V> node = bs[hash], 
      newNode = ItemNode.delete(node, k, old);
      bs[hash] = new ItemNode<K,V>(k, v, newNode);
      // Write for visibility; increment if k was not already in map
      afterSize = sizes.addAndGet(stripe, newNode == node ? 1 : 0);
    }
    if (afterSize * lockCount > bs.length)
      reallocateBuckets(bs);
    return null;
  }
 ```
 
Why do you not need to write to the stripe size if nothing was added?

Because the size hasn't changed

### 4. Remove
```java
 // Remove and return the value at key k if any, else return null
  public V remove(K k) {
    final int h = getHash(k), stripe = h % lockCount;
    ItemNode<K,V>[] bs = buckets;
    
    final ItemNode<K,V> node = bs[h % bs.length];
    final Holder<V> old = new Holder<V>();
    
    synchronized(locks[stripe]){
      ItemNode.delete(node,k,old);
      if(old.get() != null)
        sizes.addAndGet(stripe, -1);
    }
    return old.get();
    
  }
```
### 5. For each
```java
// Iterate over the hashmap's entries one stripe at a time.  
  public void forEach(Consumer<K,V> consumer) {
    ItemNode<K,V>[] bs = buckets;
    for (int stripe=0; stripe<bs.length; stripe++) {
      ItemNode<K,V> node = bs[stripe];
        while (node != null) {
          consumer.accept(node.k, node.v);
          node = node.next;
      }  
    }
  }
```
### 6
```
# OS:   Mac OS X; 10.14.6; x86_64
# JVM:  Oracle Corporation; 12.0.1
# CPU:  null; 4 "cores"
# Date: 2019-10-21T13:59:14+0200
SynchronizedMap       16         651667,6 us   21744,63          2
99992.0
StripedMap            16         271182,7 us   92589,24          2
99992.0
StripedWriteMap       16         234427,6 us  136565,79          2
71271.15
WrapConcHashMap       16         232737,3 us  130171,54          2
99992.0

```

Our StripedWriteMap seems to be faster (as expected) than our StripedMap. It makes sense because we only lock on write and not on read
