Почему аннотации под Android такие проблемы производительности (медленные)?

Я ведущий автор ORMLite, который использует аннотации Java для классов для построения схем баз данных. Большая проблема производительности запуска для нашего пакета оказывается вызовом методов аннотации в Android 1.6. Я вижу такое же поведение до 3,0.

Мы видим, что следующий простой код аннотации невероятно насыщен GC и является реальной проблемой производительности. 1000 вызовов метода аннотации занимает почти секунду на быстром Android-устройстве. Тот же код, который работает на моем Macbook Pro, может выполнять 28 миллионов (sic) вызовов в одно и то же время. У нас есть аннотация, в которой есть 25 методов, и мы хотели бы сделать более 50 из них в секунду.

Кто-нибудь знает, почему это происходит, и если есть какая-то работа? Есть вещи, которые ORMLite может сделать с точки зрения кеширования этой информации, но есть ли что-нибудь, что мы можем сделать, чтобы «исправить» аннотации под Android? Благодарю.

public void testAndroidAnnotations() throws Exception { Field field = Foo.class.getDeclaredField("field"); MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class); long before = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) myAnnotation.foo(); Log.i("test", "in " + (System.currentTimeMillis() - before) + "ms"); } @Target(FIELD) @Retention(RUNTIME) private static @interface MyAnnotation { String foo(); } private static class Foo { @MyAnnotation(foo = "bar") String field; } 

Это приводит к следующей записи журнала:

 I/TestRunner( 895): started: testAndroidAnnotations D/dalvikvm( 895): GC freed 6567 objects / 476320 bytes in 85ms D/dalvikvm( 895): GC freed 8951 objects / 599944 bytes in 71ms D/dalvikvm( 895): GC freed 7721 objects / 524576 bytes in 68ms D/dalvikvm( 895): GC freed 7709 objects / 523448 bytes in 73ms I/test ( 895): in 854ms 

РЕДАКТИРОВАТЬ:

После того, как @candrews указал мне в правильном направлении, я немного проговорил над кодом. Проблема производительности выглядит вызванной каким-то ужасным, грубым кодом в Method.equals() . Он вызывает метод toString() обоих методов, а затем сравнивает их. Каждый toString() использует StringBuilder с кучей методов добавления без хорошего размера инициализации. Выполнение .equals путем сравнения полей будет значительно быстрее.

РЕДАКТИРОВАТЬ:

Мне понравилось интересное улучшение характеристик отражения. Мы теперь используем рефлексию, чтобы заглянуть внутрь класса AnnotationFactory чтобы прочитать список полей напрямую. Это делает класс отражения в 20 раз быстрее для нас, поскольку он обходит вызов, который использует метод method.equals() . Это не общее решение, а вот код Java из репозитория ORMLite SVN . Для общего решения см . Ответ Янченко ниже .

Solutions Collecting From Web of "Почему аннотации под Android такие проблемы производительности (медленные)?"

Google подтвердила эту проблему и исправила ее «post-Honeycomb»,

https://code.google.com/p/android/issues/detail?id=7811

По крайней мере, они знают об этом и предположительно исправили его для какой-то будущей версии.

Вот общая версия идеи Gray 's & user931366 :

 public class AnnotationElementsReader { private static Field elementsField; private static Field nameField; private static Method validateValueMethod; public static HashMap<String, Object> getElements(Annotation annotation) throws Exception { HashMap<String, Object> map = new HashMap<String, Object>(); InvocationHandler handler = Proxy.getInvocationHandler(annotation); if (elementsField == null) { elementsField = handler.getClass().getDeclaredField("elements"); elementsField.setAccessible(true); } Object[] annotationMembers = (Object[]) elementsField.get(handler); for (Object annotationMember : annotationMembers) { if (nameField == null) { Class<?> cl = annotationMember.getClass(); nameField = cl.getDeclaredField("name"); nameField.setAccessible(true); validateValueMethod = cl.getDeclaredMethod("validateValue"); validateValueMethod.setAccessible(true); } String name = (String) nameField.get(annotationMember); Object val = validateValueMethod.invoke(annotationMember); map.put(name, val); } return map; } } 

Я сравнивал аннотацию с 4 элементами.
Миллисекундные времена для 10000 итераций либо получения значений всех из них, либо вызова метода выше:

  Device Default Hack HTC Desire 2.3.7 11094 730 Emulator 4.0.4 3157 528 Galaxy Nexus 4.3 1248 392 

Вот как я интегрировал его в DroidParts : https://github.com/yanchenko/droidparts/commit/93fd1a1d6c76c2f4abf185f92c5c59e285f8bc69 .

Чтобы следить за этим, проблема возникает при вызове методов аннотаций. Ошибка, указанная выше с помощью значков, исправляет задержку getAnnotation (), но вызов метода в аннотации по-прежнему является проблемой из-за проблем Method.equals ().

Не удалось найти отчет об ошибке для Method.equals (), поэтому я создал его здесь: https://code.google.com/p/android/issues/detail?id=37380

Edit: Итак, моя работа для этого (спасибо за идеи @Gray), на самом деле довольно проста. (Это трансконтированный код, некоторое кэширование и т. Д.)

 annotationFactory = Class.forName("org.apache.harmony.lang.annotation.AnnotationFactory"); getElementDesc = annotationFactory.getMethod("getElementsDescription", Class.class); Object[] members = (Object[])getElementDesc.invoke(annotationFactory, clz); // these are AnnotationMember[] Object element = null; for (Object e:members){ // AnnotationMembers Field f = e.getClass().getDeclaredField("name"); f.setAccessible(true); String fname = (String) f.get(e); if (methodName.equals(fname)){ element = e; break; } } if (element == null) throw new Exception("Element was not found"); Method m = element.getClass().getMethod("validateValue"); return m.invoke(element, args); 

Ваш пробег будет варьироваться в зависимости от использования, но в случае, если это было примерно в 15-20 раз быстрее, чем в случае «правильного пути»,

Я думаю, что если вам удастся изменить политику хранения RUNTIME, это не должно быть так медленно.

EDIT: Я знаю, для вашего проекта это может быть не вариант. Возможно, это больше проблема того, что вы делаете с этой аннотацией, а не с плохими результатами в целом.