当前位置: 首页 > Thinking in Java > 正文

第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 | 边城网事

该日志由 边城网事 于2015年03月18日发表在 Thinking in Java 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 第17章 – 深入研究容器 – Collection(List,Set,Queue)的性能测试框架(单线程中)(P501) | 边城网事

第17章 – 深入研究容器 – Collection(List,Set,Queue)的性能测试框架(单线程中)(P501) 暂无评论

发表评论

快捷键:Ctrl+Enter