package org.yamcs.parameter;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.utils.ValueUtility;
import org.yamcs.xtce.Parameter;
public class ParameterCacheTest {
Parameter p1 = new Parameter("p1");
Parameter p2 = new Parameter("p2");
@Test
public void test1() {
ParameterCacheConfig pcc = new ParameterCacheConfig(true, true, 1000, 4096);
ParameterCache pcache = new ParameterCache(pcc); //1 second
assertNull(pcache.getLastValue(p1));
ParameterValue p1v1 = getParameterValue(p1, 10);
ParameterValue p2v1 = getParameterValue(p2, 10);
pcache.update(Arrays.asList(p1v1, p2v1));
assertEquals(p1v1, pcache.getLastValue(p1));
assertEquals(p2v1, pcache.getLastValue(p2));
ParameterValue p1v2 = getParameterValue(p1, 20);
pcache.update(Arrays.asList(p1v2));
assertEquals(p1v2, pcache.getLastValue(p1));
assertEquals(p2v1, pcache.getLastValue(p2));
List<ParameterValue> pvlist = pcache.getValues(Arrays.asList(p1, p2));
checkEquals(pvlist, p1v2, p2v1);
pvlist = pcache.getValues(Arrays.asList(p2, p1));
checkEquals(pvlist, p2v1, p1v1);
}
@Test
public void testCircularity() {
ParameterCacheConfig pcc = new ParameterCacheConfig(true, true, 1000, 4096);
ParameterCache pcache = new ParameterCache(pcc); //1 second
assertNull(pcache.getLastValue(p1));
List<ParameterValue> expectedPVlist = new ArrayList<>();
for(int i=0;i<10;i++) {
ParameterValue pv = getUint64ParameterValue(p1, i*10L);
expectedPVlist.add(pv);
pcache.update(Arrays.asList(pv));
}
List<ParameterValue> pvlist = pcache.getAllValues(p1);
assertEquals(10, pvlist.size());
for(int i=0; i<10; i++) {
assertEquals(expectedPVlist.get(9-i), pvlist.get(i));
}
for(int i=10;i<128;i++) {
ParameterValue pv = getUint64ParameterValue(p1, i*10L);
expectedPVlist.add(pv);
pcache.update(Arrays.asList(pv));
}
pvlist = pcache.getAllValues(p1);
assertEquals(128, pvlist.size());
for(int i=0; i<128; i++) {
assertEquals(expectedPVlist.get(127-i), pvlist.get(i));
}
ParameterValue pv = getUint64ParameterValue(p1, 128*10L);
pcache.update(Arrays.asList(pv));
expectedPVlist.add(pv);
pvlist = pcache.getAllValues(p1);
assertEquals(128, pvlist.size());
for(int i=0; i<128; i++) {
assertEquals(expectedPVlist.get(128-i), pvlist.get(i));
}
}
@Test
public void testResize() {
ParameterCacheConfig pcc = new ParameterCacheConfig(true, true, 2000, 4096);
ParameterCache pcache = new ParameterCache(pcc); //should keep at least 200 samples
assertNull(pcache.getLastValue(p1));
List<ParameterValue> expectedPVlist = new ArrayList<>();
for(int i=0;i<256;i++) {
ParameterValue pv = getUint64ParameterValue(p1, i*10L);
expectedPVlist.add(pv);
pcache.update(Arrays.asList(pv));
}
List<ParameterValue> pvlist = pcache.getAllValues(p1);
assertEquals(256, pvlist.size());
for(int i=0; i<256; i++) {
assertEquals(expectedPVlist.get(255-i), pvlist.get(i));
}
ParameterValue pv = getUint64ParameterValue(p1, 256*10L);
pcache.update(Arrays.asList(pv));
expectedPVlist.add(pv);
pv = getUint64ParameterValue(p1, 257*10L);
pcache.update(Arrays.asList(pv));
expectedPVlist.add(pv);
pvlist = pcache.getAllValues(p1);
assertEquals(256, pvlist.size());
for(int i=0; i<256; i++) {
assertEquals(expectedPVlist.get(257-i), pvlist.get(i));
}
}
@Test
public void testMaxSize() {
ParameterCacheConfig pcc = new ParameterCacheConfig(true, true, 2000, 128);
ParameterCache pcache = new ParameterCache(pcc); //should keep max 128 samples
assertNull(pcache.getLastValue(p1));
List<ParameterValue> expectedPVlist = new ArrayList<>();
for(int i=0;i<256;i++) {
ParameterValue pv = getUint64ParameterValue(p1, i*10L);
expectedPVlist.add(pv);
pcache.update(Arrays.asList(pv));
}
List<ParameterValue> pvlist = pcache.getAllValues(p1);
assertEquals(128, pvlist.size());
for(int i=0; i<128; i++) {
assertEquals(expectedPVlist.get(255-i), pvlist.get(i));
}
ParameterValue pv = getUint64ParameterValue(p1, 256*10L);
pcache.update(Arrays.asList(pv));
expectedPVlist.add(pv);
pv = getUint64ParameterValue(p1, 257*10L);
pcache.update(Arrays.asList(pv));
expectedPVlist.add(pv);
pvlist = pcache.getAllValues(p1);
assertEquals(128, pvlist.size());
for(int i=0; i<128; i++) {
assertEquals(expectedPVlist.get(257-i), pvlist.get(i));
}
}
/**
*
* Perforamnce tests for different syncrhonization strategies in ParameterCache.CacheEntry
*
* Results on Quad Core Intel(R) Core(TM) i7-4610M CPU @ 3.00GHz
* Ubuntu 14.04
* java version "1.8.0_66"
* Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
* Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
*
* 1. synchronised CacheEntry.getAll and CacheEntry.add
*
* writer time 42.470 s
* totalReadTime: 334.741
*
*
* 2. Synchronised CacheEntry.add and unsynchronised CacheEntry.getAll with volatile elements and tail
* writer time: 14.603 ms
* totalReadTime: 110.966 ms
*
*
* 3. writeLock in CacheEntry.add and readLock in CacheEntry.getAll
* writer time: 19.329 ms
* totalReadTime: 125.052 ms
*
* Due to these results, and the fact that locking offers the advantage that getAll gives correctly sorted results,
* we have selected the read/write lock implementation
*
*
*/
static int numWrites = 100000;
static int numReads = 1000000;
static int numParam = 300;
static int numReaders = 10;
@Test
@Ignore
public void testConcurrency() throws InterruptedException {
ParameterCacheConfig pcc = new ParameterCacheConfig(true, true, 1000, 4096);
final ParameterCache pcache = new ParameterCache(pcc);
Thread writer = new Thread(new Runnable() {
@Override
public void run() {
long t0 = System.currentTimeMillis();
List<Parameter> plist = new ArrayList<Parameter>();
for(int i=2;i<numParam; i++) {
plist.add(new Parameter("p"+i));
}
for(long t=0; t<numWrites*10; t+=10) {
ArrayList<ParameterValue> pvList = new ArrayList<ParameterValue>();
pvList.add(getUint64ParameterValue(p1,t));
for (int i=2; i<numParam; i++) {
pvList.add(getUint64ParameterValue(plist.get(i-2),t));
}
pcache.update(pvList);
}
long t1 = System.currentTimeMillis();
System.out.println("writer finished in "+(t1-t0)+" ms");
}
});
writer.start();
Thread.sleep(100);
CacheReader[] readers = new CacheReader[numReaders];
for(int i=0; i<numReaders;i++) {
readers[i] = new CacheReader(i, pcache, p1);
new Thread(readers[i]).start();
}
writer.join();
long totalReadTime =0;
for(CacheReader r:readers) {
totalReadTime+=r.runningTime;
}
System.out.println("totalReadTime: "+totalReadTime);
}
static class CacheReader implements Runnable {
ParameterCache pcache;
Parameter p1;
int x;
long runningTime;
CacheReader(int x, ParameterCache pcache, Parameter p1) {
this.p1 = p1;
this.pcache = pcache;
this.x = x;
}
@Override
public void run() {
long t0 = System.currentTimeMillis();
int wc =0;
for(long t=0; t<numReads*10; t+=10) {
List<ParameterValue> pvlist = pcache.getAllValues(p1);
// System.out.println("reader received "+pvlist);
if(pvlist!=null) {
// System.out.println("reader received: "+pvlist.size());
ParameterValue pv = pvlist.get(0);
for(int i=1;i<pvlist.size();i++) {
ParameterValue pv1 = pvlist.get(i);
long pvt = pv.getAcquisitionTime();
long pv1t = pv1.getAcquisitionTime();
if(pv1t>pvt) {
wc++;
// System.out.println("reader "+x+" "+t+" wrong order: pv: "+pvt+" pv1: "+pv1t+" diff: "+(pv1t-pvt)/10);
}
pv = pv1;
}
}
}
long t1 = System.currentTimeMillis();
runningTime = t1-t0;
System.out.println("reader "+x+" finished in "+runningTime+" ms, wrong order count: "+wc);
}
}
ParameterValue getUint64ParameterValue(Parameter p, long t) {
ParameterValue pv = new ParameterValue(p);
pv.setGenerationTime(t);
pv.setEngineeringValue(ValueUtility.getUint64Value(t));
return pv;
}
ParameterValue getParameterValue(Parameter p, long timestamp) {
ParameterValue pv = new ParameterValue(p);
pv.setGenerationTime(timestamp);
pv.setEngineeringValue(ValueUtility.getStringValue(p.getName()+"_"+timestamp));
return pv;
}
public static void checkEquals(List<ParameterValue> actual, ParameterValue... expected) {
assertEquals(expected.length, actual.size());
for(int i=0; i<expected.length; i++) {
ParameterValue pv = expected[i];
assertEquals(pv, actual.get(i));
}
}
}