Comments 33
Скрытый текст
На хабре целая статья про этот изврат была, нагуглят быстро :)
Вопрос третий
Можно ли в Java создать класс (именно класс, не интерфейс) без единого конструктора, даже приватного?
На хабре целая статья про этот изврат была, нагуглят быстро :)
0
Скрытый текст
1. Math.random использует обычный Random, который очень плохо работает в параллельном режиме. Использовать его с parallelSetAll имеет мало смысла: будет высокий контеншн. Нужен ThreadLocalRandom
3.
Работает в javac, не работает в ecj. Я про это писал здесь. Второй вариант — сгенерировать класс на лету, например, с помощью ASM, как это сделал LSD.
Над вторым ща подумаю.
3.
public class NoConstructor {
private static class Nested {
private Nested() {}
}
public static void main(String[] args) throws Exception {
new Nested();
System.out.println(Class.forName("NoConstructor$1").getDeclaredConstructors().length);
}
}
$ javac NoConstructor.java
$ java NoConstructor
0
Работает в javac, не работает в ecj. Я про это писал здесь. Второй вариант — сгенерировать класс на лету, например, с помощью ASM, как это сделал LSD.
Над вторым ща подумаю.
+7
Скрытый текст
Во втором для приватного метода javac сгенерирует аксессор, который увеличит глубину вложенности вызовов на один. Видимо, максимальная глубина инлайна ограничена 10. Пытаюсь найти опцию JVM, которая это контролирует.
+2
Скрытый текст
Ну да, предположение правильное. С +PrintCompilation +PrintInlining вижу такое:
А вот getPublic пролазит:
210 263 3 org.sample.Modifiers::testGetPrivate (7 bytes)
@ 3 org.sample.Modifiers$Inner::access$000 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getPrivate (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX9 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX8 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX7 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX6 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX5 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX4 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX3 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX2 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX1 (5 bytes) inlining too deep
А вот getPublic пролазит:
168 261 3 org.sample.Modifiers::testGetPublic (7 bytes)
@ 3 org.sample.Modifiers$Inner::getPublic (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX9 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX8 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX7 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX6 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX5 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX4 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX3 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX2 (5 bytes)
@ 1 org.sample.Modifiers$Inner::getX1 (5 bytes)
+4
Первая задача
Не джавист, но первая мысль при взгляде на
о том, что скорее всего Math.random() в java не очень хорош в concurrency, о чём можно почитать в документации и убедиться. Соответсвенно, использовать ThreadLocalRandom
public static double[] getRandomVector(int size) {
double[] vector = new double[size];
Arrays.parallelSetAll(vector, i -> Math.random());
return vector;
}
о том, что скорее всего Math.random() в java не очень хорош в concurrency, о чём можно почитать в документации и убедиться. Соответсвенно, использовать ThreadLocalRandom
+2
Мои непричёсанные мысли
1. Причина в том, что вся эта штука работает, по сути, последовательно (будет затор у Math.random()). Как сделать правильно и параллельно — сказать не могу, потому что не знаю, как сделать несколько датчиков случайных чисел, инициализированных чем-то более-менее независимым. Думаю, даже если сделать код простым последовательным, всё равно будет быстрее.
2. Точно не в курсе, но, думаю, какое-то хитрое поведение оптимизатора, который понимает, что getPublic() можно свернуть в return x; а getPrivate() — почему-то нет.
3. На уровне байткода, разумеется, можно. На уровне языка… думаю, что-нибудь наподобие внутреннего private abstract без нестатических данных.
2. Точно не в курсе, но, думаю, какое-то хитрое поведение оптимизатора, который понимает, что getPublic() можно свернуть в return x; а getPrivate() — почему-то нет.
3. На уровне байткода, разумеется, можно. На уровне языка… думаю, что-нибудь наподобие внутреннего private abstract без нестатических данных.
0
Ответ
все три мысли — в правильном направлении! Копайте дальше!
0
Я подсмотрел правильные ответы.
З.Ы. Если сработало, да не так — задача не решена.
Заголовок спойлера
По поводу первого — надо смотреть реализацию threadLocalRandom. Сработает-то сработает, но насколько качественны случайные числа, которые оно выдаст?
Второе — ключи компилятора. В такие дебри не совался.
А третье — просто не было времени запустить NetBeans и посмотреть.
Второе — ключи компилятора. В такие дебри не совался.
А третье — просто не было времени запустить NetBeans и посмотреть.
З.Ы. Если сработало, да не так — задача не решена.
0
Вот на вчерашней Никитиной задаче я затупил, хотя про слоты всё знал. Ну да, сейчас понял. Достаточно первый пример модифицировать вот так, и он заработает:
Мораль: пойду на доклад к Никите :D
public class OOM1 {
private static final int SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.55);
public static void main(String[] args) {
{
byte[] bytes = new byte[SIZE];
System.out.println(bytes.length);
}
byte[] bytes1 = null;
bytes1 = new byte[SIZE];
System.out.println(bytes1.length);
System.out.println("I allocated memory successfully");
}
}
Мораль: пойду на доклад к Никите :D
+3
Спасибо что теперь хоть по Java, без всяких там Springов Groovyей!
Мой ответы
По первой задачи Math.random() синхронизируется внутри, не очень подходит для параллелизации. Видимо нужно задействовать ThreadLocalRandom().
Во второй задаче — всё просто, вызов private будет обёрнут ещё в access метод, соответственно лишних overhead.
По третьей — тут уж я не такой знаток. Наверняка можно какой-нибудь библиотекой кодогенерации так сделать. Получить список констркторов, все удалить и сохранить. А вот чтобы из Java. Немного гуглежа и я нашёл обсуждение на StackOveflow — да, javac добавляет конструктор по умолчанию, это положено по спецификации. А при создании java байткода вручную таких ограничений нет, спецификация класс-файла не обязывает иметь конструктор.
Во второй задаче — всё просто, вызов private будет обёрнут ещё в access метод, соответственно лишних overhead.
По третьей — тут уж я не такой знаток. Наверняка можно какой-нибудь библиотекой кодогенерации так сделать. Получить список констркторов, все удалить и сохранить. А вот чтобы из Java. Немного гуглежа и я нашёл обсуждение на StackOveflow — да, javac добавляет конструктор по умолчанию, это положено по спецификации. А при создании java байткода вручную таких ограничений нет, спецификация класс-файла не обязывает иметь конструктор.
+1
Заголовок спойлера
1 — параллельное заполнение массива будет медленным не столько из-за Random, сколько из-за кэша процессора.
2 — Разница в том, что для private метода генерируется специальный метод access (предполагаю, т.к. внутренний класс хранится в отдельном class файле, иначе никак):
3 — не хочу даже думать над этим извратом, один вопрос — зачем???
2 — Разница в том, что для private метода генерируется специальный метод access (предполагаю, т.к. внутренний класс хранится в отдельном class файле, иначе никак):
public int testGetPrivate();
Code:
0: getstatic #2 // Field inner:LModifiers$Inner;
3: invokestatic #3 // Method Modifiers$Inner.access$000:(LModifiers$Inner;)I
6: ireturn
public int testGetPublic();
Code:
0: getstatic #2 // Field inner:LModifiers$Inner;
3: invokevirtual #4 // Method Modifiers$Inner.getPublic:()I
6: ireturn
.....
static int access$000(Modifiers$Inner);
Code:
0: aload_0
1: invokespecial #1 // Method getPrivate:()I
4: ireturn
3 — не хочу даже думать над этим извратом, один вопрос — зачем???
-1
1. Поясните, что вы имеете ввиду
+1
Заголовок спойлера
1 — я думал сперва про spatial locality (подумал, тут подвох и не может быть задача на просто знание JavaDoc).
Тесты заставили меня поменять свои взгляды gist.github.com/relgames/840d89280237acd4c385
На малых значениях последовательное заполнение действительно быстрее параллельного, но не из-за кэша, а из-за расходов на параллельность. При увеличении размера массива параллельная обработка становится быстрее. В целом, это доказывает рассуждения о том, что parallelStream() нужен не везде, а в тех случаях, когда элементов много либо же когда операции долгие.
Неожиданным открытием было то, что ThreadLocalRandom работает быстрее Random всегда и везде — на больших, маленьких, параллельных и последовательных операциях. Подозреваю, это из-за синхронизации в Random.
Тесты заставили меня поменять свои взгляды gist.github.com/relgames/840d89280237acd4c385
Benchmark (size) Mode Cnt Score Error Units
MyBenchmark.randomParallel 100 avgt 5 20.629 ± 1.920 us/op
MyBenchmark.randomSequential 100 avgt 5 2.996 ± 0.569 us/op
MyBenchmark.threadLocalRandomParallel 100 avgt 5 4.336 ± 0.172 us/op
MyBenchmark.threadLocalRandomSequential 100 avgt 5 0.351 ± 0.086 us/op
MyBenchmark.randomParallel 1000 avgt 5 166.651 ± 52.215 us/op
MyBenchmark.randomSequential 1000 avgt 5 28.984 ± 7.477 us/op
MyBenchmark.threadLocalRandomParallel 1000 avgt 5 7.591 ± 0.420 us/op
MyBenchmark.threadLocalRandomSequential 1000 avgt 5 3.178 ± 0.683 us/op
MyBenchmark.randomParallel 10000 avgt 5 1542.161 ± 710.309 us/op
MyBenchmark.randomSequential 10000 avgt 5 286.011 ± 60.862 us/op
MyBenchmark.threadLocalRandomParallel 10000 avgt 5 18.001 ± 3.008 us/op
MyBenchmark.threadLocalRandomSequential 10000 avgt 5 33.620 ± 4.221 us/op
MyBenchmark.randomParallel 10000000 avgt 5 1511798.753 ± 477838.982 us/op
MyBenchmark.randomSequential 10000000 avgt 5 275049.387 ± 45049.956 us/op
MyBenchmark.threadLocalRandomParallel 10000000 avgt 5 29184.447 ± 34445.026 us/op
MyBenchmark.threadLocalRandomSequential 10000000 avgt 5 37231.631 ± 15880.473 us/op
На малых значениях последовательное заполнение действительно быстрее параллельного, но не из-за кэша, а из-за расходов на параллельность. При увеличении размера массива параллельная обработка становится быстрее. В целом, это доказывает рассуждения о том, что parallelStream() нужен не везде, а в тех случаях, когда элементов много либо же когда операции долгие.
Неожиданным открытием было то, что ThreadLocalRandom работает быстрее Random всегда и везде — на больших, маленьких, параллельных и последовательных операциях. Подозреваю, это из-за синхронизации в Random.
+1
Consider using S.parallelStream().operation(F) instead of S.stream().operation(F) when…gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html
The total time to execute the sequential version exceeds a minimum threshold. These days, the threshold is roughly (within a factor of ten of) 100 microseconds across most platforms.
А также недавнее упоминание на русском:
— А части ли случаи, когда Stream API проигрывает по перфомансу классическому (Collections) API?habrahabr.ru/company/jugru/blog/255219
Куксенко: Как правило, Stream API проигрывает по производительности классическому API просто потому что Stream API подразумевает под собой некоторый оверхед, некоторые действия, которые мы обязаны сделать для того чтобы сконструировать хороший стрим и начать с ним работать. Вопрос: что мы выиграем, сделав эти некоторые затраты? Без параллелизации с точки зрения перфоманса тут нечего выигрывать, за исключением более компактного и лучше читаемого кода. Вопрос параллелизации, об этом тоже много говорилось, и писались различные примеры, шаблоны, графики, когда лучше переходить.
Сейчас самая осторожная оценка, которая выше нашей внутренней оценки, и которая, в принципе, достаточно разумна, и ее предлагал Даг Ли: если количество параллелизуемой работы в общем случае превышает 100 миллисекунд, то, параллелизуя ее, можно получить выигрыш с вероятностью 99%. Если количество работы меньше 100 миллисекунд, лучше не заморачиваться параллелизацией. По нашим замерам это 10 миллисекунд, но там уже… Это безопасная граница.
Кстати, 23derevo, выходит что Walrus либо оговорился (100 микросекунд/миллисекунд), либо вы неправильно расшифровали.
Неожиданным открытием было то, что ThreadLocalRandom работает быстрее Random всегда и везде — на больших, маленьких, параллельных и последовательных операциях. Подозреваю, это из-за синхронизации в Random.Да :)
+1
Скрытый текст
Результат интересный, хотя в принципе логичный. В ThreadLocalRandom сид хранится в поле класса Thread, которое ещё и помечено как Contended. То есть тут самая обычная запись в память, никакой синхронизации, никаких барьеров и хорошая поддержка со стороны кэша процессора. В обычном Random нет синхронизации, но есть AtomicLong, который обновляется через CAS. Даже в одном потоке это дополнительные расходы.
С сегодняшнего дня перехожу на ThreadLocalRandom!
С сегодняшнего дня перехожу на ThreadLocalRandom!
0
а у тебя так много рандома под контеншеном? ;)
Math.random() компактнее выглядит, глаз не режет. Вот увидит любой из твоих менее шарящих коллег ThreadLocalRandom и заподозрит подвох. Начнет копать не туда… Тебе оно нада?
Math.random() компактнее выглядит, глаз не режет. Вот увидит любой из твоих менее шарящих коллег ThreadLocalRandom и заподозрит подвох. Начнет копать не туда… Тебе оно нада?
0
Math.random() — это вообще плохо. Во-первых, с него можно получить только дробные числа от 0 до 1. Это требуется не очень часто. Чаще нужно Random.nextInt(a, b). А таскать где-то созданный Random на протяжении всего алгоритма может быть не очень удобно. В результате люди либо пишут что-то уродливое вроде (int)(Math.random()*(b-a))+a. В этом случае ThreadLocalRandom.current().nextInt(a, b) гораздо меньше режет глаз.
По хорошему стохастический алгоритм должен принимать на вход фабрику рэндомов. Тогда можно и стабильного поведения для тестов добиться, и тредлокал использовать, если скорость нужна.
По хорошему стохастический алгоритм должен принимать на вход фабрику рэндомов. Тогда можно и стабильного поведения для тестов добиться, и тредлокал использовать, если скорость нужна.
+1
Скрытый текст
2 — это только часть ответа. Вызовите getX8 вместо getX9 из обоих методов, и производительность будет одинаковой.
+1
Кстати, про regexp — не могу пройти мимо:
Нет, нельзя это фиксать! Я не хочу видеть ни первый, ни второй код в своём приложении. Надо написать грамматику, хоть antlr, хоть на худой конец javacc. Это очень просто, куча примеров в сети, и главное — легко поддерживать и дополнять новыми фичами.
Я неплохо читаю регексы, я даже кроссворд разгадал примерно за час. Но моё, возможно, холиварное мнение в том, что если начинают требоваться штуки с бэктрекингом, то убирайте это и пишите грамматику.
Пофиксить можно двумя способами:
Possessive quantifier:
- Pattern.compile("/\\*(?:[^*]|\\*[^/])*+\\*/")
Independent group:
- Pattern.compile("/\\*(?>,[^*]|\\*[^/])*\\*/")
Нет, нельзя это фиксать! Я не хочу видеть ни первый, ни второй код в своём приложении. Надо написать грамматику, хоть antlr, хоть на худой конец javacc. Это очень просто, куча примеров в сети, и главное — легко поддерживать и дополнять новыми фичами.
Я неплохо читаю регексы, я даже кроссворд разгадал примерно за час. Но моё, возможно, холиварное мнение в том, что если начинают требоваться штуки с бэктрекингом, то убирайте это и пишите грамматику.
+8
Решение первой задачи
Проблема кода в том, что для большого числа потоков (их количество определяет JDK на основе, видимо, размера массива и прочего) и большого массива может оказаться, что есть contention, т.к. все потоки
будут использовать общий экземпляр класса Random. Там, конечно, не просто synchronize, а CAS вызовы внутри в nextDouble(),
но тем не менее, авторы JDK8 предлагают в таких случаях использовать ThreadLocalRandom.
Просто, чтобы потоки не дрались между собой. Соответственно фикс простой:
будут использовать общий экземпляр класса Random. Там, конечно, не просто synchronize, а CAS вызовы внутри в nextDouble(),
но тем не менее, авторы JDK8 предлагают в таких случаях использовать ThreadLocalRandom.
Просто, чтобы потоки не дрались между собой. Соответственно фикс простой:
public static double[] getRandomVector(int size) {
double[] vector = new double[size];
Arrays.parallelSetAll(vector, i -> ThreadLocalRandom.current().nextDouble());
return vector;
}
0
Sign up to leave a comment.
Новый суперблиц по Java-хардкору