From cb4643510cc8a5793b87d09140842f2e62ef86c9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 31 Jul 2021 23:42:43 +0000 Subject: [PATCH] Improved the memoizer by adding fast path and slightly simplified existing logic, updated copyright year in readme document. --- Readme.markdown | 2 +- src/io/nayuki/fastqrcodegen/Memoizer.java | 29 ++++++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index dfcff4c..b0e0e0f 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -60,7 +60,7 @@ Examples License ------- -Copyright © 2019 Project Nayuki. (MIT License) +Copyright © 2021 Project Nayuki. (MIT License) [https://www.nayuki.io/page/fast-qr-code-generator-library](https://www.nayuki.io/page/fast-qr-code-generator-library) Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/src/io/nayuki/fastqrcodegen/Memoizer.java b/src/io/nayuki/fastqrcodegen/Memoizer.java index cbb7455..34a50b7 100644 --- a/src/io/nayuki/fastqrcodegen/Memoizer.java +++ b/src/io/nayuki/fastqrcodegen/Memoizer.java @@ -24,10 +24,10 @@ package io.nayuki.fastqrcodegen; import java.lang.ref.SoftReference; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -35,7 +35,7 @@ import java.util.function.Function; final class Memoizer { private final Function function; - private Map> cache = new HashMap<>(); + Map> cache = new ConcurrentHashMap<>(); private Set pending = new HashSet<>(); @@ -47,21 +47,30 @@ final class Memoizer { // Computes function.apply(arg) or returns a cached copy of a previous call. public R get(T arg) { + // Non-blocking fast path + { + SoftReference ref = cache.get(arg); + if (ref != null) { + R result = ref.get(); + if (result != null) + return result; + } + } + + // Sequential slow path while (true) { synchronized(this) { - if (cache.containsKey(arg)) { - SoftReference ref = cache.get(arg); + SoftReference ref = cache.get(arg); + if (ref != null) { R result = ref.get(); if (result != null) return result; cache.remove(arg); } - // Now cache.containsKey(arg) == false + assert !cache.containsKey(arg); - if (!pending.contains(arg)) { - pending.add(arg); + if (pending.add(arg)) break; - } try { this.wait(); @@ -73,9 +82,7 @@ final class Memoizer { try { R result = function.apply(arg); - synchronized(this) { - cache.put(arg, new SoftReference<>(result)); - } + cache.put(arg, new SoftReference<>(result)); return result; } finally { synchronized(this) {