1

Тема: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Постараюсь быстренько описать ситуацию как есть.
Решил значит я "поиграть" в MMORPG... Написал скрипт - на входе (один раз) создаёт 3 массива, элементов по 300. Написал две функции одна бинарным алгоритмом проходит по массивам (максимум 9 вызовов на массив!), вторая внутри первой берёт яркость пикселя (соответственно тоже максимум 9 раз на массив). Математики в скрипте минимум. В диспетчере задач смотрю - скрипт нагрузки даёт вообще мизер.... А ЧЁ БЛИН МЕДЛЕННО ТАК!? Прохода 4 в секунду делает всего (таймер 50мс. стоит)! Я когда в WoW играл, так там целые "книги" писал (LUA аддоны), с громадными массивами и кучей расчётов, так всё работало аж комп нагружало, приходилось оптимизировать, но работало то быстро, как и рассчитывалось.
Собственно в чём проблема? AHK медленный сам по себе или я не знаю как его ускорить? Если первое, подскажет кто нибудь альтернативу? Если второе ... странно что нагрузки то на систему никакой, а "ползёт как черепаха" ... такое чувство, что какое то ограничение есть, так может его можно снять?

2

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

88man, веткой ошиблись, для игр есть отдельная. По сути — скорее всего алгоритм кривой. Для ускорения выполнения скрипта есть команда SetBatchLines, -1.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

3

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

teadrinker, вот "шапка" скрипта

#SingleInstance Force
#Persistent
#MaxThreads 1000
#MaxThreadsPerHotkey 250
#HotkeyInterval 1000
#MaxHotkeysPerInterval 1000
#WinActivateForce
#InstallKeybdHook
#InstallMouseHook
#KeyHistory 0
#NoEnv
ListLines Off
Process Priority, , A
SetDefaultMouseSpeed 0
SetBatchLines -1
SetKeyDelay -1, -1
SetMouseDelay -1
SetWinDelay -1
SetControlDelay -1
DetectHiddenText On
DetectHiddenWindows On
SetTitleMatchMode 3
SetTitleMatchMode Fast

Игра это вторично, алгоритм проверил с помощью Count := Count+1 при вызове, всё исправно, 8-9 вызовов на массив (именно вызовов а не проходов). Тем не менее если оставить один массив то как раз раза в 3 быстрее работает, будто какое то принудительное ожидание есть...

4

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Не совсем ясно, какого ответа вы ждёте. Ваша «шапка скрипта» говорит только о том, что вы не очень опытный кодер.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

5

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

teadrinker, так и есть, я любитель. В шапке, что нагуглил по запросу AHK быстродействие, то и написал, подскажете какие там ошибки? А ответа я жду как и написал одного из двух, либо да - AHK, 3 таблицы бинарным поиском с вызовом PixelGetColor и 4 прохода в секунду это норма, тогда чем можно решить эту задачу быстрее? Либо нет - AHK должен работать значительно быстрее, надо разбираться где накосячил.

6

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Попробуйте искать пиксели так:
http://forum.script-coding.com/viewtopic.php?id=12938

7

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

88man пишет:

подскажете какие там ошибки?

Ошибкой можно считать любое ненужное действие, использующее дополнительные ресурсы. Почитайте в справке о каждой команде и решите, нужна вам она, или нет.

88man пишет:

бинарным поиском с вызовом PixelGetColor

Ну вот хоть какая-то конкретика. PixelGetColor — довольно медленная операция, попробуйте то, что посоветовали выше.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

8 (изменено: 88man, 2018-10-22 19:24:47)

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Malcev, teadrinker, Навряд ли  дело в PixelGetColor, поскольку я написал ещё скрипт, который делает то же самое но "тупо в лоб", при помощи PixelSearch, и скорость увеличилась в разы (верю про 50мс. в SetTimer). А в игры я "играю" действительно в кавычках, на деле только и занимаюсь скриптингом и моддингом... Так что хотя сносный результат и получен, я всё равно хочу разобраться почему линейный поиск PixelSearch оказался быстрее бинарного (учитывая, что просматривается до 300 пикселей).
Смущает, то что работает всё правильно, но медленно... Пожалуй закомментирую код и выложу, может кто образованный подскажет неучу-любителю?

9

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Чтобы понять в чем причина тормозов засекайте время с помощью a_tickcount.

10

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

88man, чтобы оптимизировать что-то, надо сначала понять где узкое место.
PixelSearch а тем более PixelGetColor расчитаны на единичные редкие вызовы.
Полагать что PixelSearch состоит из отдельных вызовов PixelGetColor наивно.

11

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

В общем, вот мои изыскания с A_TickCount:
Вся математика, логика, присвоения - 0мс.
Вызов PixelGetColor выдаёт два варианта - 0мс. и 15мс.
Вызов PixelSearch на 300 пикселей выдаёт два варианта - 0мс. и 15мс.
Вызов пользовательской функции, в составе которой PixelGetColor выдаёт три варианта - 0мс. 15мс. и 30мс.
По всей видимости всё как я и предполагал, есть какое то принудительное ожидание на функцию и оно составляет 15мс. однако происходит не всегда.
Что это за задержка?

12

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Приведите код.

13

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Malcev, ситуация с 15мс. даже на чистом скрипте.
Код сейчас выложу, докомментирую...

14 (изменено: 88man, 2018-10-23 15:35:26)

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

#SingleInstance Force
#Persistent
#WinActivateForce
#InstallKeybdHook
#InstallMouseHook
#KeyHistory 0
#NoEnv
ListLines Off
SetBatchLines -1
SetDefaultMouseSpeed 0
SetKeyDelay 0, 0
SetMouseDelay 0
SetWinDelay 0
SetControlDelay 0
SetTitleMatchMode 3
SetTitleMatchMode Fast
CoordMode Pixel, Screen

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


; Создаём функцию которая будет создавать массивы.
; Параметры:
; CreateScaleDirectionInput - вводится пользователем - направление поиска "Horizontal" или "Vertical".
; CreateScaleColorTypeInput - вводится пользователем - искомый цвет "Red" или "Green" или "Blue" или "Brightness".
; CreateScaleColorValueInput - вводится пользователем - искомая яркость (целое число)
; CreateScalePositionInput, CreateScaleBegin, CreateScaleEnd - вводятся пользователем - координаты шкалы  (Y,X1,X2 или X,Y1,Y2).
; CreateScaleArray - выводится функцией - имя создаваемого массива.
; CreateScaleDirectionOutput - выводится функцией - направление как переменная, для последующего поиска.
; CreateScaleColorTypeOutput - выводится функцией - искомый цвет как переменная, для последующего поиска.
; CreateScaleColorValueOutput - выводится функцией - искомая яркость как переменная, для последующего поиска.
; CreateScalePositionOutput - выводится функцией - начальная координата как переменная, для последующего поиска пикселя (Y или X).
; CreateScalePercentValue - выводится функцией - процентное значение одного пикселя, в искомой шкале.
CreateScale(CreateScaleDirectionInput, CreateScaleColorTypeInput, CreateScaleColorValueInput, CreateScalePositionInput, CreateScaleBegin, CreateScaleEnd, ByRef CreateScaleArray, ByRef CreateScaleDirectionOutput, ByRef CreateScaleColorTypeOutput, ByRef CreateScaleColorValueOutput, ByRef CreateScalePositionOutput, ByRef CreateScalePercentValue)
{
	CreateScaleDirectionOutput := CreateScaleDirectionInput
	CreateScaleColorTypeOutput := CreateScaleColorTypeInput
	CreateScaleColorValueOutput := CreateScaleColorValueInput
	CreateScalePositionOutput := CreateScalePositionInput
	CreateScaleArray := []
	CreateScaleArray.Push(CreateScaleBegin)
	CreateScaleArrayCount := Abs(CreateScaleEnd-CreateScaleBegin)+1
	CreateScalePercentValue := 100/CreateScaleArrayCount
	While CreateScaleArray.MaxIndex()<CreateScaleArrayCount
	{
		If CreateScaleBegin<CreateScaleEnd
		{
			CreateScaleBegin := CreateScaleBegin+1
		}
		Else
		{
			CreateScaleBegin := CreateScaleBegin-1
		}
		CreateScaleArray.Push(CreateScaleBegin)
	}
}


; Создаём функцию которая будет оценивать пиксели по яркости, внутри другой функции.
; Параметры:
; PixelX, PixelY - вводятся пользователем - но будут принимать переменные от других функций (координаты).
; PixelGetColorValueRed, PixelGetColorValueGreen, PixelGetColorValueBlue, PixelGetColorValueBrightness - выводятся функцией - яркость по цветам и общая.
PixelGetColorValue(PixelX, PixelY, ByRef PixelGetColorValueRed, ByRef PixelGetColorValueGreen, ByRef PixelGetColorValueBlue, ByRef PixelGetColorValueBrightness)
{
	PixelGetColor PixelGetColorHex, PixelX, PixelY, RGB
	PixelGetColorValueRed := (PixelGetColorHex & 0xFF0000) >> 16
	PixelGetColorValueGreen := (PixelGetColorHex & 0xFF00) >> 8
	PixelGetColorValueBlue := PixelGetColorHex & 0xFF
	PixelGetColorValueBrightness := Max(PixelGetColorValueRed, PixelGetColorValueGreen, PixelGetColorValueBlue)
}


; Создаём функцию, которая будет выбирать пиксели на шкале для проверки яркости, внутри функции будет вызываться функция оценки яркости.
; За основу я взял бинарный поиск, но слегка видоизменил, ведь нужно найти не конкретное значение, а точку перехода от True к False, но основной принцип ведь тот же - левее True не может быть False а правее False не может быть True.
; Все параметры в функции - будут принимать переменные, выводимые из массива.
; Функция возвращает последний пиксель искомой яркости на шкале, в процентном выражении.
SearchScale(SearchScaleArray, SearchScaleDirection, SearchScaleColorType, SearchScaleColorValue, SearchScalePosition, SearchScalePercentValue)
{
	SearchScaleBegin := SearchScaleArray.MinIndex()
	SearchScaleEnd := SearchScaleArray.MaxIndex()
	SearchScalePercent := 0
	While SearchScaleBegin<=SearchScaleEnd
	{
		SearchScaleMiddle := Floor((SearchScaleBegin+SearchScaleEnd)/2)
		If (SearchScaleDirection = "Vertical")
		{
			PixelGetColorValue(SearchScalePosition, SearchScaleArray[SearchScaleMiddle], SearchScaleRed, SearchScaleGreen, SearchScaleBlue, SearchScaleBrightness)
		}
		Else If (SearchScaleDirection = "Horizontal")
		{
			PixelGetColorValue(SearchScaleArray[SearchScaleMiddle], SearchScalePosition, SearchScaleRed, SearchScaleGreen, SearchScaleBlue, SearchScaleBrightness)
		}
		If (SearchScaleColorType = "Red")
		{
			If SearchScaleRed>%SearchScaleColorValue%
			{
				SearchScalePercent := SearchScaleMiddle
				SearchScaleBegin := SearchScaleMiddle+1
			}
			Else
			{
				SearchScaleEnd := SearchScaleMiddle-1
			}
		}
		Else If (SearchScaleColorType = "Green")
		{
			If SearchScaleGreen>%SearchScaleColorValue%
			{
				SearchScalePercent := SearchScaleMiddle
				SearchScaleBegin := SearchScaleMiddle+1
			}
			Else
			{
				SearchScaleEnd := SearchScaleMiddle-1
			}
		}
		Else If (SearchScaleColorType = "Blue")
		{
			If SearchScaleBlue>%SearchScaleColorValue%
			{
				SearchScalePercent := SearchScaleMiddle
				SearchScaleBegin := SearchScaleMiddle+1
			}
			Else
			{
				SearchScaleEnd := SearchScaleMiddle-1
			}
		}
		Else If (SearchScaleColorType = "Brightness")
		{
			If SearchScaleBrightness>%SearchScaleColorValue%
			{
				SearchScalePercent := SearchScaleMiddle
				SearchScaleBegin := SearchScaleMiddle+1
			}
			Else
			{
				SearchScaleEnd := SearchScaleMiddle-1
			}
		}
	}
	Return Round(SearchScalePercent*SearchScalePercentValue)
}


; Единожды создаём массивы, вводим параметры ручками.
; Имена переменных параметров указываем соответственно как в массиве (который выводит данные в эти переменные), так и в функции поиска (которая принимает данные из этих переменных).
CreateScale("Horizontal", "Brightness", 165, 782, 802, 1090, HealthArray, HealthDirection, HealthColorType , HealthColorValue , HealthPosition, HealthPercentValue)
CreateScale("Horizontal", "Brightness", 165, 786, 1181, 1393, PetArray, PetDirection, PetColorType , PetColorValue , PetPosition, PetPercentValue)
CreateScale("Horizontal", "Brightness", 165, 826, 802, 1090, StaminaArray, StaminaDirection, StaminaColorType , StaminaColorValue , StaminaPosition, StaminaPercentValue)


SetTimer Spam, 50
Spam:
{
	Health := SearchScale(HealthArray, HealthDirection, HealthColorType , HealthColorValue , HealthPosition, HealthPercentValue)
	Pet := SearchScale(PetArray, PetDirection, PetColorType , PetColorValue , PetPosition, PetPercentValue)
	Stamina := SearchScale(StaminaArray, StaminaDirection, StaminaColorType , StaminaColorValue , StaminaPosition, StaminaPercentValue)
	ToolTip Health = %Health% Pet = %Pet% Stamina = %Stamina%
}
Return

15

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Я в подробности вашего алгоритма не вдавался, но зачем вы в секцию автовыполнения понапихали кучу всего?
А тормозит ваш скрипт наверняка из-за SetBatchLines 0.

16

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Malcev, SetBatchLines 0 банальная ошибка при копипастах, спасибо, что обратили внимание, а то я думаю, от чего ещё медленнее стало...
Дело не в коде, я всё перепроверил. И по поводу A_TickCount:

	DllCall("QueryPerformanceFrequency", "Int64*", TimerFrequency)
	DllCall("QueryPerformanceCounter", "Int64*", TimerBefore)
	; Вот тут то что хотим отследить.
	DllCall("QueryPerformanceCounter", "Int64*", TimerAfter)
	Timer := (TimerAfter-TimerBefore)/TimerFrequency*1000
	ToolTip %Timer%

Вот так даёт точный результат по времени, теперь понятно почему A_TickCount мне выдовал, то 0 то 15 мс. - он просто округлял значения, хотя в справке написано, что округляет до 10, не понятно от куда 15...
В общем, вот этим уточнённым методом, перепроверил - любая функция занимает от почти 0 до почти 20 мс. Почему такой большой разброс? Это нормально? Может дело в системе? А то этот Win10 я сразу невзлюбил...

17 (изменено: Malcev, 2018-10-23 11:50:50)

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Вообще для удобства чтения сначала пишется код, а потом уже функции или классы.
У меня 1000 итераций вашего кода без sleep выполняется за 650ms.

a := a_tickcount
loop 1000
{
	Health := SearchScale(HealthArray, HealthDirection, HealthColorType , HealthColorValue , HealthPosition, HealthPercentValue)
	Pet := SearchScale(PetArray, PetDirection, PetColorType , PetColorValue , PetPosition, PetPercentValue)
	Stamina := SearchScale(StaminaArray, StaminaDirection, StaminaColorType , StaminaColorValue , StaminaPosition, StaminaPercentValue)
	ToolTip Health = %Health% Pet = %Pet% Stamina = %Stamina%
}
msgbox % a_tickcount - a

Возможно, на Win10 с включенным desktop composition будет медленнее.
Насчёт функций - они срабатывают действительно медленнее, но не настолько, насколько вы описали.

SetBatchLines, -1
n:= 0, k:= 6
a:=a_tickcount
loop % 1920*1080
{
   if k = 6
      n++   
}
msgbox % n "`n" a_tickcount - a

SetBatchLines, -1
n:= 0, k:= 6
a:=a_tickcount
loop % 1920*1080
{
   if test(k)
      n++   
}
msgbox % n "`n" a_tickcount - a

test(var)
{
   if var = 6
      return true
}

18

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

88man, не разброс, а ошибка округления. Считать надо большое кол-во итераций - 100, 1000.
А хп гораздо удобнее читать из памяти мгры.
Malcev, у вас тоже тоже dwm включен, если только вы не на классической теме оформления. Буфер находится в выделенной видеопамяти, доступ к которой связан с задержками.
Начиная с 8 винды отключить его уже нельзя вроде.

19 (изменено: 88man, 2018-10-23 15:35:00)

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Malcev, 1000 за 433281ms. ... сказать что разница колоссальная - ничего не сказать. Ещё есть момент, я не указал CoordMode Pixel, Screen, так что ваш результат мог зависеть от активного окна, которое могло быть меньше просматриваемых координат, мой косяк - делал для себя, не подумал о людях, не могли бы вы повторить эксперимент с указанным CoordMode (я добавил строку в код на форуме)? И если результат подтвердится, в чём может быть причина такой невероятной задержки?
stealzy, Читать из памяти игры? Таким искусством не владею. Я так понимаю в двух словах не расскажешь? Можно полезную ссылку и/или пример кода?

20 (изменено: Malcev, 2018-10-23 16:33:45)

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

stealzy, я как раз таки сижу на классической.
88man, CoordMode тут не при чём.
Причина задержки, как уже выше мы вам писали, в PixelGetColor вкупе с включенным desktop composition.
Если вам надо проверить n-ое количество точек на соответствие какого-либо цвета, то быстрее всего это сделать методом по ссылке из 6 поста.
Вот тема про чтение из памяти:
http://forum.script-coding.com/viewtopic.php?id=13915

21

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Установить Win7 и классическую тему - это решение?

22

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Почему вы так упорно хотите использовать PixelGetColor?
Чем вас не устраивает метод через LockBits?

23

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

Malcev, ели честно я не разбирался в той теме. Ведь проблема не в PixelGetColor, задержка примерно в 15мс. происходит при вызове любой функции, например SendInput так же даёт эту задержку.

24 (изменено: Malcev, 2018-10-23 18:49:38)

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

При чем тут SendInput и вызов функции?
SendInput из другой оперы.
Рекомендую почитать внимательней справку.
Вот вызов функции выполняется более 2 миллионов раз.
У меня занимает это 1 секунду.

SetBatchLines, -1
n:= 0, k:= 6
a:=a_tickcount
loop % 1920*1080
{
   if test(k)
      n++   
}
msgbox % n "`n" a_tickcount - a

test(var)
{
   if var = 6
      return true
}

25

Re: AHK: Быстродействие или альтернатива? Нужен совет ветеранов.

При чем тут SendInput и вызов функции?

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

Вот вызов функции выполняется более 2 миллионов раз.
У меня занимает это 1 секунду

Так же занимает чуть меньше секунды. Но ведь в коде никаких команд не задействовано, используется только логика (If), математика и присвоение (n++). Подобные манипуляции задержек и не вызывали.
В общем, всем спасибо, помогли уточнить проблему. Тему можно закрывать, мне нужно потестить всё более тщательно и обозначить проблему более конкретно.