наконец, блокировать выполнение перед соответствующим уловом в java [duplicate]

У OMG это просто спирально.

В большинстве этих примеров мало понимания о том, как работает все оборудование.

В любое время, когда у вас есть ветка, процессор должен угадайте, какая ветвь будет взята. Трубка инструкции загружается инструкциями, которые ведут вниз по угаданному пути. Если ЦП догадался неправильно, тогда заголовок команды загорается, а другая ветвь должна быть загружена.

Рассмотрим простой цикл while сверху. Предполагается, что он останется внутри цикла. Это будет неправильно, по крайней мере, один раз, когда он покинет цикл. Это WILL очистит инструкцию. Такое поведение немного лучше, чем угадать, что он покинет цикл, и в этом случае он будет промывать канал команд на каждой итерации.

Количество потерянных циклов процессора сильно отличается от одного типа процессора к другому. Но вы можете ожидать от 20 до 150 потерянных циклов процессора.

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

Давайте рассмотрим, что происходит, когда вы просматриваете значение в таблице. Скорее всего, в настоящее время значение не находится в кеше, по крайней мере, не в первый раз, когда вы вызываете свою функцию. Это означает, что CPU останавливается, пока значение загружается из кеша. Опять же это меняется от одной машины к другой. Новые чипы Intel фактически используют это как возможность обмена потоками, пока текущий поток ожидает завершения загрузки кеша. Это может быть легко дороже, чем сбой команды, но если вы выполняете эту операцию несколько раз, это может произойти только один раз.

Очевидно, что самым быстрым решением постоянного времени является решение, которое включает детерминированную математику , Чистое и элегантное решение.

Приносим извинения, если это уже было рассмотрено.

Каждый компилятор, который я использую, кроме XCODE AFAIK, имеет встроенные компиляторы как для прямого бита, так и для обратного бита. Они будут скомпилированы для одной команды сборки на большинстве аппаратных средств без Cache Miss, no Branch Miss-Prediction и других программистов, генерирующих блоки преткновения.

Для компиляторов Microsoft используют _BitScanForward & amp; _BitScanReverse. Для использования GCC __builtin_ffs, __builtin_clz, __builtin_ctz.

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

Извините, я полностью забыл предоставить решение .. Это код, который я использую в IPAD, который не имеет инструкции для уровня сборки для задачи:

unsigned BitScanLow_BranchFree(unsigned value) { bool bwl = (value & 0x0000ffff) == 0; unsigned I1 = (bwl * 15); value = (value >> I1) & 0x0000ffff; bool bbl = (value & 0x00ff00ff) == 0; unsigned I2 = (bbl * 7); value = (value >> I2) & 0x00ff00ff; bool bnl = (value & 0x0f0f0f0f) == 0; unsigned I3 = (bnl * 3); value = (value >> I3) & 0x0f0f0f0f; bool bsl = (value & 0x33333333) == 0; unsigned I4 = (bsl * 1); value = (value >> I4) & 0x33333333; unsigned result = value + I1 + I2 + I3 + I4 - 1; return result; }

. Здесь нужно понять, что это не сравнение, которое дорогая, но ветка, которая возникает после сравнения. Сравнение в этом случае вынуждается значением 0 или 1 с символом .. == 0, и результат используется для объединения математики, которая произошла бы по обе стороны от ветви.

Изменить :

Вышеприведенный код полностью нарушен. Этот код работает и остается без ветвей (если оптимизирован):

int BitScanLow_BranchFree(ui value) { int i16 = !(value & 0xffff) << 4; value >>= i16; int i8 = !(value & 0xff) << 3; value >>= i8; int i4 = !(value & 0xf) << 2; value >>= i4; int i2 = !(value & 0x3) << 1; value >>= i2; int i1 = !(value & 0x1); int i0 = (value >> i1) & 1? 0 : -32; return i16 + i8 + i4 + i2 + i1 + i0; }

Это возвращает -1, если задано 0. Если вам все равно 0 или счастливы получить 31 для 0, удалите расчет i0, экономя кусок времени.

42
задан Nick Heiner 10 December 2009 в 20:20
поделиться

6 ответов

Они разные потоки и сбрасываются в разное время.

Если вы положили

  System.out.flush ();  System.err.flush ();   

внутри вашего цикла, он будет работать так, как ожидалось.

Чтобы уточнить, выходные потоки кэшируются, поэтому вся запись поступает в этот буфер памяти.

Вы пишете два буфера, затем после периода бездействия они оба покраснели (один за другим).

33
ответ дан Bill K 16 August 2018 в 05:45
поделиться
  • 1
    Это не работает. После размещения этих команд смыва внутри цикла ничего не изменилось. Использование Java 1.7. – Mark Jeronimus 10 March 2012 в 10:00
  • 2
    Уже ответил на тему переполнения стека Java: синхронизация стандартного и стандартного ошибок – MikeOnline 6 April 2012 в 19:55
  • 3
    Этот ответ на самом деле неверен, так как System.out и System.err должны быть автоматически сброшены на каждой новой строке, что явно присутствует, поскольку вызовы используют println ( ) . Так что это не должно иметь значения. – Maarten Bodewes 1 March 2013 в 01:33
  • 4
    Вы используете слово, должно быть много ... вы действительно попробовали? – Bill K 1 March 2013 в 01:49
  • 5
    Ответ неверен. Эти потоки autoflush. – robert 18 November 2016 в 10:25

Если вы используете консоль Eclipse, на работе, похоже, работают два разных явления: один, как описано в @Gemtastic , является обработкой потоков JVM, а другой - способом Eclipse читает эти потоки, как упоминалось @DraganBozanovic . Поскольку я использую Eclipse, недостаточно -потока @BillK , которая касается только проблемы с JVM, [40]

В итоге я написал класс-помощник под названием EclipseTools со следующим содержимым (и требуемым объявлением и импортом пакета). Это немного хак, но исправлены обе проблемы:

  public class EclipseTools {private static List & lt; OutputStream & gt;  streams = null;  private static OutputStream lastStream = null;  private static class FixedStream расширяет OutputStream {private final OutputStream target;  public FixedStream (OutputStream originalStream) {target = originalStream;  streams.add (это);  } @Override public void write (int b) throws IOException {if (lastStream! = This) swap ();  target.write (б);  } @Override public void write (byte [] b) throws IOException {if (lastStream! = This) swap ();  target.write (б);  } @Override public void write (byte [] b, int off, int len) throws IOException {if (lastStream! = This) swap ();  target.write (b, off, len);  } private void swap () throws IOException {if (lastStream! = null) {lastStream.flush ();  попробуйте {Thread.sleep (200);  } catch (InterruptedException e) {}} lastStream = this;  } @Override public void close () throws IOException {target.close ();  } @Override public void flush () выдает IOException {target.flush ();  }} / ** * Вставляет задержку 200 мс в System.err или System.out OutputStreams * каждый раз, когда выход переключается с одного на другой.  Это не позволяет * консоли Eclipse показывать вывод двух потоков из * заказа.  Эту функцию нужно вызвать только один раз.  * / public static void fixConsole () {if (streams! = null) return;  streams = new ArrayList & lt; OutputStream & gt; ();  System.setErr (новый PrintStream (новый FixedStream (System.err)));  System.setOut (новый PrintStream (новый FixedStream (System.out)));  }}  

Чтобы использовать, просто вызовите EclipseTools.fixConsole () один раз в начале вашего кода.

В основном это заменяет два потока System.err и System.out с настраиваемым набором потоков, которые просто пересылают свои данные в исходные потоки, но отслеживают, какой поток был записан последним. Если поток, который записывается для изменений, например, System.err.something (...) , за которым следует System.out.something (...) , он сбрасывает выходные данные последнего потока и ждет 200 мс, чтобы дать время консоли Eclipse завершить его печать.

Примечание: 200 мс - это просто приблизительное начальное значение. Если этот код уменьшает, но не устраняет проблему для вас, увеличьте задержку в Thread.sleep от 200 до уровня выше, пока он не будет работать. В качестве альтернативы, если эта задержка работает, но влияет на производительность вашего кода (если вы чередуете потоки часто), вы можете попытаться уменьшить его постепенно, пока не начнете получать ошибки.

9
ответ дан Community 16 August 2018 в 05:45
поделиться

Это ошибка в Eclipse . Кажется, что Eclipse использует отдельные потоки для чтения содержимого out и err потоков без какой-либо синхронизации.

Если вы скомпилируете класс и выполните его в консоль (с классическим java & lt; main class name & gt; ), порядок как ожидалось.

1
ответ дан Dragan Bozanovic 16 August 2018 в 05:45
поделиться

Это вызвано функцией JVM и, если вы не делаете взломать, например, тот, который предоставлен Marcus A., на самом деле не так просто работать. [D0] .flush () работает в этом случае, но причина этого намного сложнее в работе.

Что здесь происходит?

JVM умный, но также очень, очень аутичный. Когда вы программируете на Java, вы не говорите компьютеру, что делать, вы сообщаете JVM (виртуальная машина Java), что вы хотели бы сделать. И он будет делать это, но более эффективно. Ваш код не является точным подробным инструкциям, в этом случае вам нужен только компилятор, как на C и C ++, JVM берет ваш код в качестве списка спецификаций для того, что он должен оптимизировать, а затем делать. Это то, что происходит здесь. Java видит, что вы нажимаете строки в два разных потока буферов. Самый эффективный способ сделать это - буферизировать все строки, которые вы хотите передать потокам, а затем вывести их. Это происходит по одному потоку в то время, по существу преобразуя ваш код, сделав что-то вроде этого (будьте осторожны: псевдокод) :

  for (int i = 0; i & lt;  5; i ++) {out.add ();  err.add ();  } out.flush ();  err.flush ();   

Поскольку это более эффективно, именно это будет делать JVM. Добавление .flush () в цикле будет сигнализировать JVM о необходимости промывки в каждом цикле, который не может быть улучшен с помощью вышеуказанного метода. Но если вы объясните, как это будет работать, цикл JVM изменит порядок вашего кода, чтобы сделать его последним, потому что это более эффективно.

  System.out  .println ( "из");  System.out.flush ();  System.err.println ( "ERR");  System.err.flush ();  System.out.println ( "из");  System.out.flush ();  System.err.println ( "ERR");  System.err.flush ();   

Этот код всегда будет реорганизован на что-то вроде этого:

  System.out.println («out»); * System.err.  println ("err"); * System.out.println ("out"); * System.err.println ("err"); * System.out.flush ();  System.err.flush ();   

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

Как его решить

Здесь может возникнуть дизайн кода и архитектура; вы вроде бы не решаете этого. Чтобы обойти это, вы должны сделать его более эффективным для буферизации печати / флеша, печати буфера или флеша, чем буфер, а затем сброс. Это, скорее всего, соблазнит вас неудачным дизайном. Если для вас важно, как вывести его упорядоченно, я предлагаю вам попробовать другой подход. For-looping с .flush () - это один из способов взломать его, но вы все еще взламываете функцию JVM, чтобы повторно упорядочить и оптимизировать свой код для вас.

* Я не могу проверить, что буфер, который вы добавили в первую очередь, сначала будет печататься первым, но, скорее всего, будет.

24
ответ дан Gemtastic 16 August 2018 в 05:45
поделиться

Я использовал поток для вывода вывода System.out и System.err последовательно:

  for (int i = 0; i & lt; 5; i ++) {try {Thread.  сна (100);  System.out.print ( "OUT");  Thread.sleep (100);  System.err.print ( "ERR");  } catch (InterruptedException ex) {System.out.println (ex.getMessage ());  }}  
0
ответ дан Milan Paudyal 16 August 2018 в 05:45
поделиться

Два println оператора обрабатываются двумя разными потоками. Результат снова зависит от того, в какой среде вы запускаете код. Например, я выполнил следующий код в IntelliJ и в командной строке по 5 раз.

  public class Test {public static void main (String [] args) {for (int i = 0; i & lt; 10; i ++) {System.out.print ("OUT")  ;  System.err.print («ERR»);  }}}  

Это приводит к следующему выводу: Commandline

  OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR  OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR  OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR OUT ERR  

IntelliJ:

  ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR  ERR ERR ERR ERR ERR OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT ERR ERR ERR ERR ERR ERR ERR ERR ERR ERR OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT OUT ERR ERR ERR ERR ERR  ERR ERR ERR ERR ERR  

Я полагаю, что разные среды обрабатывают буфер иначе. Один из способов увидеть, что эти потоки обрабатываются различными потоками, - это добавить в цикл инструкцию sleep . Вы можете попробовать изменить значение, которое вы установили для сна, и посмотреть, что это infact, обрабатываемые разными потоками.

  public class Test {public static void main (String [] args) {for (  int i = 0; i & lt; 10; i ++) {System.out.print ("OUT");  System.err.print («ERR»);  try {Thread.sleep (500);  } catch (InterruptedException e) {e.printStackTrace ();  }}}}  

Выход в этом случае оказался

  OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR ERR OUT OUT ERR ERR OUT  OUT ERR OUT ERR OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR OUT ERR ERR OUT ERR OUT OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR OUT ERR ERR OUT ERR OUT ERR OUT OUT ERR ERR OUT  OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR OUT OUT ERR OUT ERR OUT ERR ERR OUT OUT ERR ERR OUT OUT ERR ERR OUT ERR OUT OUT ERR OUT ERR  

Один из способов заставить его для печати в том же порядке будет использоваться .flush () , который работал для меня. Но это не то, что не все получают с ним правильные результаты.

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

1
ответ дан Rakesh 16 August 2018 в 05:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: