All streams
Search
Write a publication
Pull to refresh
44
0

Разработчик

Send message

Ответ переехал выше.

Ещё много изменений произошло в Actuator.

Знаю один из проектов, который ещё не переехал на СБ2 именно из-за Актуатора.


Кстати, раз уж речь зашла от миграции: отдельная и интересная тема — переезд с голого Спринга на Спринг Бут. Так на одном из проектов столкнулись с тем, что перестал работать откат транзакций при выбрасывании проверяемых исключений. Когда-то давно возникла необходимость откатывать спринговую транзакцию при выбрасывании этого исключения. Это было сделано с помощью AnnotationTransactionAttributeSource примерно вот так:


public class CustomAttributeSource extends AnnotationTransactionAttributeSource {
  @Override
  protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    return new DefaultTransactionAttribute() {
      @Override
      public boolean rollbackOn(Throwable ex) {
        return super.rollbackOn(ex) || ex instanceof CustomCheckedException;
      }
    };
  }
}

На чистом Спринге работало, после переезда на СБ1 транзакции откатываться перестали.

1. Количество развёрнутых носителей не исчисляется тысячами, в лучшем случае сотнями. Количество же развёрнутых боеголовок не превышает двух тысяч. Данные из Википедии.

2. Перехватив ракету до развода боевых блоков можно одной противоракетой уничтожить сразу несколько головных частей. А как мы помним, речь не идёт о перехвате тысяч, а всего лишь сотен носителей. Таким образом, масштабное развёртывание ПРО может сильно уменьшить возможность нанесения ответного удара. Не даром же этот договор был заключен.

3. Как я указал в первом ответе (и как справедливо заметил ув. grondek ) в установки может быть загружено всё что угодно. И именно эта возможность вызывает наибольшее беспокойство.
Доказано в Дрездене и Хиросиме.
США свою систему ПРО (из договора о которой они вышли в одностороннем порядке ещё в 2002 году) почему-то не сворачивают и не ликвидируют. Более того, ПРО размещается в непосредственной близости от границ России, в частности в Польше и Румынии. Официально заявляется, что это для защиты от Ирана (верим!). И вот Минобороны России вполне резонно подозревает, что пусковые установки ракет ПРО в этих странах могут использоваться в т. ч. для размещения наступательного вооружения и запрашивает проверку этих установок. На что получает отказ, хотя если наступательных вооружений там нет и установка их невозможна, то этот отказ выглядит подозрительным.

Получается, что Россия не может запросить даже проверку объектов, вызывающих у неё опасение (небезосновательно), при этом от самой России в ультимативной форме требуют уничтожить вооружение, которое, во-первых, не размещается непосредственно у границ США, и во-вторых не является доказанным нарушением Договора РСМД.

Вы правы, передача размера в StringBuilder даёт неплохой прирост в данном случае


@Benchmark
public String appendBoundsSized(Data data) {
  int beginIndex = data.beginIndex;
  int endIndex = data.endIndex;

  return new StringBuilder(endIndex - beginIndex + 2)
          .append('L')
          .append(data.str, beginIndex, endIndex)
          .append(';')
          .toString();
}

Вывод


Benchmark                             length nonLatin   Score   rror Units
appendBounds                              10     true    41,3 ±  0,9 ns/op
appendBounds                             100     true   143,6 ±  8,1 ns/op
appendBounds                            1000     true  1206,5 ± 48,7 ns/op

appendBoundsSized                         10     true    42,6 ±  0,7 ns/op
appendBoundsSized                        100     true   116,2 ± 17,1 ns/op
appendBoundsSized                       1000     true   880,9 ± 33,7 ns/op

appendBounds                              10    false    28,4 ±  0,2 ns/op
appendBounds                             100    false    99,0 ±  3,9 ns/op
appendBounds                            1000    false   663,3 ± 44,5 ns/op

appendBoundsSized                         10    false    29,5 ±  0,9 ns/op
appendBoundsSized                        100    false    68,7 ±  3,9 ns/op
appendBoundsSized                       1000    false   485,6 ± 11,2 ns/op

appendBounds:·gc.alloc.rate.norm          10     true   200,0 ±  0,0  B/op
appendBounds:·gc.alloc.rate.norm         100     true  1192,0 ±  0,0  B/op
appendBounds:·gc.alloc.rate.norm        1000     true 10200,0 ±  0,0  B/op

appendBoundsSized:·gc.alloc.rate.norm     10     true   192,0 ±  0,0  B/op
appendBoundsSized:·gc.alloc.rate.norm    100     true   736,0 ±  0,0  B/op
appendBoundsSized:·gc.alloc.rate.norm   1000     true  6144,0 ±  0,0  B/op

appendBounds:·gc.alloc.rate.norm          10    false   112,0 ±  0,0  B/op
appendBounds:·gc.alloc.rate.norm         100    false   544,0 ±  0,0  B/op
appendBounds:·gc.alloc.rate.norm        1000    false  4152,0 ±  0,0  B/op

appendBoundsSized:·gc.alloc.rate.norm     10    false   112,0 ±  0,0  B/op
appendBoundsSized:·gc.alloc.rate.norm    100    false   288,0 ±  0,0  B/op
appendBoundsSized:·gc.alloc.rate.norm   1000    false  2096,0 ±  0,0  B/op

Интересно, что в этом случае (малое количество вызовов StringBuilder.append) точное выделение памяти даёт очень хороший прирост, чего не скажешь о случае, когда вызовов StringBuilder.append значительно больше.

Спасибо! Уже не первый год работаю, но не устаю удивляться, как много тонкостей стоит вроде бы за простым кодом.

Вы имеете ввиду неэквивалентность байт-кода? Поведение обоих методов, насколько я понимаю, одинаковое:


String foo(String a1, String a2, String a3) {
  StringBuilder sb = new StringBuilder();
  sb.append(a1);
  sb.append(a2);
  sb.append(a3);
  return sb.toString();
}

String _foo(String a1, String a2, String a3) {
  return new StringBuilder()
          .append(a1)
          .append(a2)
          .append(a3)
          .toString();
}

Действительно, передав размер конечной строки сразу в конструктор StringBuilder-а можно выделить память только один раз и ровно столько, сколько нужно. Но это почти не даёт прироста.
Представьте такой код:


public class ToHexStringConverter {

  private static final char[] HEX_CHARS = {
          '0', '1', '2', '3',
          '4', '5', '6', '7',
          '8', '9', 'A', 'B',
          'C', 'D', 'E', 'F'
  };

  public String toHexString(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
      int temp = (int) b & 0xFF;
      sb.append(HEX_CHARS[temp / 16]);
      sb.append(HEX_CHARS[temp % 16]);
    }
    return sb.toString();
  }

  public String patched_toHexString(byte[] bytes) {
    StringBuilder sb = new StringBuilder(bytes.length * 2);
    for (byte b : bytes) {
      int temp = (int) b & 0xFF;
      sb.append(HEX_CHARS[temp / 16]);
      sb.append(HEX_CHARS[temp % 16]);
    }
    return sb.toString();
  }
}

Здесь оба метода преобразовывают входной массив байт в его шестнадцатеричное представление. Первый метод исходный, второй — улучшенный. Смысл улучшения в том, что мы используем известный размер массива, а также тот факт, что каждый байт соответствует двум знакам, добавляемым к StringBuilder-у, для передачи ёмкости в конструктор.


Но увы, это не даёт значимого прироста. Возьмём 20 Мб и скормим обоим методам:


@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms2g", "-Xmx2g"})
public class SizedStringBuilderBenchmark {

  private byte[] bytes;
  private ToHexStringConverter converter;

  @Setup
  public void init() {
    bytes = new byte[1024 * 1024 * 20];
    converter = new ToHexStringConverter();
    ThreadLocalRandom.current().nextBytes(bytes);
  }

  @Benchmark
  public String original() {
    return converter.toHexString(bytes);
  }

  @Benchmark
  public String patched() {
    return converter.patched_toHexString(bytes);
  }
}

На выходе имеем


Benchmark                        Mode  Cnt          Score   Error   Units

original                         avgt   25        124,766 ± 1,610   ms/op
patched                          avgt   25        113,763 ± 3,432   ms/op

original:·gc.alloc.rate.norm     avgt   25  192938425,434 ± 0,886    B/op
patched:·gc.alloc.rate.norm      avgt   25   83886183,845 ± 1,341    B/op

Действительно, выигрыш по памяти более чем двукратный, но разница во времени не столь велика.


Конкретно в этом случае ощутимый прирост даст выбрасывание StringBuilder-a:


public class ToHexStringConverter {

  private static final char[] HEX_CHARS = {
          '0', '1', '2', '3',
          '4', '5', '6', '7',
          '8', '9', 'A', 'B',
          'C', 'D', 'E', 'F'
  };

  //...

  public String chars_toHexString(byte[] bytes) {
    char[] result = new char[bytes.length * 2];
    int idx = 0;
    for (byte b : bytes) {
      int temp = (int) b & 0xFF;
      result[idx++] = HEX_CHARS[temp / 16];
      result[idx++] = HEX_CHARS[temp % 16];
    }
    return new String(result);
  }
}

Берём тот же замер:


@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms2g", "-Xmx2g"})
public class SizedStringBuilderBenchmark {

  private byte[] bytes;
  private ToHexStringConverter converter;

  @Setup
  public void init() {
    bytes = new byte[1024 * 1024 * 20];
    converter = new ToHexStringConverter();
    ThreadLocalRandom.current().nextBytes(bytes);
  }

  @Benchmark
  public String original() {
    return converter.toHexString(bytes);
  }

  @Benchmark
  public String patched() {
    return converter.patched_toHexString(bytes);
  }

  @Benchmark
  public String chars() {
    return converter.chars_toHexString(bytes);
  }

}

И вот тут получаем почти 4-х кратный прирост по времени:


Benchmark                        Mode  Cnt          Score   Error   Units

original                         avgt   25        124,766 ± 1,610   ms/op
patched                          avgt   25        113,763 ± 3,432   ms/op
chars                            avgt   25         32,367 ± 0,656   ms/op

original:·gc.alloc.rate.norm     avgt   25  192938425,434 ± 0,886    B/op
patched:·gc.alloc.rate.norm      avgt   25   83886183,845 ± 1,341    B/op
chars:·gc.alloc.rate.norm        avgt   25  125829182,781 ± 0,242    B/op

Спасибо за ссылку, я упустил эту особенность. Интересно, почему тогда javac не превращает


StringBuilder sb = new StringBuilder();
sb.append(a1);
sb.append(a2);
sb.append(a3);
sb.toString();

в


new StringBuilder().append(a1).append(a2).append(a3).toString();

ещё на этапе компиляции исходного кода? Что мешает такому преобразованию?

Добрый день, действительно ли использование объектных пулов даёт в вашем проекте ощутимый прирост производительности? Вопрос задаю в связи с всплывшим в памяти докладом Алексея Кудрявцева "Computer Science ещё жива", в котором утверждается, что от объектных пулов он отказался после переезда на "восьмёрку".


Вот тут этот момент в докладе: https://youtu.be/Ra2RSsyO4XU?t=2097

Спасибо вам за ссылку! Попробую копнуть в этом направлении.

по зомбоящику РосПропаганда ему круглосуточно рассказывает

Вы-то "зомбоящик", насколько я могу судить, не смотрите. А коль так, то откуда вы знаете, что он рассказывает, да ещё и круглосуточно?


не рвал дупу

Что такое "дупа"?


перевода стрелок как дети

Именно это вы и сделали, написав, что пользователи, не восторгающиеся шутками про "глюпи рюзге вспарывает обшивку своего окрабля, азаза", являются ольгинскими (иначе же и быть не может)


Бутина уже заговорила

Да-да, #скоро :)


в России начался настоящий патриотический угар

Судя по использованию слов вроде "дупа" вы живёте не в России. Откуда вы тогда знаете какой угар там начался? ТСН рассказала? ;)


пропогандонш, непричемышей

Ещё раз перечитайте мой предыдущий ответ про предсказуемость.

насколько же вы предсказуемые

ольгинцам

зрителям зомбоящика

РосПропаганда

агент Трамп

Вы-то, я вижу, ни разу не предсказуемы.

Процентов 95 уже через месяц не вспомнят об этом событии, что не отменяет формирования картины мира из подобных «Не более» и «очередной мемасик».

Опять же, если отмотать твиттеры этих самых журналистов до эпизода с плавающей ступенью и сравнить отзывы тогда и сейчас, то видна вполне ясная разница в тональности высказываний и оценок.

Думаю, уместно задать этот вопрос "Роскосмосу". На мой взгляд, мешало наплевательское отношение к своему образу. Мой ответ был не об этом, а об освещении работы космонавтов "непредвзятыми" журналистами. Комментарии вроде


Это, вероятно, является ещё одним первым достижением российской космической программы: космонавт только что обнажил большой нож и проткнул им свой космический корабль.

и


Я отвлеклась на русских, распарывающих свой космический корабль

являются пертосянством чистой воды.

Окажись в этом же положении астронавты НАСА, эти же журналисты захлёбывались бы от хвалебных комментариев и подчёркивали бы важность, нужность и т. д. Но на их месте русские, поэтому объективные западные журналисты натужно шутят и соревнуются в петросянстве.
По поводу двойственного ощущения я ниже оставил комментарий про `SimpleJpaRepository::findAll`.

По поводу дальнейшего использования, я тут посмотрел внутрь org.springframework.data.jpa.repository.support.SimpleJpaRepository, там есть такой метод


public List<T> findAll(Iterable<ID> ids) {

    if (ids == null || !ids.iterator().hasNext()) {
        return Collections.emptyList();
    }

    if (entityInformation.hasCompositeId()) {

        List<T> results = new ArrayList<T>();

        for (ID id : ids) {
            results.add(findOne(id));
        }

        return results;
    }

    ByIdsSpecification<T> specification = new ByIdsSpecification<T>(entityInformation);
    TypedQuery<T> query = getQuery(specification, (Sort) null);

    return query.setParameter(specification.parameter, ids).getResultList();
}

В текущем виде возможны два случая, когда возвращается пустой список: 1) на вход пришла пустая коллекция ключей и 2) запрос вернул пустую выборку.


Так вот в первом случае возвращается неизменяемый список, а во втором — ArrayList.

Про чистый JDBC не скажу, а Hibernate возвращает пустой список (ЕМНИП, пустой ArrayList).

Information

Rating
Does not participate
Registered
Activity