оптимизация строки Java - алгоритм загрузки на месте - PullRequest
1 голос
/ 28 сентября 2011

Мне нужно оптимизировать фактическую загрузку / разбор CSV-файла (строки). Лучший способ, которым я знаю, - это алгоритмы загрузки на месте, и я успешно использовал их, используя JNI и DLL C ++, которая загружает данные непосредственно из файла, созданного из проанализированных данных CSV.

Было бы хорошо, если бы он остановился на этом, но использование этой схемы только сделало это на 15% быстрее (больше не нужно разбирать данные). Одна из причин того, что это не так быстро, как я думал, заключается в том, что java-клиент использует jstring, поэтому мне нужно снова преобразовать фактические данные из char * в jstring.

Лучше всего было бы игнорировать этот шаг преобразования и загружать данные непосредственно в объекты jstring (больше не нужно преобразовывать). Таким образом, вместо дублирования данных, основанных на загруженных на месте данных, jstring будет указывать прямо на кусок памяти (обратите внимание, что данные будут состоять из jchars, а не chars). Очень плохо то, что нам нужно убедиться, что сборщик мусора не собирает эти данные (сохраняя ссылку на них, может быть?), Но это должно быть выполнимо ... нет?

Я думаю, у меня есть два варианта:

1- Загрузите данные в java (не более jni) и используйте символы, указывающие на загруженные данные, для создания строк ... но мне нужно найти способ предотвратить дублирование данных при создании строки.

2- Продолжайте использовать jni для «ручного» создания и установки переменной jstring и убедитесь, что параметры сборщика мусора установлены правильно, чтобы он ничего не делал с ним. Например:

jstring str; 
str.data = loadedinplacedata;  // assign data pointer
return str;

Не уверен, что это возможно, но я не возражаю, просто сохраните jstring непосредственно в файл и перезагрузите его так:

jstring * str = (jstring *)&loadedinplacedata[someoffset];
return * str;

Я знаю, что это не обычная вещь Java, но я почти уверен, что Java достаточно расширяема, чтобы это можно было сделать. И дело не в том, что у меня действительно есть выбор ... проекту уже 3 года, и он должен работать. = S

Это код JNI (C ++):

const jchar * data = GetData(id, row, col); // get pointer of the string ends w/ \0
unsigned int len = wcslen( (wchar_t*)data );
// The best would be to prevent this function to duplicate the data.
jstring str = env->NewString( data, len ); 
return str;

Примечание. Приведенный выше код ускорил его на 20% (вместо 15) благодаря использованию данных Unicode вместо UTF8 (NewString вместо NewStringUTF). Это показывает, что если я смогу убрать этот шаг или оптимизировать его, я получу неплохое увеличение производительности.

Ответы [ 3 ]

0 голосов
/ 28 сентября 2011

Я думаю, что сначала вы должны понять, почему версия C ++ работает на 15% быстрее, и почему это улучшение производительности не может быть напрямую переведено на Java. Почему вы не можете написать код на 15% быстрее в Java?

Давайте посмотрим на вашу проблему. Вы устранили синтаксический анализ с помощью DLL C ++. (Почему это не могло быть сделано в Java?). И тогда, как я понимаю:

  1. Вы предлагаете напрямую манипулировать содержимым jstrings
  2. Вы хотите, чтобы сборщик мусора не касался этих измененных строк jst (сохраняя ссылку на них) и, следовательно, потенциально изменял поведение JVM и связывался с сборщиком мусора, когда он в конечном итоге собирает мусор.

Будете ли вы исправлять эти ссылки, прежде чем разрешить их сборку мусора?

Если вы предлагаете сделать свое собственное управление памятью, почему вы вообще используете java? Почему бы просто не сделать это на чистом C ++?

Предполагая, что вы хотите продолжить работу в Java, когда вы создаете строку, сама строка является новым объектом, но данные, на которые она указывает, необязательно. Вы можете проверить это, вызвав String.intern (). Используя следующий код:

public static void main(String[] args) {
    String s3 = "foofoo";

    String s1 = call("foo");
    String s2 = call("foo");

    System.out.println("s1 == s2=" + (s1 == s2));
    System.out.println("s1.intern() == s2.intern()=" + (s1.intern() == s2.intern()));
    System.out.println("s1.intern() == s3.intern()=" + (s1.intern() == s3.intern()));

    System.out.println("s1.substring(3) == s2.substring(3)=" + (s1.substring(3) == s2.substring(3)));
    System.out.println("s1.substring(3).intern() == s2.substring(3).intern()=" + (s1.substring(3).intern() == s2.substring(3).intern()));
}

public static String call(String s) {
    return s + "foo";        
}

Это производит:

s1 == s2=false
s1.intern() == s2.intern()=true
s1.intern() == s3.intern()=true
s1.substring(3) == s2.substring(3)=false
s1.substring(3).intern() == s2.substring(3).intern()=true

Итак, вы можете видеть, что, хотя объекты String отличаются, данные, действительные байты - нет. Таким образом, ваши изменения могут не соответствовать действительности, JVM, возможно, уже сделает это за вас. И стоит сказать, что если вы начнете модифицировать внутреннюю структуру jstrings, это вполне может испортить ситуацию.

Мое предложение было бы выяснить, что вы можете сделать с точки зрения алгоритмов. Разработка с использованием чистого Java всегда быстрее, чем Java и JNI вместе взятые. У вас гораздо больше шансов найти лучшее решение на чистой Java.

0 голосов
/ 07 октября 2011

Ну ... кажется, что то, что я хотел сделать, не "поддерживается" Java, если я не взломал его ... Я считаю, что это можно сделать, используя GetStringCritical, чтобы получить реальный адрес массива символов и затем выяснить, количество символов и тому подобное, но это далеко за пределы «безопасного» программирования.

Лучшая работа, которую я нашел, заключалась в создании хеш-таблицы в java и использовании уникального идентификатора, обработанного при создании моего файла данных (действующего подобно .intern ()). если строка отсутствует в хеш-таблице, она запросит ее через dll и сохранит ее в хеш-таблице.

файл данных: numrow, numcols, для каждой ячейки добавьте целочисленное значение (в моем случае смещение в памяти, указывающее на строку) для каждой ячейки добавьте строку, заканчивающуюся \ 0

Используя значение смещения, я могу несколько минимизировать количество создаваемых строк и запросов строк. Я попытался использовать globalref, чтобы сохранить строку внутри DLL, но это сделало его в 4 раза медленнее.

0 голосов
/ 28 сентября 2011

Я никогда не работал с JNI, но ... имеет ли смысл возвращать собственный класс, реализующий CharSequence, и, возможно, несколько других интерфейсов, таких как Comparable , вместо String? Похоже, что таким образом у вас будут меньше проблем с повреждением данных.

...