Caffeine eviction by size seems to not work - caffeine-cache

I am using caffeine cache.
I want to put it under size limitation but it does not work properly.
test 1:
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.build();
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.build();
for (int i = 1; i <= 10; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
}
System.out.println("cache size: " + cache.estimatedSize() + ", cache keys: " + cache.asMap().values().stream().collect(Collectors.joining(",")));
result: cache size: 10, cache keys: 1,2,10
another test: trying to get key and set max to 1
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(1)
.build();
for (int i = 1; i <= 10; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
System.out.println("cache size: " + cache.estimatedSize() + ", cache keys: " + cache.asMap().values().stream().collect(Collectors.joining(",")));
cache size: 10, cache keys: 2,3,4,5,6,7,8,9,10
last test : run 100 times, max size 1
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(1)
.build();
for (int i = 1; i <= 100; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
System.out.println("cache size: " + cache.estimatedSize() + ", cache keys: " + cache.asMap().values().stream().collect(Collectors.joining(",")));
cache size: 99, cache keys: 96,97,99,19,23,58
can someone please help me understand this and how to make it work properly?
Thanks to Ben Manes, I added .executor(Runnable::run)
Now after doing this I do get only 3 items
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.executor(Runnable::run)
.build();
for (int i = 1; i <= 10; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
cache.cleanUp();
System.out.println("cache size: " + cache.estimatedSize() + ", cache: " + CodecUtils.toJson(cache.asMap().values()));
cache size: 3, cache: ["3","9","10"]
wouldn't this block my thread?
why isn't key 5 in the cache as I have been using it several times?

By default the cache will perform some operations asynchronously, such as eviction and notifying a removal listener. This is to minimize request latencies, as the auxiliary work is not necessary for the request itself and the user supplied callbacks might be expensive.
The cache's own maintenance work is very cheap, so you can safely run it on the caller's thread if desired by using Caffeine.executor(Runnable::run). This will penalize the caller with additionally evicting the entry, but will not block other operations from occurring. This is due to the cache internally using multiple locks and operation buffers, so that it can schedule work when a lock is busy rather than block threads.
In regards to the size, this is because the entry is evicted prior to being retrieved so it doesn't build up frequency. A getIfPresent doesn't increase the frequency if the entry is absent, whereas get(key, /* loading function */) would because it is penalized to load the value on the miss. The eviction policy utilizes both recency and frequency in its decisions, so it may evict recent arrivals early as often "one-hit wonders", aka cache pollution.
If we take your code as is and output the cache's state we see this,
for (int i = 1; i <= 10; i++) {
String val = String.valueOf(i);
cache.put(val, val);
System.out.println(val + " -> " + cache.asMap());
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
cache.cleanUp();
System.out.println("cache size: " + cache.estimatedSize());
1 -> {1=1}
2 -> {1=1, 2=2}
3 -> {1=1, 2=2, 3=3}
4 -> {2=2, 3=3, 4=4}
5 -> {2=2, 3=3, 5=5}
6 -> {2=2, 3=3, 6=6}
7 -> {2=2, 3=3, 7=7}
8 -> {2=2, 3=3, 8=8}
9 -> {2=2, 3=3, 9=9}
10 -> {2=2, 3=3, 10=10}
cache size: 3
If we access key 5 on every iteration then it is retained,
for (int i = 1; i <= 10; i++) {
String val = String.valueOf(i);
cache.put(val, val);
System.out.println(val + " -> " + cache.asMap());
cache.getIfPresent("5");
}
cache.cleanUp();
System.out.println("cache size: " + cache.estimatedSize());
1 -> {1=1}
2 -> {1=1, 2=2}
3 -> {1=1, 2=2, 3=3}
4 -> {2=2, 3=3, 4=4}
5 -> {2=2, 3=3, 5=5}
6 -> {3=3, 5=5, 6=6}
7 -> {3=3, 5=5, 7=7}
8 -> {3=3, 5=5, 8=8}
9 -> {3=3, 5=5, 9=9}
10 -> {3=3, 5=5, 10=10}
cache size: 3

Related

Recursive call in swift

I am confused with the following recursive example. The place where recursion happens, the local variable needs to be updated every time. I wonder then how it could store the base result? And let variable is not mutable, how it updates?
The question is as follows for the following solution:
Implement a recursive function named digits that takes a positive
integer number and return an array containing it’s digits in order.
Function call:
digits(123)
Function output:
[1, 2, 3]
func digits(_ number:Int) -> [Int]
{
if number >= 10 {
// confusion in the following line
let firstDigits = digits(number / 10)
let lastDigit = number % 10
return firstDigits + [lastDigit]
} else {
return [number]
}
}
I would rather approach the problems as follows. I wonder what is the advantages of having the above solution.
func digits(_ number:Int) -> [Int]
{
if number >= 10 {
let lastDigit = number % 10
return digits(number / 10) + [lastDigit]
} else {
return [number]
}
}
I wonder then how it could store the base result? And let variable is not mutable, how it updates?
firstDigits never changes, it is only set once in each invocation of digits. Each invocation of digits has it's own variables.
Example Execution
In the following example I show how the execute proceeds as a series of substitutions.
digits(123) ->
digits(123 / 10) + [123 % 10] ->
digits(12) + [3] ->
digits(12 / 10) + [12 % 10] + [3] ->
digits(1) + [2] + [3] ->
[1] + [2] + [3] ->
[1, 2, 3]
Another way to write it that may be more clear
func digits(_ number:Int) -> [Int]
{
if number >= 10 {
return digits(number / 10) + [number % 10]
} else {
return [number]
}
}

How is capacity increased in StringBuffer?

We know that StringBuffer's default capacity is 16 and when we try to add 17th char it will be increased by following rule:
newCapacity = (current capacity + 1) *2;
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaa"); // length is 16
System.out.println(sb.capacity()); // it gives 16
If I add 17th char
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaaa"); // length is 17
System.out.println(sb.capacity()); // it gives 34
But confusing part is now
If I try to add 35 chars
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // length is 35
System.out.println(sb.capacity()); // it gives 35
capacity should have been increased by 70 at this point of time.
Interesting part is
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // length is 34
sb.append("a"); // added 35th char
System.out.println(sb.capacity()); // it gives 70 which is correct
Can any one shed some light on this ?
The expandCapacity in StringBuffer does:
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
Since value.length is 16 at that time in your 35 example, it will use 35 (given in minimumCapacity). In the last example however, during the last append the value.length is 34 and minimumCapacity is 35, so new capacity will be value.length * 2 + 2.
The specifics may depend slightly on JDK version, but on my local version of 1.8.0_66:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
Note that value.length is actually capacity, not the length of the string being stored. The number of characters currently in the buffer is count! Also recall that value.length is initially 16, when calling new StringBuffer(). With those things in mind, let's do a little bit of stack tracing for each of the cases you presented.
For a string of size 17:
sb.append("12345678901234567")
if (str == null) -> false
len = 17;
ensureCapacityInternal(0 + 17)
if (17 - 16 > 0) -> true
expandCapacity(17)
newCapacity = 16 * 2 + 2 = 34
if (34 - 17 < 0) -> false
value = Arrays.copyOf("", 34)
str.getChars(0, 17, "", 17)
return this
sb.build() -> "12345678901234567"
sb.capacity() -> 34
For a string of size 35:
sb.append("12345678901234567890123456789012345")
if (str == null) -> false
len = 35;
ensureCapacityInternal(0 + 35)
if (35 - 16 > 0) -> true
expandCapacity(35)
newCapacity = 16 * 2 + 2 = 34
if (34 - 35 < 0) -> true
newCapacity = 35
value = Arrays.copyOf("", 35)
str.getChars(0, 35, "", 35)
return this
sb.build() -> "12345678901234567890123456789012345"
sb.capacity() -> 35
Note that the difference comes on the if (newCapacity - minimumCapacity < 0) line. If a string is appended that is longer than oldCapacity * 2 + 2, then newCapacity will be set to the length of the string to be appended.
In other words, when appending, if the buffer is not big enough to hold the existing text plus the appended text, it will check if (roughly) doubling in size would hold the new text. If that is still not enough, rather than recursively expanding, it will expand to exactly big enough.
This doesn't only happen with 35, though with strings much longer than that you probably wouldn't be running into the case where what you're appending is more than twice as long as your current capacity.
You would also see the same "length = capacity" if you were to do, say
StringBuffer sBuffer = new StringBuffer();
sBuffer.append("1234567890123456");
System.out.println(sBuffer.capacity()); // 16
sBuffer.append("1234567890123456789");
System.out.println(sBuffer.capacity()); // 35
But not
StringBuffer sBuffer = new StringBuffer();
sBuffer.append("1234567890123456");
System.out.println(sBuffer.capacity()); // 16
sBuffer.append("123456789012345678");
System.out.println(sBuffer.capacity()); // 34
sBuffer.append("1");
System.out.println(sBuffer.capacity()); // 70

Recursive function in XCode Playground (Swift)

I'm learning recursive functions in Swift, and I did the following:
func recursive(i: Int) -> Int {
if i == 1 {
return 1
} else if i >= 2 {
return recursive(i: i - 1) + 1
}
return 0
}
I couldn't figure out why the function above is not working. I've tested it by doing the below doing print(recursive(10)), which gives me an output of 10. I expected the output to be 1. Can anyone help me with this? Thank you in advance.
I'm using Playgrounds on XCode 8.3.
When you do this:
recursive(i: i - 1) + 1
… then you are in effect decrementing i and then incrementing it again. That cancels out and you arrive at i again.
Let's write down what calculation would be done for i = 3:
(3 - 1) + 1 = ((2 - 1) + 1) + 1 = (((1) + 1) + 1) = 3
This is a perfect example of printing numbers without using any loop.
The recursive functions are very useful to handle such cases.
func printCount( count : inout Int , limit : Int) {
print(count, terminator: " ")
count += 1
if count > limit {
return
}
printCount(count: &count , limit: limit)
}
var count = 11
let limit = 20
printCount(count: &count , limit: limit)
Output : 11 12 13 14 15 16 17 18 19 20

Refactor for-loop statement to swift 3.0

I have following line in my code:
for (i = 0, j = count - 1; i < count; j = i++)
Can anyone help to remove the two compiler warnings, that i++ will be removed in Swift 3.0 and C-style for statement is depreciated?
You could use this:
var j = count-1
for i in 0..<count {
defer { j = i } // This will keep the cycle "logic" all together, similarly to "j = i++"
// Cycle body
}
EDIT
As #t0rst noted, be careful using defer, since it will be executed no matter how its enclosing scope is exited, so it isn't a 100% replacement.
So while the standard for ( forInit ; forTest ; forNext ) { … } will not execute forNext in case of a break statement inside the cycle, a return or an exception, the defer will.
Read here for more
Alternatively, lets go crazy to avoid having to declare j as external to the loop scope!
Snippet 1
let count = 10
for (i, j) in [count-1..<count, 0..<count-1].flatten().enumerate() {
print(i, j)
}
/* 0 9
1 0
2 1
3 2
4 3
5 4
6 5
7 6
8 7
9 8 */
Snippet 2
for (i, j) in (-1..<count-1).map({ $0 < 0 ? count-1 : $0 }).enumerate() {
print(i, j)
}
Trying to win the prize for the craziest solution in this thread
Snippet 1
extension Int {
func j(count:Int) -> Int {
return (self + count - 1) % count
}
}
for i in 0..<count {
print(i, i.j(count))
}
Snippet 2
let count = 10
let iList = 0..<count
let jList = iList.map { ($0 + count - 1) % count }
zip(iList, jList).forEach { (i, j) in
print(i, j)
}
You could use a helper function to abstract away the wrapping of j as:
func go(count: Int, block: (Int, Int) -> ()) {
if count < 1 { return }
block(0, count - 1)
for i in 1 ..< count {
block(i, i - 1)
}
}

Dynamic piping with FRP

Consider a problem:
split file by lines
write lines to a result file
if a result file exceeds some size create a new result file
For example, if I have a file which weights 4gb and split size is equal 1gb. The result is four files weights 1gb.
I'm looking for a solution with something like Rx*/Bacon or any other similar library in any language.
My solution in Coffee with Highland.js:
_ = require('underscore')
H = require('highland')
fs = require('fs')
debug = require('debug')
log = debug('main')
assert = require('assert')
readS = H(fs.createReadStream('walmart.dump')).map((buffer) ->
{ buffer: buffer }
)
MAX_SIZE = 10 ** 7
counter = 0
nextStream = ()->
stream = fs.createWriteStream("result/data#{counter}.txt")
wrapper = H.wrapCallback(stream.write.bind(stream))
counter += 1
return wrapper
debug('profile')('start')
s = readS.scan({
size: 0
stream: nextStream()
}, (acc, {buffer}) ->
debug('scan')(acc, buffer)
acc.size += buffer.length
acc.buffer = buffer
if acc.size > MAX_SIZE
debug('notify')(counter - 1, acc.size)
acc.size = 0
acc.stream = nextStream()
log(acc)
return acc
).filter((x)->x.buffer?)
s.parallel 4
s.flatMap((x) ->
debug('flatMap')(x)
x.stream(x.buffer)
)
.done -> debug('profile')('finish')
walmart.dump is a text file which contains 6gb of text. Splitting for 649 files takes:
profile start +0ms
profile finish +53s