Представление ThreadLocal переменной
сколько читается из ThreadLocal
переменная медленнее, чем из обычного поля?
более конкретно-это просто создание объекта быстрее или медленнее, чем доступ к ThreadLocal
переменной?
Я предполагаю, что это достаточно быстро, так что ThreadLocal<MessageDigest>
экземпляр намного быстрее, чем создание экземпляра MessageDigest
каждый раз. Но это также относится к байту[10] или байту[1000], например?
Edit: вопрос в том, что на самом деле происходит при вызове ThreadLocal
'ы сделать? Если что это просто поле, как и любое другое, тогда ответ будет "это всегда быстрее", верно?
6 ответов:
запуск неопубликованных ориентиры,
ThreadLocal.get
занимает около 35 циклов за итерацию на моей машине. Не очень много. В реализации Sun пользовательская линейная зондирующая хэш-карта вThread
картыThreadLocal
s к значениям. Поскольку он доступен только одному потоку, он может быть очень быстрым.выделение небольших объектов принимать такое же количество циклов, хотя из-за исчерпания кэша, вы можете получить несколько более низкие цифры в непрерывном цикле.
строительство
MessageDigest
вероятно, это будет относительно дорого. Он имеет изрядное количество государства и строительство идет черезProvider
механизм SPI. Вы можете быть в состоянии оптимизировать, например, клонирование или предоставленияProvider
.просто потому, что это может быть быстрее, чтобы кэш в
ThreadLocal
, а не создавать не обязательно означает, что производительность системы будет увеличиваться. У вас будут дополнительные накладные расходы, связанные с GC, который замедляет все.если ваше приложение не очень сильно использует
MessageDigest
вы можете рассмотреть возможность использования обычного потокобезопасного кэша вместо этого.
в 2009 году некоторые JVMs реализовали ThreadLocal, используя несинхронизированную хэш-карту в потоке.currentThread (объекта). Это сделало его чрезвычайно быстрым (хотя и не так быстро, как при использовании обычного доступа к полю, конечно), а также гарантировало, что объект ThreadLocal был убран, когда поток умер. Обновление этого ответа в 2016 году, кажется, больше всего (все?) новые виртуальные машины использовать так называемые с линейного пробирования. Я не уверен в производительности этих-но я не могу себе представить, что это так значительно хуже, чем предыдущая реализация.
конечно, новый объект () также очень быстр в эти дни, и сборщики мусора также очень хороши в восстановлении недолговечных объектов.
Если вы не уверены, что создание объекта будет дорогостоящим, или вам нужно сохранить какое-то состояние в потоке по потоку, вам лучше пойти на более простое выделение, когда это необходимо, и только переключиться на реализацию ThreadLocal, когда профилировщик говорит вам, что вам нужно.
хороший вопрос, я задавал себе этот вопрос в последнее время. Чтобы дать вам определенные числа, ниже приведены тесты (в Scala, скомпилированные практически в те же байт-коды, что и эквивалентный код Java):
var cnt: String = "" val tlocal = new java.lang.ThreadLocal[String] { override def initialValue = "" } def loop_heap_write = { var i = 0 val until = totalwork / threadnum while (i < until) { if (cnt ne "") cnt = "!" i += 1 } cnt } def threadlocal = { var i = 0 val until = totalwork / threadnum while (i < until) { if (tlocal.get eq null) i = until + i + 1 i += 1 } if (i > until) println("thread local value was null " + i) }
скачать здесь, были выполнены на двухъядерном процессоре AMD 4x 2.8 GHz и четырехъядерном i7 с гиперпоточностью (2.67 GHz).
вот эти цифры:
i7
технические характеристики: Процессор Intel i7 с 2х четырехъядерный @ 2.67 ГГц Тест: скала.нити.ParallelTests
имя теста: loop_heap_read
нить num.: 1 Всего тестов: 200
время выполнения: (показывает последние 5) 9.0069 9.0036 9.0017 9.0084 9.0074 (в среднем = 9.1034 мин = 8.9986 Макс = 21.0306 )
нить num.: 2 Всего тестов: 200
время выполнения: (показывает последние 5) 4.5563 4.7128 4.5663 4.5617 4.5724 (в среднем = 4.6337 min = 4.5509 max = 13.9476)
нить num.: 4 Всего тестов: 200
время выполнения: (показывает последние 5) 2.3946 2.3979 2.3934 2.3937 2.3964 (в среднем = 2.5113 мин = 2.3884 Макс = 13.5496)
нить num.: 8 Всего тестов: 200
время выполнения: (показывает последние 5) 2.4479 2.4362 2.4323 2.4472 2.4383 (в среднем = 2.5562 мин = 2.4166 Макс = 10.3726 )
имя теста: threadlocal
нить num.: 1 Всего тестов: 200
время выполнения: (показывает последние 5) 91.1741 90.8978 90.6181 90.6200 90.6113 (avg = 91.0291 min = 90.6000 max = 129.7501 )
нить num.: 2 Всего тестов: 200
время выполнения: (показывает последние 5) 45.3838 45.3858 45.6676 45.3772 45.3839 (в среднем = 46.0555 мин = 45.3726 Макс = 90.7108 )
нить num.: 4 Всего тестов: 200
время выполнения: (показывает последние 5) 22.8118 22.8135 59.1753 22.8229 22.8172 (avg = 23.9752 min = 22.7951 max = 59.1753)
нить num.: 8 Всего тестов: 200
время выполнения: (показывает последние 5) 22.2965 22.2415 22.3438 22.3109 22.4460 (в среднем = 23.2676 мин = 22.2346 Макс = 50.3583)
AMD
спецификации: AMD 8220 4х двухъядерный с частотой 2,8 ГГц Тест: скала.нити.ParallelTests
имя теста: loop_heap_read
общая работа: 20000000 Нить числ.: 1 Всего тестов: 200
время выполнения: (показывает последние 5) 12.625 12.631 12.634 12.632 12.628 (в среднем = 12.7333 мин = 12.619 Макс = 26.698)
имя теста: loop_heap_read Всего работ: 20000000
время выполнения: (показывает последние 5) 6.412 6.424 6.408 6.397 6.43 (в среднем = 6.5367 мин = 6.393 Макс = 19.716 )
нить num.: 4 Всего тестов: 200
время выполнения: (показывает последние 5) 3.385 3.385 4.298 9.7 6.535 (СР = 5.6079 мин = 3.354 Макс = 21.603 )
нить num.: 8 Всего тестов: 200
время выполнения: (показывает последние 5) 5.389 5.795 10.818 3.823 3.824 (СР = 5.5810 мин = 2.405 Макс = 19.755 )
имя теста: threadlocal
нить num.: 1 Всего тестов: 200
время выполнения: (показывает последние 5) 200.217 207.335 200.241 207.342 200.23 (в среднем = 202.2424 мин = 200.184 Макс = 245.369 )
нить num.: 2 Всего тестов: 200
время выполнения: (показывает последние 5) 100.208 100.199 100.211 103.781 100.215 (в среднем = 102.2238 мин = 100.192 Макс = 129.505)
нить num.: 4 Всего тестов: 200
время выполнения: (показывает последние 5) 62.101 67.629 62.087 52.021 55.766 (в среднем = 65.6361 мин = 50.282 Макс = 167.433 )
нить num.: 8 Всего тестов: 200
время выполнения: (показывает последние 5) 40.672 74.301 34.434 41.549 28.119 (в среднем = 54.7701 мин = 28.119 Макс = 94.424 )
резюме
локальный поток составляет около 10-20x, что из кучи чтения. Он также, кажется, хорошо масштабируется на этой реализации JVM и этих архитектурах с количеством процессоров.
здесь идет еще один тест. Результатов показывает, что ThreadLocal немного медленнее, чем обычное поле, но в том же порядке. Примерно на 12% медленнее
public class Test { private static final int N = 100000000; private static int fieldExecTime = 0; private static int threadLocalExecTime = 0; public static void main(String[] args) throws InterruptedException { int execs = 10; for (int i = 0; i < execs; i++) { new FieldExample().run(i); new ThreadLocaldExample().run(i); } System.out.println("Field avg:"+(fieldExecTime / execs)); System.out.println("ThreadLocal avg:"+(threadLocalExecTime / execs)); } private static class FieldExample { private Map<String,String> map = new HashMap<String, String>(); public void run(int z) { System.out.println(z+"-Running field sample"); long start = System.currentTimeMillis(); for (int i = 0; i < N; i++){ String s = Integer.toString(i); map.put(s,"a"); map.remove(s); } long end = System.currentTimeMillis(); long t = (end - start); fieldExecTime += t; System.out.println(z+"-End field sample:"+t); } } private static class ThreadLocaldExample{ private ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<Map<String,String>>() { @Override protected Map<String, String> initialValue() { return new HashMap<String, String>(); } }; public void run(int z) { System.out.println(z+"-Running thread local sample"); long start = System.currentTimeMillis(); for (int i = 0; i < N; i++){ String s = Integer.toString(i); myThreadLocal.get().put(s, "a"); myThreadLocal.get().remove(s); } long end = System.currentTimeMillis(); long t = (end - start); threadLocalExecTime += t; System.out.println(z+"-End thread local sample:"+t); } } }'
выход:
0-запущенный образец поля
0-конец образца поля: 6044
0-запуск локального потока образца
0-конец потока локальный образец:6015
1-Запуск образца поля
1-конец образца поля: 5095
1-запуск локального потока образец
1-конец потока локальный образец: 5720
2-Запуск образца поля
2-Концевой образец поля: 4842
2-запуск потока локального образца
2-Концевой поток локальный образец: 5835
3-Запуск образца поля
3-Концевой образец поля: 4674
3-запуск локального потока образца
3-Концевой поток локальный образец: 5287
4-запуск образца поля
4-конечное поле образец:4849
4-запуск локального образца потока
4-Концевой поток локальный образец:5309
5-Запуск образца поля
5-Концевой образец поля: 4781
5-запуск локального потока образца
5-Концевой поток локальный образец: 5330
6-запуск образца поля
6-Концевой образец поля: 5294
6-запуск локального потока образца
6-Концевой поток локальный образец: 5511
7-бегущее поле образец
7-Концевой образец поля: 5119
7-запуск локального потока образца
7-конец потока локальный образец: 5793
8-запуск образца поля
8-Концевой образец поля: 4977
8-запуск локального потока образца
8-Концевой поток локальный образец:6374
9-запуск образца поля
9-конец образца поля: 4841
9-запуск локального образца потока
9-конец резьбы локальный образец:5471
поле avg: 5051
ThreadLocal avg: 5664
Env:
openjdk версия "1.8.0_131"
процессор Intel ® Core™ i7-7500U @ 2.70 GHz × 4
Ubuntu 16.04 LTS
@Pete является правильным тестом, прежде чем оптимизировать.
Я был бы очень удивлен, если построение MessageDigest имеет какие-либо серьезные накладные расходы по сравнению с actaully его использованием.
Мисс с помощью ThreadLocal может быть источником утечек и висячих ссылок, которые не имеют четкого жизненного цикла, как правило, я никогда не использую ThreadLocal без очень четкого плана, когда конкретный ресурс будет удален.
построить его и измерить его.
кроме того, вам нужен только один threadlocal, если вы инкапсулируете свое поведение переваривания сообщений в объект. Если вам нужен локальный MessageDigest и локальный байт[1000] для какой-либо цели, создайте объект с полем messageDigest и byte [] и поместите этот объект в ThreadLocal, а не по отдельности.