Что нового

Убрать замедление при переборе массива

mef-t

Осваивающий
Сообщения
306
Репутация
30
Добрый день.

При перебое массива, т.е. при прохождении в цикле по каждому элементу массива, и выполнении с этим массивом одних и тех же действий, выполнение с каждым элементом становится медленнее.

Возможно ли как то этого избежать?

Пример:
Код:
Local $tt = UBound($arrPhone)
	For $i = 1 To UBound($arrPhone) - 1
		$pData = _INetGetSource($arrPhone[$i]) ; $site[$z] & $arrPhone[$i]
		$pData = StringRegExpReplace($pData, '[\h-()+]', '')
		$arrList = StringRegExp($pData, '\n\d+(?=<div)', 1)
		If IsArray($arrList) Then
			$arrPhone[$i] = $arrList[0]
		EndIf
		_GUICtrlFFLabel_SetData($iLabel, 'Сбор данных: ' & Round(($i-1)*100/$tt, 2) & ' %')
	Next
При величине массива в 4000 элементов максимум я выжал 80% за 1 час. После этого скрипт зависает намертво.

Может нужно обновлять память, или что то еще?
Подскажите пожалуйста.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
1. Попробую, но подобную ситуацию наблюдал и без данной проверки.
2. Примерно 10000 КБ. Не увеличивается.




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

Подскажите, Может вместо "_INetGetSource()" быстрее использовать другую функцию получения исходного кода страницы?
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Внес правки в "StringRegExp". Стало работать в 2 раза быстрее, но все равно не получается.
Программа подвисает на каком нить проценте и все.
Есть идеи, на чаем из указанного куска кода система может конкретно повиснуть?

Могу предоставить весь код, но там содержимое для 18+.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Хорошо, мы будем с закрытыми глазами искать ошибку
Я про то, что это может быть запрещено правилами (которые я мог не доглядеть), или оскорбить личные чувства кого-нить.

Ниже весь код:
Код:
#include <INet.au3>
#include <array.au3>
#include <Excel.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include "GUIFFLabel.au3"

Global 	$pData
Global Const $xlUp = -4162
Global 	$xls_type = '.xls'
Global 	$site[2]
Global 	$param[2]
Global 	$title[2]
Global 	$lastPage[1]

Global $xls_file = '\kyrtizanki_ru' ;название файла
Global $phone = '(?<=</div><a href=").*(?=" title)' ;строка поиска ссылок на персональные страницы


		$site[0]= 'http://kyrtizanki.ru'
		$site[1]= ''
		$title[0] = 'МСК'
		$title[1] = 'Питер'
		$param[0] = '/&page='
		$param[1] = ''
Global	$paramLast = '' ; параметр после номера страницы


Global $repeat = True ; страницы повторяются бесконечно. Если true, то ищется номер последней страницы
Global $lastPageExp = "(?<=\.  <span><a href='/&page=).*(?='><)" ; строка посика последней страницы


$Form = GUICreate("Парсер", 200, 50, -1, -1, $WS_POPUP)
;~ GUICtrlCreateLabel('Please Wait...', 10, 20, 180, 17, $SS_CENTER)
$iLabel = _GUICtrlFFLabel_Create($Form, "Please Wait...", 10, 17, 180, 17)
GUISetState(@SW_SHOW)

If $site[1] = '' Then
	$q=0
Else
	$q=1
EndIf

For $z=0 To $q

	Local $arrPhone[1]
	$arrPhone[0] = $title[$z]

	If $repeat Then
		$pData = _INetGetSource($site[$z])
		$lastPage = StringRegExp($pData, $lastPageExp, 1)
	EndIf
	If Not IsArray($lastPage) Then Local $lastPage[1] = [1]

;~ 	MsgBox(64, '', $lastPage[0])
;~ 	Exit

	_GUICtrlFFLabel_SetData($iLabel, 'Сбор анкет...')

	Global $i = 0
	While True
		$i+=1
		$pData = _INetGetSource($site[$z] & $param[$z] & $i & $paramLast)
;~ 		$pData = StringRegExpReplace($pData, '[\h-()+]', '')
		$arrList = StringRegExp($pData, $phone, 3)
		If @error > 0 Then ExitLoop
		_ArrayConcatenate($arrPhone, $arrList)
		If $lastPage[0] = $i Then ExitLoop
;~ 		ExitLoop
	WEnd

	$arrPhone = ArrayUnique($arrPhone)

;~ 	_ArrayDisplay($arrPhone)
;~ 	Exit

	_GUICtrlFFLabel_SetData($iLabel, 'Сбор телефонов...')

	Local $tt = UBound($arrPhone)
	For $i = 1 To UBound($arrPhone) - 1
		$pData = _INetGetSource($arrPhone[$i]) ; $site[$z] & $arrPhone[$i]
		$pData = StringRegExpReplace($pData, '[\h-()+]', '')
		$arrList = StringRegExp($pData, '(?<=потелефону).+(?=иВам)', 1)
;~ 		$arrPhone[$i] = StringMid($pData, StringInStr($pData, "потелефону")+10, StringInStr($pData, "иВам") - StringInStr($pData, "потелефону")-10)
		If Not @error Then
			$arrPhone[$i] = $arrList[0]
		EndIf
		_GUICtrlFFLabel_SetData($iLabel, 'Сбор телефонов: ' & Round(($i-1)*100/$tt, 2) & ' %')
		Sleep(10)
	Next

	$arrPhone = ArrayUnique($arrPhone)

;~ 	_ArrayDisplay($arrPhone)
;~ 	Exit

	Excel_Create()

Next

Func Excel_Create()

	$oError = ObjEvent('AutoIt.Error', '_Error')

	If FileExists(@ScriptDir & $xls_file & $xls_type) Then
		$oExcel = _ExcelBookOpen(@ScriptDir & $xls_file & $xls_type)
	Else
		$oExcel = _ExcelBookNew()
		_ExcelBookSaveAs($oExcel, @ScriptDir & $xls_file)
	EndIf

	$iMaxRowCnt = $oExcel.Rows.Count
	Switch $z
		Case 0
			$RowName = 'A'
		Case 1
			$RowName = 'B'
	EndSwitch
	$iLastRow = $oExcel.Cells($iMaxRowCnt, $RowName ).End($xlUp).Row
	If $iLastRow > 1 Then
		$iLastRow += 1
		_ArrayDelete($arrPhone, 0)
	EndIf
	_ExcelWriteArray($oExcel, $iLastRow, $z+1, $arrPhone, 1, 0)
	_ExcelBookClose($oExcel)

EndFunc

Func ArrayUnique($aArray, $iBase = 0, $oBase = 0)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 1 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique because of only 1 element
    Local $i

        If $iBase And UBound($aArray) - 1 <> $aArray[0] Then Return SetError(4, 0, 0)
        Local $sData = '', $sSep = ChrW(160), $flag
        For $i = $iBase To UBound($aArray) - 1
            If Not IsDeclared($aArray[$i] & '$') And $aArray[$i] <> '' Then ;
                Assign($aArray[$i] & '$', 0, 1)
                $sData &= $aArray[$i] & $sSep
            EndIf
        Next
        If Not $oBase Then $flag = 2
        Local $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep, $flag)

    Return SetError(0, 0, $aUnique)
EndFunc   ;==>ArrayUnique
 

johnmarshall

Осваивающий
Сообщения
202
Репутация
39
mef-t
есть соображения, но код переписывать долго; можно сделать так:
переписать вместо _ArrayConcatenate
$arrPhone объявить переменной, $arrList строить с помощью StringRegExp и очищать,
$arrList[0] = 0.
прибавлять в цикле к $arrPhone
Код:
for $i = 0 to ubound($arrList) - 1
    $arrPhone &= $arrList[$i] & ','
next

только смотри, чтобы в конце строки не отсталась запятая, а то потучишь +1 пустую ячейку 8)
, потом
Код:
$arrPhone = StringSplit($arrPhone, ',')

_ArrayUnique тоже переписать, в ней слишком много Ubound'ов, это тоже время на вычисление размера массива.

в общем все массивы надо заменять циклами, избавляться от них.
я такую проблему в одном проекте решил при вычислении значения в 8000 с 22 сек до 3 сек, разница очевидна.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Мне не нравится кусок кода, который указан в первом сообщении.
Так как подвисает именно на нем.
Потому _ArrayConcatenate и _ArrayUnique мне не мешают в том виде, в котором они есть. Да, они могут медленно работать. Но конкретно их работу я могу потерпеть.


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

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

Про _ArrayUnique, данный алгоритм нашел на просторах этого форума, работает на 1-2 порядка быстрее стандартной функции. Да и UBound там не так уж и часто используется.


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

Меня очень тревожит _INetGetSource.
На его выполнение иногда уходит до 0,5 секунды.
Возможно ли получить исходник как то быстрее?
Понимаю, что уже отвечали, но все же.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Код:
#include <INet.au3>

	$t = TimerInit()
	$pData = InetRead('http://ya.ru', 16)
	$pData = BinaryToString($pData)
	ConsoleWrite("InetRead: " & TimerDiff($t) & @CRLF)

	Sleep(1000)

	$t = TimerInit()
	$pData = _INetGetSource('http://ya.ru')
	ConsoleWrite("_INetGetSource: " & TimerDiff($t) & @CRLF)


Показатели примерно одинаковы. Ранее тут писали (затем удалили), то _INetGetSource использует InetRead


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

_INetGetSource() - это
Код:
Func _INetGetSource($s_URL, $bString = True)
	Local $sString = InetRead($s_URL, 1)
	Local $nError = @error, $nExtended = @extended
	If $bString Then $sString = BinaryToString($sString)
	Return SetError($nError, $nExtended, $sString)
EndFunc   ;==>_INetGetSource

Так что по времени одно и то же
 
Верх