Эффективно передавать функции Clojure в java

Я хотел бы реализовать наивный неленивый map в Java с циклом Java. Меня больше всего беспокоит вызов функций в java из Clojure.

Вот мой код:

Класс под названием NaiveClojure для реализации функций с использованием Java.

package java_utils;

import java_utils.ApplyFn;

public class NaiveClojure {

    public static Object[] map (ApplyFn applyfn, Object function, Object[] coll) {

        int len = coll.length;

        for (int i = 0 ; i < len ; i++) {
            coll[i] = applyfn.apply(function, coll[i]);
        }

        return coll;
    }
}

Абстрактный класс с именем ApplyFn

package java_utils;

public abstract class ApplyFn {

  public abstract Object apply (Object function, Object value);

}

Итак, в Clojure у меня есть

(defn java-map [f coll]
  (let [java-fn (proxy [ApplyFn] []
                  (apply [f x]
                         (f x)))]
    (seq (NaiveClojure/map java-fn f (to-array coll)))))

Я старался

(doall (map inc (range 0 10000))) ;; 3.4 seconds for 10000 operations
(java-map inc (range 0 10000) ;; 5.4 seconds

Моя цель не в том, чтобы превзойти map (я реализовал это в качестве примера), я просто хочу делать такие вещи с конкретными функциями (а не изобретать велосипед Clojure).

Есть ли лучший способ передать такие функции? (как простой и быстрый способ) И чтобы улучшить мой код в целом (у меня плохие теоретические знания), вы знаете, что здесь убивает производительность? Я бы сказал, что общий ввод типа Object, но я не вижу ничего другого

Спасибо


person Joseph Yourine    schedule 07.10.2016    source источник
comment
Это ненадежный способ измерения производительности (проверьте критерий), и замедление менее чем в 2 раза вряд ли «убивает производительность». Единственный факт, что вы оборачиваете функции Clojure, может объяснить, почему они работают медленнее (поскольку вы выполняете больше работы).   -  person Valentin Waeselynck    schedule 07.10.2016
comment
О, спасибо за имя библиотеки, на самом деле похоже, что моя функция работает быстрее, чем карта clojure для последовательностей как со временем, так и со скамейкой критериев. Мне просто интересно, даст ли использование IFn или лучший подход лучшие результаты.   -  person Joseph Yourine    schedule 07.10.2016
comment
Если это быстрее, это, вероятно, потому, что вы обновляете массив на месте (что не имеет ту же семантику, что и clojure.core/map !)   -  person Valentin Waeselynck    schedule 07.10.2016
comment
Да, конечно, я хочу использовать это для достижения точной локальной работы, которая не нуждается в гибкости и возможностях карты Clojure.   -  person Joseph Yourine    schedule 10.10.2016


Ответы (1)


Здесь у вас нет причин для беспокойства, то, как вы это делаете, прекрасно и эффективно.

coll[i] = applyfn.apply(function, coll[i]);

Это вполне нормальный способ. При измерении, пожалуйста, как указывает Валентин Васелинк, не забывайте использовать надежную функцию микробенчмаркинга, а также имейте в виду, что бенчмаркинг такого небольшого фрагмента кода в отдельности сложен.

Когда вы создаете функцию clojure, она создает «обычный» класс Java с методом, называемым apply. Вызов будет не медленнее, потому что вы вызываете функцию, изначально написанную на Clojure, чем вызов метода класса, написанного с использованием обычного синтаксиса Java. Как только Hotspot JIT завершит его прогрев и встраивание, он, скорее всего, будет работать так же быстро, как и без вызова метода (именно поэтому бенчмаркинг такого рода вещей сложнее, чем это должно быть интуитивно).

person Arthur Ulfeldt    schedule 07.10.2016
comment
Спасибо за ответ, такое дело делаю впервые. Я, вероятно, попытаюсь написать методы для примитивов, чтобы улучшить скорость на определенных данных. - person Joseph Yourine; 10.10.2016