Высокопроизводительный анализ текстовых файлов в .net - PullRequest
2 голосов
/ 20 марта 2010

Вот ситуация:

Я делаю небольшую прогу для разбора файлов журнала сервера.

Я протестировал его с файлом журнала с несколькими тысячами запросов (между 10000 - 20000 точно не знаю)

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

Это занимает больше всего ресурсов.

Методы, которые занимают больше всего времени процессора, - это те, которые (наихудшие в первую очередь виновные):

string.split - разбивает строковые значения на массив значений

string.contains - проверка, содержит ли пользовательский агент определенную строку агента. (определить идентификатор браузера)

string.tolower - различного назначения

streamreader.readline - читать файл журнала построчно.

string.startswith - определить, является ли строка строкой определения столбца или строкой со значениями

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

Теперь я работаю на быстром двухъядерном процессоре, и общее время, необходимое для загрузки упомянутого файла, составляет около 1 секунды.

Теперь это действительно плохо.

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

Так, каковы мои альтернативы? Если таковые имеются, потому что я думаю, что это просто ограничение .net, и я ничего не могу с этим поделать.

EDIT:

Если некоторые из вас, гуру, захотят взглянуть на код и найти проблему, вот мои файлы кода:

Функция, которая потребляет наибольшее количество ресурсов, безусловно, LogEntry.New Функция, которая загружает все данные, называется Data.Load

Общее количество созданных объектов LogEntry: 50 000. Время: 0,9 - 1,0 секунды.

Процессор: amd phenom II x2 545 3 ГГц.

не многопоточный

Ответы [ 6 ]

4 голосов
/ 20 марта 2010

Не видя ваш код, трудно понять, есть ли у вас какие-либо ошибки, которые стоят вам производительности. Не имея примеров данных, мы не можем разумно проводить эксперименты, чтобы увидеть, как мы себя чувствуем.

Каким был ваш словарный ключ раньше? Переход к многомерному массиву звучит как странное движение, но нам нужно больше информации, чтобы знать, что вы делали с данными раньше.

Обратите внимание, что если вы явно не распараллеливаете работу, наличие двухъядерного компьютера не будет иметь никакого значения. Если вы действительно связаны с процессором, то вы могли бы распараллелить - хотя вам нужно делать это осторожно; вполне вероятно, что вы захотите прочитать «кусок» текста (несколько строк) и попросить один поток проанализировать его, а не передавать по одной строке за раз. Результирующий код, вероятно, будет значительно сложнее.

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

РЕДАКТИРОВАТЬ: Хорошо, я быстро взглянул на код. Несколько мыслей ...

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

Однако для оптимизации процесса синтаксического анализа:

  • Я бы лично не продолжал проверять, находится ли StreamReader в конце - просто звоните ReadLine, пока результат не будет Nothing.
  • Если вы ожидаете, что строка "#fields" будет стоять первой, прочитайте ее вне цикла. Тогда вам не нужно видеть, есть ли у вас поля на каждой итерации.
  • Если вы знаете, что строка не пуста, возможно , что проверка на наличие первого символа '#' может быть быстрее, чем вызов line.StartsWith("#") - мне придется проверить.
  • Вы сканируете через поля каждый раз, когда запрашиваете дату, время, URI или пользовательский агент; вместо этого, когда вы анализируете строку "#fields", вы можете создать экземпляр нового LineFormat класса, который может справиться с с любыми именами полей, но специально запоминает индекс полей, который вы знаете, что вы собираюсь хотеть. Это также позволяет избежать копирования полного списка полей для каждой записи журнала, что довольно расточительно.
  • Когда вы разбиваете строку, вы получаете больше информации, чем обычно: вы знаете, сколько полей ожидать, и знаете, что разбиваете только один символ. Возможно, вы могли бы написать оптимизированную версию этого.
  • Может быть быстрее проанализировать поля даты и времени отдельно, а затем объединить результат, а не объединять их и затем анализировать. Я должен был проверить это.
  • Многомерные массивы значительно медленнее, чем одномерные. Если вы do хотите придерживаться идеи "копировать все имена полей для каждой записи", то стоит разделить ее на два массива: один для полей, другой для значений.

Возможно, есть и другие вещи, но, боюсь, у меня сейчас нет времени на них: (

2 голосов
/ 20 марта 2010

Вы уже просмотрели отображенные в память файлы ? (но в .NET 4.0)

РЕДАКТИРОВАТЬ : - Кроме того, возможно ли разделить эти большие файлы на более мелкие и проанализировать меньшие файлы. Это то, что мы сделали в некоторых наших больших файлах, и это было быстрее, чем анализ гигантских файлов.

1 голос
/ 20 марта 2010

Вы можете попробовать RegEx. Или измените бизнес-процесс так, чтобы файл можно было загружать с такой скоростью более удобно.

0 голосов
/ 20 марта 2010

Вы можете сделать несколько вещей:

Служба Windows, которая постоянно анализирует журнал при каждом его изменении. Тогда ваш пользовательский интерфейс запрашивает эту услугу.

Или вы можете анализировать его каждую минуту или больше и кэшировать результат, вам действительно нужно, чтобы он был в реальном времени? может быть, его нужно проанализировать только один раз?

0 голосов
/ 20 марта 2010

Рассматривали ли вы загрузку записей журнала в базу данных и выполнение запросов оттуда? Таким образом, вы сможете пропустить анализ записей журнала, которые вы уже сохранили в базе данных.

0 голосов
/ 20 марта 2010

Вы можете попробовать ленивую загрузку: например, читать файл по 4096 байт за раз, искать окончания строк и сохранять все окончания строк в массиве. Теперь, если какая-то часть вашей программы хочет LogEntry N, найдите начальную позицию этой строки, прочитайте ее и создайте объект LogEntry на лету. (Это немного проще для файлов с отображением в память.) По мере возможности оптимизации, если вызывающему коду обычно требуются последовательные объекты LogEnties, ваш код может, например, упреждающее чтение следующих 100 записей журнала автоматически. Вы можете кэшировать последние 1000 записей, к которым был получен доступ.

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