/*
* Copyright (C) 2012 Facebook, Inc.
*
* 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 com.facebook.stats;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.facebook.logging.Logger;
import com.facebook.stats.topk.TopK;
import static org.testng.Assert.assertEquals;
public abstract class TestIntegerTopK {
// few hundred millis each test to keep tests short
private static final long TEST_TIME_NANOS = TimeUnit.MILLISECONDS.toNanos(1250);
private static Logger LOG;
@BeforeMethod(alwaysRun = true)
public void setUp() throws Exception {
LOG = getLogger();
}
protected abstract TopK<Integer> getInstance(int keySpaceSize, int k);
/**
* defers logger creation so we log based on subclass name; may do efficient caching
*
* @return Logger
*/
protected abstract Logger getLogger();
@Test(groups = "fast")
public void testTop3() {
TopK<Integer> topK = getInstance(10, 3);
assertTopK(topK);
topK.add(1, 3);
assertTopK(topK, 1);
topK.add(2, 2);
assertTopK(topK, 1, 2);
topK.add(3, 8);
assertTopK(topK, 3, 1, 2);
topK.add(4, 1);
assertTopK(topK, 3, 1, 2);
topK.add(4, 3);
assertTopK(topK, 3, 4, 1);
topK.add(2, 3);
assertTopK(topK, 3, 2, 4);
}
@Test(groups = "fast",
expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = "count to add must be non-negative, got -3")
public void testAddNegative() {
TopK<Integer> topK = getInstance(10, 3);
topK.add(0, -3);
}
@Test(groups = "fast",
expectedExceptions = NullPointerException.class,
expectedExceptionsMessageRegExp = "key can't be null")
public void testNullKey() {
TopK<Integer> topK = getInstance(10, 3);
topK.add(null, 1);
}
@Test(groups = "slow")
public void testInsertionTiming() {
int keySpaceSize = 10000;
int k = 100;
int maxAdd = 100;
TopK<Integer> topK = getInstance(keySpaceSize, k);
LOG.info("Timing add() performance with keySpaceSize = %d, k = %d", keySpaceSize, k);
Random random = new Random(0);
long totalTime = 0;
long count = 0;
long begin = System.nanoTime();
while (System.nanoTime() - begin < TEST_TIME_NANOS) {
long start = System.nanoTime();
topK.add(random.nextInt(keySpaceSize), random.nextInt(maxAdd));
if (System.nanoTime() - begin > TimeUnit.SECONDS.toNanos(1)) {
// discard the first second of measurements
totalTime += System.nanoTime() - start;
++count;
}
}
LOG.info(
"Processed %d entries in %d ms. Insertion rate = %f entries/s",
count,
TimeUnit.NANOSECONDS.toMillis(totalTime),
count / (totalTime * 1.0 / TimeUnit.SECONDS.toNanos(1))
);
}
@Test(groups = "slow")
public void testRetrievalTiming() {
int keySpaceSize = 10000;
int k = 100;
int maxAdd = 100;
TopK<Integer> topK = getInstance(keySpaceSize, k);
LOG.info("Timing getTopK() performance with keySpaceSize = %d, k = %d", keySpaceSize, k);
Random random = new Random(0);
long totalTime = 0;
long count = 0;
long begin = System.nanoTime();
while (System.nanoTime() - begin < TEST_TIME_NANOS) {
topK.add(random.nextInt(keySpaceSize), random.nextInt(maxAdd));
long start = System.nanoTime();
topK.getTopK();
if (System.nanoTime() - begin > TimeUnit.SECONDS.toNanos(1)) {
// discard the first second of measurements
totalTime += System.nanoTime() - start;
++count;
}
}
LOG.info(
"Processed %d entries in %d ms. Retrieval rate = %f retrievals/s",
count,
TimeUnit.NANOSECONDS.toMillis(totalTime),
count / (totalTime * 1.0 / TimeUnit.SECONDS.toNanos(1))
);
}
private static void assertTopK(TopK<Integer> topK, Integer... expected) {
assertEquals(topK.getTopK(), Arrays.asList(expected));
}
}