[concurrency-interest] Scalable object cache

Nathan Reynolds nathan.reynolds at oracle.com
Wed Feb 29 11:18:23 EST 2012


While clock is constant, get() will scale very well.  So, the only 
concern is an occasional scalability issue when clock is updated.  The 
problem will show up as high CPU usage.

Every time clock is updated in the worst case, each thread will detect 
that "read" is behind and write to "read".  The fence after the write 
won't be a scalability issue.  The act of invalidating and updating the 
cache line holding "read" will be a bottleneck.  Unless the optimizer 
moves instructions in between, the window of vulnerability is very small 
(i.e. 1-2 instructions). Most likely only 1 thread is going to do the 
update.  Rarely 2 threads will.  So, I highly doubt you will have any 
trouble from get().

Nathan Reynolds 
<http://psr.us.oracle.com/wiki/index.php/User:Nathan_Reynolds> | 
Consulting Member of Technical Staff | 602.333.9091
Oracle PSR Engineering <http://psr.us.oracle.com/> | Server Technology

On 2/29/2012 6:37 AM, Peter Firmstone wrote:
> I don't have the hardware to test this so I'm hoping someone on the list
> will know.
>
> In the class below, the get() method returns the referent, provided it
> hasn't been enqueued.
>
> Both read and clock are volatile, clock is written to, once every 10
> seconds (or an interval the developer sets) by a single thread.
>
> if (read<  clock) read = clock; //Avoid unnecessary volatile write.
>
> If many threads are retrieving the referent, the volatile read variable
> is written at least once in between clock updates, but any further
> retrievals will only be reads.
>
> So in this case the volatile long values are multi read, with occasional
> writes, does get() look scalable to you?
>
> Regards,
>
> Peter.
>
> /*
>   * Copyright 2012 Zeus Project Services Pty Ltd.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
>   * You may obtain a copy of the License at
>   *
>   *      http://www.apache.org/licenses/LICENSE-2.0
>   *
>   * Unless required by applicable law or agreed to in writing, software
>   * distributed under the License is distributed on an "AS IS" BASIS,
>   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
>   * See the License for the specific language governing permissions and
>   * limitations under the License.
>   */
> package au.net.zeus.collection;
>
> /**
>   *
>   * @author Peter Firmstone.
>   */
> class TimedReferrer<T>  implements Referrer<T>, TimeBomb {
>
>      private volatile long clock;
>      private volatile long read;
>      private final TimedRefQueue queue;
>      private volatile T referent;
>      private volatile boolean enqued;
>      private final Object lock;
>      private final int hash;
>
>      TimedReferrer(T k, TimedRefQueue q){
>          long time = System.nanoTime();
>          clock = time;
>          read = time;
>          referent = k;
>          queue = q;
>          enqued = false;
>          lock = new Object();
>          int hash = 7;
>          hash = 29 * hash + k.hashCode();
>          hash = 29 * hash + k.getClass().hashCode();
>          this.hash = hash;
>      }
>
>      public T get() {
>          // Doesn't need to be atomic.
>          if (read<  clock) read = clock; //Avoid unnecessary volatile
> write.
>          return referent;
>      }
>
>      public void clear() {
>          referent = null;
>      }
>
>      public boolean isEnqueued() {
>          return enqued;
>      }
>
>      public boolean enqueue() {
>          if (enqued) return false;
>          if (referent == null) return false;
>          if (queue == null) return false;
>          synchronized (lock){ // Sync for atomic write of enqued.
>              if (enqued) return false;
>              enqued = queue.offer(this);
>          }
>          return enqued;
>      }
>
>      @Override
>      public void updateClock(long time){
>          if (read<  clock) { // only write volatile if necessary.
>              enqueue();
>              clear();
>          } else {
>              clock = time;
>          }
>      }
>
>      @Override
>      public boolean equals(Object o) {
>          if (this == o)  return true; // Same reference.
>          if (!(o instanceof Referrer))  return false;
>          Object k1 = get();
>          Object k2 = ((Referrer) o).get();
>          if ( k1 != null&&  k1.equals(k2)) return true;
>          return ( k1 == null&&  k2 == null&&  hashCode() ==
> o.hashCode()); // Both objects were collected.
>      }
>
>      @Override
>      public int hashCode() {
>          Object k = get();
>          int hash = 7;
>          if (k != null) {
>              hash = 29 * hash + k.hashCode();
>              hash = 29 * hash + k.getClass().hashCode();
>          } else {
>              hash = this.hash;
>          }
>          return hash;
>      }
>
>      @Override
>      public String toString(){
>          Object s = get();
>          if (s != null) return s.toString();
>          return super.toString();
>      }
>
> }
>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20120229/082e6f8f/attachment-0001.html>


More information about the Concurrency-interest mailing list