Как получить хорошую параллельную производительность чтения из диска

Это ваш выбор. В архиве веб-приложений Java (WAR) есть три способа:


1. Поместите его в classpath

. Чтобы вы могли загрузить его с помощью ClassLoader#getResourceAsStream() с относительным путем classpath:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("foo.properties");
// ...
Properties properties = new Properties();
properties.load(input);

Здесь foo.properties предполагается, что они будут помещены в один из корней, которые покрываются классом по умолчанию для webapp, например webapp /WEB-INF/lib и /WEB-INF/classes, /lib сервера или JDK / JRE /lib. Если файл свойств является webapp-специфическим, лучше всего поместить его в /WEB-INF/classes. Если вы разрабатываете стандартный проект WAR в среде IDE, поместите его в папку src (исходную папку проекта). Если вы используете проект Maven, поместите его в папку /main/resources.

Вы также можете поместить его где-то за пределы пути по умолчанию и добавить свой путь к пути к классам на сервере приложений. Например, Tomcat вы можете настроить его как свойство shared.loader в Tomcat/conf/catalina.properties.

Если вы поместили foo.properties в структуру пакета Java, например com.example, вам необходимо загрузить его как показано ниже

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("com/example/foo.properties");
// ...

Обратите внимание, что этот путь загрузчика класса контекста не должен начинаться с /. Только когда вы используете «относительный» загрузчик классов, например SomeClass.class.getClassLoader(), вам действительно нужно запустить его с помощью /.

ClassLoader classLoader = getClass().getClassLoader();
InputStream input = classLoader.getResourceAsStream("/com/example/foo.properties");
// ...

Однако видимость файла свойств зависит затем на класс загрузчика, о котором идет речь. Это видно только для того же загрузчика классов, что и тот, который загружал класс. Таким образом, если класс загружается, например, server common classloader вместо загрузчика классов webapp, а файл свойств находится внутри самого webapp, а затем он невидим. Загрузчик контекстного класса - это ваша самая безопасная ставка, поэтому вы можете поместить файл свойств «всюду» в путь к классам и / или вы намерены переопределить сервер, предоставленный с помощью webapp.


2. Поместите его в webcontent

Чтобы вы могли загрузить его с помощью ServletContext#getResourceAsStream() с относительным путем webcontent:

InputStream input = getServletContext().getResourceAsStream("/WEB-INF/foo.properties");
// ...

Обратите внимание, что я продемонстрировал для размещения файла в папке /WEB-INF, иначе он был бы общедоступным для любого веб-браузера. Также обратите внимание, что ServletContext находится в любом классе HttpServlet, доступном только унаследованным GenericServlet#getServletContext() и в Filter на FilterConfig#getServletContext() . Если вы не находитесь в классе сервлета, его обычно можно вводить через @Inject.


3. Поместите его в локальную файловую систему диска

. Чтобы загрузить его обычным способом java.io с использованием пути к файловой системе с абсолютным локальным диском:

InputStream input = new FileInputStream("/absolute/path/to/foo.properties");
// ...

Обратите внимание на важность использования абсолютный путь. Относительные пути локальной файловой системы на диске - это абсолютное отсутствие в веб-приложении Java EE. См. Также первую ссылку «См. Также».


Что выбрать?

Просто взвешивайте преимущества / недостатки в своих собственных мнениях ремонтопригодность.

Если файлы свойств «статические» и никогда не должны меняться во время выполнения, вы можете сохранить их в WAR.

Если вы предпочитаете редактировать файлы свойств извне веб-приложение без необходимости перестраивать и переустанавливать WAR каждый раз, а затем помещать его в путь класса вне проекта (при необходимости добавить каталог в путь к классам).

Если вы предпочитаете иметь возможность редактировать файлы свойств программно из веб-приложения с помощью метода Properties#store(), выведите его за пределы веб-приложения. Поскольку для Properties#store() требуется Writer, вы не можете использовать путь к файловой системе диска. Этот путь, в свою очередь, может быть передан в веб-приложение как аргумент VM или системное свойство. В качестве меры предосторожности никогда не используйте getRealPath() . Все изменения в папке развертывания будут потеряны при повторном развертывании по той простой причине, что изменения не будут возвращены в исходный файл WAR.

См. Также:

26
задан tzot 10 October 2008 в 22:06
поделиться

6 ответов

Проблема, кажется, находится в Windows I/O, планируя политику. Согласно тому, что я нашел здесь , существует много путей к O.S. для планирования дисковых запросов. В то время как Linux и другие могут выбрать между различными политиками, прежде чем Windows Vista был заблокирован в единственной политике: очередь FIFO, где все запросы, где splitted в блоках на 64 КБ. Я полагаю, что эта политика является причиной для проблемы, которую Вы испытываете: планировщик смешает запросы от двух потоков, вызывать непрерывный ищет между различными областями диска.
Теперь, хорошие новости - то, что согласно здесь и здесь , Vista представил более умный дисковый планировщик, где можно установить приоритет запросов и также выделить минимум badwidth для процесса.
плохие новости - то, что я не нашел способа изменить дисковую политику или буферный размер в предыдущих версиях Windows. Кроме того, даже если повышение дискового приоритета ввода-вывода Вашего процесса повысит производительность против других процессов, у Вас все еще есть проблемы Ваших потоков, конкурирующих друг против друга.
то, Что я могу предложить, должно изменить Ваше программное обеспечение путем представления самодельной политики доступа к диску.
, Например, Вы могли использовать политику как это в Вашем потоке B (подобный для Потока A):

if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again  

Вы могли использовать семафоры для проверки состояния, или Вы могли использовать счетчики perfmon для получения состояния очереди фактической дисковой емкости. Значения X и/или Y могли также быть автоматически настроены путем проверки фактических trasfer уровней и медленно изменить их, таким образом максимизировав пропускную способность когда выполнение приложения на различных машинах и/или O.S. Вы могли найти, что кэш, память или уровни RAID влияют на них способом или другой, но с автоматической настройкой Вас будет всегда получать лучшую производительность в каждом сценарии.

11
ответ дан Andrea Bertani 25 September 2019 в 08:23
поделиться

Я хотел бы добавить некоторые дальнейшие примечания в своем ответе. Все другие операционные системы не-Microsoft, которые мы протестировали, не страдают от этой проблемы. Linux, FreeBSD и Mac OS X (этот заключительный на различных аппаратных средствах) все ухудшаются намного более корректно с точки зрения совокупной пропускной способности при перемещении от одного потока до два. Linux, например, ухудшенный от ~45 мебибайт/секунда до ~42 мебибайт/секунда. Эти другие операционные системы должны читать, большие блоки файла между каждым ищут, и для этого не проводящий почти все их время, ожидая на диске для поиска.

Наше решение для Windows состоит в том, чтобы передать эти FILE_FLAG_NO_BUFFERING флаг к CreateFile и использовать большой (~16MiB) чтения в каждом вызове к ReadFile. Это является субоптимальным по нескольким причинам:

  • Файлы не становятся кэшируемыми, когда считано как это, таким образом, нет ни одного из преимуществ, которые обычно дает кэширование.
  • ограничения при работе с этим флагом намного более сложны, чем нормальное чтение (выравнивание буферов чтения для подкачки страниц границ, и т.д.).

(Как заключительный комментарий. Это объясняет, почему свопинг в соответствии с Windows таким образом адский? Т.е., Windows неспособен к выполнению IO в несколько файлов одновременно с любой эффективностью, поэтому в то время как свопинг всех других операций IO вынужден быть непропорционально медленным.)

<час>

Редактирование для добавления некоторой более подробной информации для Will Dean:

, Конечно, через эти различные аппаратные конфигурации необработанные числа действительно изменялись (иногда существенно). Проблемой однако является последовательная неисправность в производительности, которую только Windows переносит при перемещении от одного потока до два. Вот сводка протестированных машин:

  • Несколько рабочих станций Dell (Intel Xeon) различных возрастов, выполняющих Windows 2000, Windows XP (32-разрядный), и Windows XP (64-разрядный) с единственным диском.
  • А сервер Dell 1U (Intel Xeon) рабочий Windows Server 2003 (64-разрядный) с RAID 1+0.
  • рабочая станция HP (AMD Opteron) с (64-разрядным) Windows XP, и Windows Server 2003 и аппаратные средства RAID 5.
  • Мой домашний ПК без клейма (AMD Athlon64), выполняющий (32-разрядный) Windows XP, (64-разрядный) FreeBSD, и Linux (64-разрядный) с единственным диском.
  • Мой домашний MacBook (Intel Core1) под управлением Mac OS X, единственный диск SATA.
  • Мое домашнее Koolu пк, запускающий Linux. Весьма недостаточно мощный по сравнению с другими системами, но я продемонстрировал, что даже эта машина может превзойти Windows Server по характеристикам с RAID5 при выполнении многопоточного чтения с диска.

использование ЦП во всех этих системах было очень низким во время тестов, и антивирус был отключен.

я забыл упоминать прежде, но мы также попробовали нормальный Win32 CreateFile API FILE_FLAG_SEQUENTIAL_SCAN набор флага. Этот флаг не решил проблему.

5
ответ дан wilx 25 September 2019 в 08:23
поделиться

Действительно кажется немного странным, что Вы не видите различия через настоящий широкий спектр версий для Windows, и ничто между единственным диском и аппаратными средствами не совершает рейд 5.

Это - только 'шестое чувство', но это действительно делает меня сомнительным, что это - действительно простая проблема поиска. Кроме OS X и Raid5, все это было испытано та же машина - Вы попробовали другую машину? Ваше использование ЦП, в основном обнуляют во время этого теста?

, Каково самое короткое приложение, которое можно записать, который демонстрирует эту проблему? - Мне было бы интересно пробовать его здесь.

1
ответ дан Will Dean 25 September 2019 в 08:23
поделиться

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

0
ответ дан Stacey Richards 25 September 2019 в 08:23
поделиться

Вы используете IOCompletionPorts в соответствии с Windows? Windows через C++ имеет всестороннюю главу по этому предмету и по стечению обстоятельств, это также доступно на MSDN.

0
ответ дан graham.reeds 25 September 2019 в 08:23
поделиться

Paul - видел обновление. Очень интересный.

было бы интересно попробовать его на Vista или Win2008, поскольку люди, кажется, сообщают о некоторых значительных улучшениях ввода-вывода относительно них при некоторых обстоятельствах.

Мое единственное предложение о различном API должно было бы попробовать размещение в ОЗУ файлы - Вы попробовали это? К сожалению, на уровне 2 ГБ за файл, Вы не собираетесь быть способными отобразить несколько целых файлов на 32-разрядной машине, что означает, что это не вполне столь тривиально, как это могло бы быть.

0
ответ дан Will Dean 25 September 2019 в 08:23
поделиться
Другие вопросы по тегам:

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