第17章 – 深入研究容器 – Collection(List,Set,Queue)的性能测试框架(单线程中)(P501)
第17章 – 深入研究容器 – Collection(List,Set,Queue)的性能测试框架(单线程中)(P501)
1. Test抽象类,和测试参数类 TestParam
一个Test表示一个具体的测试.
String name; 表示这个测试的名称,比如可以测试add方法的耗时.
Test的抽象方法test表示具体运行测试的方法,返回一个int值.
test方法需要传入两个参数C container, TestParam tp,泛型C表示当前测试的容器,
TestParam 类有两个参数size和loop,size表示参与测试的额容器的某种尺寸,
而loop表示测试中循环执行的次数.
TestParam方法中 提供方法public static TestParam[] array(int… values)
将 一个一维属性转换成TestParam对象数组.
一维数组格式[10,5000,100,500,1000,50] 表示size=10,循环5000次,
size=100,循环500次,size=1000,循环500次
这样执行array方法后,就返回一个TestParam[] 包含3个TestParam对象.
2. 测试器类Tester
这个类构造时需要传入测试的容器,测试数组(Test数组) 和 测试参数数组(TestParam数组)
Tester.timedTest() 分别按照TestParam数组中执行Test数组中每个Test的test()方法,(一个双重循环)
并且打印每种参数(TestParam)下面各种Test的 执行test的耗时/test的返回值
执行 结果类似下面这样:
——————– ArrayList for String ——————–
size add get set iteradd insert remove sort
10 627 424 116 136 2927 201 707
100 398 268 105 163 5369 7 152
1000 379 119 221 1119 1944 0 267
10000 437 150 157 7473 10627 0 243
其中 ArrayList for String 是Test的HeaderLine
size表示TestParam中的size大小.add,get等就是一Test中的name.里面是数字表示执行test的耗时/test的返回值
3. 实际使用
可以创建Tester的子类
比如ListPerformance 中就创建了子类 static class ListTester extends Tester<List<Integer>>
表示进行List<Integer>的各种测试(这里配套的Test的数组tests也必须是针对List<Integer>的)
构造函数为:
public ListTester(List<Integer> container, List<Test<List<Integer>>> tests)
{
super(container, tests);
}
表示一个具体的List<Integer>,和List<Test<List<Integer>>> 即可.
还提供了方法:
public static void run(List<Integer> list, List<Test<List<Integer>>> tests)
{
new ListTester(list, tests).timedTest();
}
这样可以调用下面的方法测试
ListTester.run(new ArrayList<Integer>(), tests);
这里ListTester和相应的Test数组tests 可以测试各种元素为Integer的List,
但是如果要测试中元素为String的List时,就必须重新弄一个Test的数组stests
(static List<Test<List<String>>> stests = new ArrayList<Test<List<String>>>();)
总之运行Tester要遵循C container, List<Test<C>> tests的泛型约束.
Test<C> 数组test中的C最多可以实现一类容器的通用,比如List,Queue,Set.
公共代码:
package containers; public abstract class Test<C> { String name; public Test(String name) { this.name = name; } // Override this method for different tests. // Returns actual number of repetitions of test. abstract int test(C container, TestParam tp); }
package containers; public class TestParam { public final int size; public final int loops; public TestParam(int size, int loops) { this.size = size; this.loops = loops; } // Create an array of TestParam from a varargs sequence: public static TestParam[] array(int... values) { int size = values.length / 2; TestParam[] result = new TestParam[size]; int n = 0; for (int i = 0; i < size; i++) result[i] = new TestParam(values[n++], values[n++]); return result; } // Convert a String array to a TestParam array: public static TestParam[] array(String[] values) { int[] vals = new int[values.length]; for (int i = 0; i < vals.length; i++) vals[i] = Integer.decode(values[i]); return array(vals); } } // /:~
package containers; //: containers/Tester.java // Applies Test objects to lists of different containers. import java.util.*; public class Tester<C> { public static int fieldWidth = 8; public static TestParam[] defaultParams = TestParam.array(10, 5000, 100, 5000, 1000, 5000, 10000, 500); // Override this to modify pre-test initialization: protected C initialize(int size) { return container; } protected C container; private String headline = ""; private List<Test<C>> tests; private static String stringField() { return "%" + fieldWidth + "s"; } private static String numberField() { return "%" + fieldWidth + "d"; } private static int sizeWidth = 5; private static String sizeField = "%" + sizeWidth + "s"; private TestParam[] paramList = defaultParams; public Tester(C container, List<Test<C>> tests) { this.container = container; this.tests = tests; if (container != null) headline = container.getClass().getSimpleName(); } public Tester(C container, List<Test<C>> tests, TestParam[] paramList) { this(container, tests); this.paramList = paramList; } public void setHeadline(String newHeadline) { headline = newHeadline; } // Generic methods for convenience : public static <C> void run(C cntnr, List<Test<C>> tests) { new Tester<C>(cntnr, tests).timedTest(); } public static <C> void run(C cntnr, List<Test<C>> tests, TestParam[] paramList) { new Tester<C>(cntnr, tests, paramList).timedTest(); } private void displayHeader() { // Calculate width and pad with '-': int width = fieldWidth * tests.size() + sizeWidth; int dashLength = width - headline.length() - 1; StringBuilder head = new StringBuilder(width); for (int i = 0; i < dashLength / 2; i++) head.append('-'); head.append(' '); head.append(headline); head.append(' '); for (int i = 0; i < dashLength / 2; i++) head.append('-'); System.out.println(head); // Print column headers: System.out.format(sizeField, "size"); for (Test test : tests) System.out.format(stringField(), test.name); System.out.println(); } // Run the tests for this container: public void timedTest() { displayHeader(); for (TestParam param : paramList) { System.out.format(sizeField, param.size); for (Test<C> test : tests) { C kontainer = initialize(param.size); long start = System.nanoTime(); // Call the overriden method: int reps = test.test(kontainer, param); long duration = System.nanoTime() - start; long timePerRep = duration / reps; // Nanoseconds System.out.format(numberField(), timePerRep); } System.out.println(); } } }
package containers; // : containers/MapPerformance.java // Demonstrates performance differences in Maps. // {Args: 100 5000} Small to keep build testing short import java.util.*; public class MapPerformance { static List<Test<Map<Integer, Integer>>> tests = new ArrayList<Test<Map<Integer, Integer>>>(); static { tests.add(new Test<Map<Integer, Integer>>("put") { int test(Map<Integer, Integer> map, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { map.clear(); for (int j = 0; j < size; j++) map.put(j, j); } return loops * size; } }); tests.add(new Test<Map<Integer, Integer>>("get") { int test(Map<Integer, Integer> map, TestParam tp) { int loops = tp.loops; int span = tp.size * 2; for (int i = 0; i < loops; i++) for (int j = 0; j < span; j++) map.get(j); return loops * span; } }); tests.add(new Test<Map<Integer, Integer>>("iterate") { int test(Map<Integer, Integer> map, TestParam tp) { int loops = tp.loops * 10; for (int i = 0; i < loops; i++) { Iterator it = map.entrySet().iterator(); while (it.hasNext()) it.next(); } return loops * map.size(); } }); } public static void main(String[] args) { if (args.length > 0) Tester.defaultParams = TestParam.array(args); Tester.run(new TreeMap<Integer, Integer>(), tests); Tester.run(new HashMap<Integer, Integer>(), tests); Tester.run(new LinkedHashMap<Integer, Integer>(), tests); Tester.run(new IdentityHashMap<Integer, Integer>(), tests); Tester.run(new WeakHashMap<Integer, Integer>(), tests); Tester.run(new Hashtable<Integer, Integer>(), tests); Map<String,Integer> aMap = Collections.synchronizedMap(new HashMap<String,Integer>()); } }
3.1 各种List的测试(依赖 Thinking in Java 源代码中的net.mindview.util包中的类)
Queue使用LinkedList实现的,也在这里面测试.
package containers; // : containers/ListPerformance.java // Demonstrates performance differences in Lists. // {Args: 100 500} Small to keep build testing short import java.util.*; import net.mindview.util.*; public class ListPerformance { static Random rand = new Random(); static int reps = 1000; static List<Test<List<Integer>>> tests = new ArrayList<Test<List<Integer>>>(); static List<Test<LinkedList<Integer>>> qTests = new ArrayList<Test<LinkedList<Integer>>>(); static List<Test<List<String>>> stests = new ArrayList<Test<List<String>>>(); static { stests.add(new Test<List<String>>("add") { int test(List<String> list, TestParam tp) { int loops = tp.loops; int listSize = tp.size; Generator<String> g = new CountingGenerator.String(); for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < listSize; j++) { list.add(g.next()); } } return loops * listSize; } }); stests.add(new Test<List<String>>("get") { int test(List<String> list, TestParam tp) { int loops = tp.loops; int listSize = tp.size; for (int i = 0; i < loops; i++) list.get(rand.nextInt(listSize)); return loops; } }); stests.add(new Test<List<String>>("set") { int test(List<String> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for (int i = 0; i < loops; i++) list.set(rand.nextInt(listSize), "test"); return loops; } }); stests.add(new Test<List<String>>("iteradd") { int test(List<String> list, TestParam tp) { final int LOOPS = 1000000; int half = list.size() / 2; ListIterator<String> it = list.listIterator(half); for (int i = 0; i < LOOPS; i++) it.add("test"); return LOOPS; } }); stests.add(new Test<List<String>>("insert") { int test(List<String> list, TestParam tp) { int loops = tp.loops; for (int i = 0; i < loops; i++) list.add(5, "test"); // Minimize random-access // cost return loops; } }); stests.add(new Test<List<String>>("remove") { int test(List<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; Generator<String> g = new CountingGenerator.String(); for (int i = 0; i < loops; i++) { list.clear(); list.add(g.next()); while (list.size() > 5) list.remove(5); // Minimize } return loops * size; } }); stests.add(new Test<List<String>>("sort") { int test(List<String> list, TestParam tp) { int loops = tp.loops; int size = tp.size; Generator<String> g = new CountingGenerator.String(); for (int i = 0; i < loops; i++) { Collections.sort(list); } return loops * size; } }); // ----------------- tests.add(new Test<List<Integer>>("add") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; int listSize = tp.size; for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < listSize; j++) list.add(j); } return loops * listSize; } }); tests.add(new Test<List<Integer>>("get") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for (int i = 0; i < loops; i++) list.get(rand.nextInt(listSize)); return loops; } }); tests.add(new Test<List<Integer>>("set") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for (int i = 0; i < loops; i++) list.set(rand.nextInt(listSize), 47); return loops; } }); tests.add(new Test<List<Integer>>("iteradd") { int test(List<Integer> list, TestParam tp) { final int LOOPS = 1000000; int half = list.size() / 2; ListIterator<Integer> it = list.listIterator(half); for (int i = 0; i < LOOPS; i++) it.add(47); return LOOPS; } }); tests.add(new Test<List<Integer>>("insert") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; for (int i = 0; i < loops; i++) list.add(5, 47); // Minimize random-access // cost return loops; } }); tests.add(new Test<List<Integer>>("remove") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); list.addAll(new CountingIntegerList(size)); while (list.size() > 5) list.remove(5); // Minimize // random-access // cost } return loops * size; } }); tests.add(new Test<List<Integer>>("sort") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { Collections.sort(list); } return loops * size; } }); // Tests for queue behavior: qTests.add(new Test<LinkedList<Integer>>("addFirst") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < size; j++) list.addFirst(47); } return loops * size; } }); qTests.add(new Test<LinkedList<Integer>>("addLast") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < size; j++) list.addLast(47); } return loops * size; } }); qTests.add(new Test<LinkedList<Integer>>("rmFirst") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); list.addAll(new CountingIntegerList(size)); while (list.size() > 0) list.removeFirst(); } return loops * size; } }); qTests.add(new Test<LinkedList<Integer>>("rmLast") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); list.addAll(new CountingIntegerList(size)); while (list.size() > 0) list.removeLast(); } return loops * size; } }); } static class ListTester extends Tester<List<Integer>> { public ListTester(List<Integer> container, List<Test<List<Integer>>> tests) { super(container, tests); } // Fill to the appropriate size before each test: @Override protected List<Integer> initialize(int size) { container.clear(); container.addAll(new CountingIntegerList(size)); return container; } // Convenience method: public static void run(List<Integer> list, List<Test<List<Integer>>> tests) { new ListTester(list, tests).timedTest(); } } static class ListTester1 extends Tester<List<String>> { public ListTester1(List<String> container, List<Test<List<String>>> tests) { super(container, tests); } // Fill to the appropriate size before each test: @Override protected List<String> initialize(int size) { container.clear(); Generator<String> sg = new CountingGenerator.String(); for (int i = 0; i < size; i++) { container.add(sg.next()); } return container; } // Convenience method: public static void run(List<String> list, List<Test<List<String>>> tests) { new ListTester1(list, tests).timedTest(); } } public static void main(String[] args) { if (args.length > 0) Tester.defaultParams = TestParam.array(args); // Can only do these two tests on an array: Tester<List<Integer>> arrayTest = new Tester<List<Integer>>(null, tests.subList(1, 3)) { // This will be called before each test. It // produces a non-resizeable array-backed list: @Override protected List<Integer> initialize(int size) { Integer[] ia = Generated.array(Integer.class, new CountingGenerator.Integer(), size); return Arrays.asList(ia); } }; arrayTest.setHeadline("Array as List"); arrayTest.timedTest(); Tester.defaultParams = TestParam.array(10, 5000, 100, 5000, 1000, 1000, 10000, 200); if (args.length > 0) Tester.defaultParams = TestParam.array(args); ListTester1 lst1 = new ListTester1(new ArrayList<String>(), stests); lst1.setHeadline("ArrayList for String"); lst1.timedTest(); ListTester.run(new ArrayList<Integer>(), tests); ListTester.run(new LinkedList<Integer>(), tests); ListTester.run(new Vector<Integer>(), tests); Tester.fieldWidth = 12; Tester<LinkedList<Integer>> qTest = new Tester<LinkedList<Integer>>(new LinkedList<Integer>(), qTests); qTest.setHeadline("Queue tests"); qTest.timedTest(); } }
3.2 各种Set测试
package containers; // : containers/SetPerformance.java // Demonstrates performance differences in Sets. // {Args: 100 5000} Small to keep build testing short import java.util.*; public class SetPerformance { static List<Test<Set<Integer>>> tests = new ArrayList<Test<Set<Integer>>>(); static { tests.add(new Test<Set<Integer>>("add") { int test(Set<Integer> set, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { set.clear(); for (int j = 0; j < size; j++) set.add(j); } return loops * size; } }); tests.add(new Test<Set<Integer>>("contains") { int test(Set<Integer> set, TestParam tp) { int loops = tp.loops; int span = tp.size * 2; for (int i = 0; i < loops; i++) for (int j = 0; j < span; j++) set.contains(j); return loops * span; } }); tests.add(new Test<Set<Integer>>("iterate") { int test(Set<Integer> set, TestParam tp) { int loops = tp.loops * 10; for (int i = 0; i < loops; i++) { Iterator<Integer> it = set.iterator(); while (it.hasNext()) it.next(); } return loops * set.size(); } }); } public static void main(String[] args) { if (args.length > 0) Tester.defaultParams = TestParam.array(args); Tester.fieldWidth = 10; Tester.run(new TreeSet<Integer>(), tests); Tester.run(new HashSet<Integer>(), tests); Tester.run(new LinkedHashSet<Integer>(), tests); } }
3.3 各种Map测试
package containers; // : containers/MapPerformance.java // Demonstrates performance differences in Maps. // {Args: 100 5000} Small to keep build testing short import java.util.*; public class MapPerformance { static List<Test<Map<Integer, Integer>>> tests = new ArrayList<Test<Map<Integer, Integer>>>(); static { tests.add(new Test<Map<Integer, Integer>>("put") { int test(Map<Integer, Integer> map, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { map.clear(); for (int j = 0; j < size; j++) map.put(j, j); } return loops * size; } }); tests.add(new Test<Map<Integer, Integer>>("get") { int test(Map<Integer, Integer> map, TestParam tp) { int loops = tp.loops; int span = tp.size * 2; for (int i = 0; i < loops; i++) for (int j = 0; j < span; j++) map.get(j); return loops * span; } }); tests.add(new Test<Map<Integer, Integer>>("iterate") { int test(Map<Integer, Integer> map, TestParam tp) { int loops = tp.loops * 10; for (int i = 0; i < loops; i++) { Iterator it = map.entrySet().iterator(); while (it.hasNext()) it.next(); } return loops * map.size(); } }); } public static void main(String[] args) { if (args.length > 0) Tester.defaultParams = TestParam.array(args); Tester.run(new TreeMap<Integer, Integer>(), tests); Tester.run(new HashMap<Integer, Integer>(), tests); Tester.run(new LinkedHashMap<Integer, Integer>(), tests); Tester.run(new IdentityHashMap<Integer, Integer>(), tests); Tester.run(new WeakHashMap<Integer, Integer>(), tests); Tester.run(new Hashtable<Integer, Integer>(), tests); Map<String,Integer> aMap = Collections.synchronizedMap(new HashMap<String,Integer>()); } }
附件是代码中依赖的工具包代码

微信赞赏
支付宝赞赏
本文固定链接: https://www.jack-yin.com/coding/thinking-in-java/2109.html | 边城网事