Меню Закрыть

Java коллекции для бэктестера – в чем держать данные?

Учитывая, сколько алготрейдеру приходится гонять бэктестер, важно, чтобы он работал быстро.

Первый шаг для увеличения скорости – перевести цены в int. Держать цены в double вообще сомнительная практика, если не хотите, чтобы в глубинах вашего алго возникали значения типа 1724.999999999999997 с непредсказуемыми последствиями. Альтернативой int будет только BigDecimal, что нормально было бы для рабочего кода, но бэктестер выйдет жутко медленным.

Ок, цены в int. Тогда встает второй вопрос – в какой коллекции держать данные. Вроде бы, примитивный array должен быть самым быстрым, но работать с ним неудобно. Есть правда сторонние реализации коллекций на базе примитивов, например FastUtil, и сейчас потестируем, насколько они решают проблему.

Типовая для бэктестера задача – есть пол-миллиона точек, идем окном в 20 000 точек и делаем в окне что-то страшное. В тесте – просто суммируем и отсылаем в функцию.

Тестируем три варианта:

  1. ArrayList<Integer>.
  2. Контейнер IntArrayList от FastUtil.
  3. Примитивный array[int].

Делаем десять проходов для “прогрева” JVM, на последнем проходе получаем следующие результаты в миллисекундах:

(list) work time: 7626
(fastutil) work time: 2347
(array) work time: 2358

Результаты:

1. ArrayList<Integer> втрое медленнее контейнеров на примитивах. Что и следовало ожидать, в общем-то, boxing/unboxing.

2. Fastutil молодцы, задачу решают.

JVM: liberica-15

Код:


import it.unimi.dsi.fastutil.ints.IntArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class TestIntList {

    public static final int N = 500000;
    public static final int WINDOW = 20000;
    public static final int BOUND = 100000;


    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            testList();
            testFastUtil();
            testArray();
            System.out.println("******");
        }

    }

    private static void testFastUtil() {

        final IntArrayList values = new IntArrayList(N);
        final Random random = new Random(System.nanoTime());
        for (int i = 0; i < N; i++) {
            values.add(i, random.nextInt(BOUND));
        }

        final long startTime = System.nanoTime();

        for (int i = WINDOW - 1; i < N; i++) {
            int sum = 0;
            for (int j = i - WINDOW + 1; j <= i; j++) {
                sum += values.getInt(j);
            }
            doSomething(sum);
        }

        final long endTime = System.nanoTime();
        System.out.println("(fastutil) work time: " + (endTime - startTime) / 1000000);
    }


    private static void testList() {

        final List<Integer> values = new ArrayList<>(N);
        final Random random = new Random(System.nanoTime());
        for (int i = 0; i < N; i++) {
            values.add(i, random.nextInt(BOUND));
        }


        final long startTime = System.nanoTime();

        for (int i = WINDOW - 1; i < N; i++) {
            int sum = 0;
            for (int j = i - WINDOW + 1; j <= i; j++) {
                sum += values.get(j);
            }
            doSomething(sum);
        }

        final long endTime = System.nanoTime();
        System.out.println("(list) work time: " + (endTime - startTime) / 1000000);

    }


    private static void testArray() {

        final int[] values = new int[N];
        final Random random = new Random(System.nanoTime());
        for (int i = 0; i < N; i++) {
            values[i] = random.nextInt(BOUND);
        }

        final long startTime = System.nanoTime();

        for (int i = WINDOW - 1; i < N; i++) {
            int sum = 0;
            for (int j = i - WINDOW + 1; j <= i; j++) {
                sum += values[j];
            }
            doSomething(sum);
        }

        final long endTime = System.nanoTime();
        System.out.println("(array) work time: " + (endTime - startTime) / 1000000);
    }


    public static void doSomething(int sum) {
        if (sum < 0) System.out.println("wtf?");
    }

}


Подпишитесь на уведомления о новых постах: