Подтвердить что ты не робот

Могу ли я прекомпилировать строку формата в String.format? (Или сделать что-нибудь еще, чтобы сделать журналы форматирования быстрее?)

Хорошо известно что производительность String.format() ужасна. Я вижу большие возможные улучшения в моем (и, вероятно, очень распространенном) типичном случае. Я печатаю ту же структуру данных много раз. Представьте себе структуру типа "x:% d y:% d z:% d". Я ожидаю, что главная проблема с String.format() заключается в том, что он должен всегда анализировать строку форматирования. Мой вопрос: есть ли какой-то готовый класс, который позволит читать строку форматирования только один раз, а затем позволить быстро выдавать строку при заполнении переменных параметров? Использование должно выглядеть следующим образом:

PreString ps = new PreString("x:%d y:%d z:%d");
String s;
for(int i=0;i<1000;i++){
    s = ps.format(i,i,i); 
}

Я знаю, что это возможно - следующий мой быстрый и грязный пример, который делает то, о чем я говорю, и примерно на 10 раз быстрее на моей машине:

public interface myPrintable{
    boolean isConst();
    String prn(Object o);
    String prn();
}

public class MyPrnStr implements myPrintable{
    String s;
    public MyPrnStr(String s){this.s =s;}
    @Override public boolean isConst() { return true; }
    @Override public String prn(Object o) { return s; }
    @Override public String prn() { return s; }
}

public class MyPrnInt implements myPrintable{
    public MyPrnInt(){}
    @Override  public boolean isConst() { return false; }
    @Override  public String prn(Object o) { return String.valueOf((Integer)o);  }
    @Override  public String prn() { return "NumMissing";   }
}

public class FastFormat{
    myPrintable[]      obj    = new myPrintable[100];
    int                objIdx = 0;
    StringBuilder      sb     = new StringBuilder();

    public FastFormat() {}

    public void addObject(myPrintable o) {  obj[objIdx++] = o;   }

    public String format(Object... par) {
        sb.setLength(0);
        int parIdx = 0;
        for (int i = 0; i < objIdx; i++) {
            if(obj[i].isConst()) sb.append(obj[i].prn());
            else                 sb.append(obj[i].prn(par[parIdx++]));
        }
        return sb.toString();
    }
}

Он используется следующим образом:

FastFormat ff = new FastFormat();
ff.addObject(new MyPrnStr("x:"));
ff.addObject(new MyPrnInt());
ff.addObject(new MyPrnStr(" y:"));
ff.addObject(new MyPrnInt());
ff.addObject(new MyPrnStr(" z:"));
ff.addObject(new MyPrnInt());
for (int i = 0; i < rpt; i++) {
    s = ff.format(i,i,i);
}

когда я сравниваю с

long beg = System.nanoTime();
for (int i = 0; i < rpt; i++) {
    s = String.format("x:%d y:%d z:%d", i, i, i);
}
long diff = System.nanoTime() - beg;

Для предварительного форматирования итерации 1e6 улучшается результат в ~ 10:

time [ns]: String.format()     (+90,73%)  3 458 270 585 
time [ns]: FastFormat.format() (+09,27%)    353 431 686 

[EDIT]

Как Стив Chaloner ответил, что есть MessageFormat, который довольно делая то, что я хочу. Поэтому я попробовал код:

MessageFormat mf = new MessageFormat("x:{0,number,integer} y:{0,number,integer} z:{0,number,integer}");
Object[] uo = new Object[3];
for (int i = 0; i < rpt; i++) {
    uo[0]=uo[1]=uo[2] = i;
    s = mf.format(uo);
}

И это быстрее только в 2 раза. Не фактор 10, который я надеялся. Снова см. Измерение для 1-й итерации (JRE 1.8.0_25-b18 32bit):

time [s]: String.format()     (+63,18%)  3.359 146 913 
time [s]: FastFormat.format() (+05,99%)  0.318 569 218 
time [s]: MessageFormat       (+30,83%)  1.639 255 061 

[EDIT2]

Как Slanec ответил org.slf4j.helpers.MessageFormatter. (Я попробовал библиотечную версию slf4j-1.7.12)

Я попытался сравнить код:

Object[] uo2 = new Object[3];
beg = System.nanoTime();
for(long i=rpt;i>0;i--){
    uo2[0]=uo2[1]=uo2[2] = i;
    s = MessageFormatter.arrayFormat("x: {} y: {} z: {}",uo2).getMessage();
}

с кодом для MessageFormat, указанным выше в разделе [EDIT]. Я получил следующие результаты для его циклирования 1M раз:

Time MessageFormatter [s]: 1.099 880 912
Time MessageFormat    [s]: 2.631 521 135
speed up : 2.393 times

Итак, MessageFormatter - лучший ответ до сих пор, но мой простой пример все же немного быстрее... Итак, любое готовое ускорение предложения библиотеки?

4b9b3361

Ответ 1

Если вы ищете быструю реализацию, вам нужно посмотреть вне JDK. Вы, вероятно, используете slf4j для регистрации в любом случае, поэтому давайте посмотрим на MessageFormatter:

MessageFormatter.arrayFormat("x:{} y:{} z:{}", new Object[] {i, i, i}).getMessage();

На моей машине (и грубой и ошибочной микрофункции) она примерно на 1/6 медленнее, чем ваш класс FastFormat, и примерно в 5-10 раз быстрее, чем String::format или MessageFormat.

Ответ 2

Похоже, вы хотите MessageFormat

Из документации:

В следующем примере создается экземпляр MessageFormat, который можно использовать повторно:

 int fileCount = 1273;
 String diskName = "MyDisk";
 Object[] testArgs = {new Long(fileCount), diskName};

 MessageFormat form = new MessageFormat(
     "The disk \"{1}\" contains {0} file(s).");
 System.out.println(form.format(testArgs));

Ответ 3

Я сказал, что поставлю, и вот он. Моя библиотека для форматирования строк, поддерживающая предварительную компиляцию (работающая доказательство концепции): https://bitbucket.org/JanecekPetr/stringformatting/

Использование

StringFormat.format("x:{} y:{} z:{}", i, i, i)

Я получаю очень похожие числа с slf4j и log4j2.

Однако при использовании

CompiledStringFormat format = StringFormat.compile("x:{} y:{} z:{}");

// and then, in the loop
format.format(i, i, i)

Я получаю примерно 1/3 лучших чисел, чем ваш FastFormat. Обратите внимание, что в этот момент вы должны форматировать много строк, чтобы получить существенные отличия.