Что нового

[Массивы] Оптимизация работы с массивом

_MrSokol_

Новичок
Сообщения
3
Репутация
0
Приветствую!
Собственно, есть пару вопросов, касательно оптимизации работы скрипта. Задача в чем: есть большой файл, который загружается в массив. В скрипт подается строка, которую нужно сравнить со строками в массиве. Если файл небольшой (примерно 500кб) то все работает в пределах нужной производительности. Если файл больше, то начинаются просадки, а так как массив разрастается все дальше и дальше, то скорость также падает. Примерно скрипт выглядит так:
Код:
$file = FileOpen('test')
$memFile = _ReadFileInMemory($file)
$sCode = 'BD35JDOU37'
$return = _Check($memFile, $sCode)

Func _Check($memFile, $sCode)
	Local $i = 0
	While 1
		$string = $memFile[$i]
		If $string == '' Then
            $memFile[$i] = $sCode
			Return 1
		EndIf
		If $string == $sCode Then 
			Return 0
		EndIf
		$i += 1
	Wend
EndFunc

Func _ReadFileInMemory($file)
	Local $i = 0
	While 1
		$memFile[$i] = FileRead($file, 10)
		If @error = -1 Then
			ExitLoop
		EndIf
		$i += 1
	Wend
    Return($memFile)
EndFunc

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

Z_Lenar

Продвинутый
Сообщения
209
Репутация
52
_MrSokol_ [?]
Если файл больше, то начинаются просадки, а так как массив разрастается все дальше и дальше, то скорость также падает.
1. Считывать в массив обязательно?
2. Если нет то можно примерно так:
Код:
$buff = FileRead('test')
$pos = StringInStr($buff, 'BD35JDOU37')
While Not @error
	If Mod($pos, 10) = 0 Then
		; Совпадение кратное 10 найдено
	Else
		$pos = Round($pos / 10 + 1) * 10
	EndIf
	$pos = StringInStr($buff, 'BD35JDOU37', Default, Default, $pos)
WEnd
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Честно говоря, в голове противоречивые мысли...
С одной стороны непонятно - какая оптимизация?! почему вы решили что этот скрипт вообще работает?
С другой стороны текст сообщения (не скрипт) написан довольно грамотно.
:scratch:

_MrSokol_, давайте по порядку.
Скрипт явно не весь... :shaman_s_bubnom:

Почему вы читаете из файла по 10 символов? И как это вяжется с задачей - сравнить строку со строками в массиве?
Возникает вопрос: а как вообще выглядит этот файл? Какова его структура?
Зачем вы сами пишете функцию чтения файла в массив? Почему не пользуетесь готовой
Код:
_FileReadToArray


Зачем вам вообще понадобился массив? Какие манипуляции вы собираетесь производить с элементами массива? Задача какая, отватить ДА/НЕТ? Входит/Не входит? Так для этого массив не нужен.

Вот это вообще не понятно...
Код:
Func _Check($memFile, $sCode)
            $memFile[$i] = $sCode
            Return 1


_MrSokol_, вы сформулируйте чётко задачу - что вам нужно чтобы скрипт делал?
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
_MrSokol_
Есть готовые движки Scripting.Dictionary. Смысл такой: загружаете массив в виде ключей в объект Scripting.Dictionary. Потом вызовом Exists проверяете существование ключа BD35JDOU37 и если он уже там есть то True, иначе False. Это работает мегабыстро.
 
Автор
M

_MrSokol_

Новичок
Сообщения
3
Репутация
0
Спасибо всем за ответы, прочитал про Scripting.Dictionary, попробую потом сделать. Вообще, я писал немного сумбурно, поэтому, возможно, меня немного не так поняли :smile: Попробую внести ясность и пройтись по вопросу еще раз:
[list type=decimal]
[*]Скрипт, который я выложил выше - это пример с кусками кодами, выдранными из рабочего скрипта. Почему я решил что проблема с производительностью связанна с массивом? Потому что сейчас в рабочем скрипте массив разросся до 300 тыс. элементов и одна итерация скрипта выполняется примерно 0.9 сек. При комментировании функции с массивом скрипт выполнятся в доли секунды.
[*]
Зачем вы сами пишете функцию чтения файла в массив? Почему не пользуетесь готовой
Код:
_FileReadToArray
Потому что когда писал скрипт был глуп :smile: и не смотрел справку. А когда дописал, полез в справку и увидел функцию - хлопнул себя по лбу, но переписывать не стал, пока работает, хотя, возможно, стоит.
[*]Ну и собственно сама задача, еще раз и более полно:
Есть какой-то ключ, который генерируется в теле скрипта. Есть файл, который сохраняет эти ключи. Файл, по сути, никак не организован, просто сплошной набор текста, по типу:
BD35JDOU37AF78YTNB23 и т.д.
Можно впринципе поставить разделители, но мне это не нужно, я туда смотреть не собираюсь. Что нужно сделать: надо считать из файла ключ и сравнить его со сгенерированным ключом. Если ключи не совпали, то записать его в файл, если совпали - сгенерировать новый. И так каждый раз. Так как ключи по длине одинаковы (10 символов) то считываю я из файла 10 символов. Мне важна скорость всего это дела - файл со временем ставиться очень большим (порядка 1 млн. элементов) и желательно чтобы это миллион проходился за 0.5 - 1 сек. Первое, что я подумал, что обращение к памяти будет быстрее постоянного чтения с диска, поэтому и засунул файл в массив. Впринципе, если чтение с диска будет быстрее то можно спокойно отказаться от массива, это вообще не критично. Задача достаточно тривиальна, но мне как человеку, который в работе с массивами ничего кроме "пузырьковая сортировка" не слышал, это сложно :smile:
[/list]
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Ну, я бы сделал где-то так.

Код:
$sSpliter = "|"
$sCode = 'BD35JDOU37'

$oText = FileRead('test')

If StringInStr($oText,$sCode) = 0 Then
	$file = FileOpen('test',1)
	FileWrite($file,$sCode&$sSpliter)
	FileClose($file)
EndIf


Но! Есть пара "но".
1. Необходемо реорганизовать существующий файл и поставить разделители между уже записанными кодами.
2. Файл не может превышать 2147483647 символов.
 
Автор
M

_MrSokol_

Новичок
Сообщения
3
Репутация
0
Спасибо, проверю, отпишусь как работает


Добавлено:
Сообщение автоматически объединено:

C2H5OH
Еще раз спасибо, проверил, действительно работает быстро - 100 тыс. элементов пролетел за 0.2 секунды. Откажусь от массива, и сделаю через чтение/запись в файл. При 500 тыс. элементов проход занимает около 0.8 секунды. Можно ли еще что-то выжать по производительности или это уже предел AutoIt? Может, стоит посмотреть в сторону чего-то другого? Время пока есть свободное, надо образовываться помаленьку.
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
Когда то эту задачу решали для glax24 на конференции в джаббере. Нашёл пример:
Код:
$vData = FileRead(@ScriptDir & '\file.txt')
$vData = StringRegExp($vData, '.{10}', 3) ; читаем по 10 символов в массив
For $i = 0 To UBound($vData) - 1
	Assign($vData[$i], -2, 2) ; Создаём переменные
Next
$vData = ''
$sCh = 'BD35JDOU37'
$timer = TimerInit()
$s = IsDeclared($sCh)
$timer = TimerDiff($timer)
MsgBox(0, $s, 'Время : ' & Round($timer, 2) & ' мсек')
If $s Then
	MsgBox(0, 'Yes', 'Yes')
Else
	MsgBox(0, 'No', 'No')
EndIf


Ещё можно на SQLite сделать.
 

Z_Lenar

Продвинутый
Сообщения
209
Репутация
52
_MrSokol_ [?]
Откажусь от массива, и сделаю через чтение/запись в файл.
Я же отписал точно такой же вариант как у C2H5OH но без разделителей в предыдущем посте. :-X

Вообще, если программа должна перебирать пароли (а по-моему она этим и занимается), то не проще ли использовать хэш суммы? Или переделать генератор для последовательного перебора. А то выходит он рандомно генерит значения, а вы потом перепроверяете. :shok:
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
Вообще-то Assign и Scripting.Dictionary работают быстрее чем StringInStr. Причина в том что StringInStr не перепрыгивает к следующему разделителю, а проверяет каждый символ, а предыдущие проверяют первый символ и если он не совпадает то прыжок к следующему. Во вторых в этих движках изначально устроена сортировка и ключ скорее всего не будет искаться после прохождения наибольшего совпадения. Например если BD35JDOU37 прошел BD35J и натолкнулся на BD35R, то учитывая сортировку дальше проверять нет смысла. Просто это логически идеальная среда для этой задачи.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Если диск быстрый то можно искать сразу в файле.
Проверьте эту функцию, может оказаться быстрее. Возвращает True если данные найдены в файле
Код:
$result = in_file('file.txt','1111111111')
MsgBox(0,0,$result)
Func in_file($file_name, $data_check)
	Local $file_handle = FileOpen($file_name)
	Do
		If FileRead($file_handle, 10) = $data_check Then Return FileClose($file_handle) Or True
	Until @error
	FileClose($file_handle)
EndFunc

Или так :smile: Тут в цикле нет проверок на If ...
Код:
Func in_file($file_name, $data_check)
	Local $file_handle = FileOpen($file_name)
	Do
	Until FileRead($file_handle, 10) = $data_check Or @error
	If @error Then
		FileClose($file_handle)
		Return False
	Else
		FileClose($file_handle)
		Return True
	EndIf
EndFunc
 

MrSokol

Новичок
Сообщения
11
Репутация
0
Всем спасибо за ответы и критку, решил проблему.
Воспользовалься примером AZJIO (очень хороший пример, красиво сделано), проверил остальные примеры, но остановился на Assign + IsDeclared (работает очень быстро, 0.042 сек по таймеру). Еще раз всем спасибо.
Вообще, если программа должна перебирать пароли (а по-моему она этим и занимается), то не проще ли использовать хэш суммы?
Потому что это не брутер. Если бы мне нужен был брутер, я бы посмотрел в сторону уже написанных программ на CUDA/Opencl, вам не кажется что это уже стандарт для брутеров, почти все на видеоадаптеры ушли.
 

Z_Lenar

Продвинутый
Сообщения
209
Репутация
52
MrSokol
"Нам" не кажется.

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