-
Автор темы
- #1
Часто встречаю(да что там часто? Постоянно) использование, так называемого,"JunkCode".
По прямому переводу("мусорный код") несложно догадаться что делает данное "явление".
Основное назначение это "видоизменение" основного выполняемого кода.
давайте разберемся как это выглядит на деле и стоит ли использовать данный метод?
для теста и наглядного примера пишем небольшую консольную программу, которая циклом будет выводить сообщение в консоль. При этом измерим скорость выполнения нужного участка кода при помощи двух методов: "точные часы"(std::chrono) и "счетчика высокого разрешения"(QueryPerformance*).
Так это выглядит с точки зрения инструкций:
теперь, используя самый примитивный JunkCode, немного разнообразим программу:
смотрим в отладчик что изменилось:
Как видим в теле основного выполнения появились "левые" инструкции, что повлекло за собой видоизменение сигнатуры участка кода.
теперь будем вообще "брутальными" и добавим "мусора" в цикл:
смотрим:
вообще красота! Детектил бы какой-нибудь античит наш участок памяти в котором выполняется цикл, то после всех манипуляций он бы "ОфЕгел".
А теперь об огромном минусе, который перечеркивает на нет все выше предоставленное: режется скорость))
сравниваем:
1) чистая программа - 32 мс
2) "Мусор вокруг" цикла - 37 мс
2) "Мусор вокруг и внутри" цикла - 41 мс
Цифры будут варьироваться, но увеличении скорости обработки будет расти с количеством добавленного "мусора".
Вывод:
В рамках данного примера цифры не значительные, но если брать во внимание применении данного метода в читах, функционал которого требует быстрых и точных вычислений, а количество итераций некоторых может достигать сотен тысяч - потери огромные. Используйте с умом и пониманием)))
По прямому переводу("мусорный код") несложно догадаться что делает данное "явление".
Основное назначение это "видоизменение" основного выполняемого кода.
давайте разберемся как это выглядит на деле и стоит ли использовать данный метод?
для теста и наглядного примера пишем небольшую консольную программу, которая циклом будет выводить сообщение в консоль. При этом измерим скорость выполнения нужного участка кода при помощи двух методов: "точные часы"(std::chrono) и "счетчика высокого разрешения"(QueryPerformance*).
Код:
#include <Windows.h>
#include <conio.h>
#include <chrono>
#include <iostream>
class IQueryTimer
{
public:
IQueryTimer()
{
QueryPerformanceFrequency(&mqFreq);
}
~IQueryTimer() {}
void Start()
{
QueryPerformanceCounter(&mqStart);
}
void End()
{
QueryPerformanceCounter(&mqEnd);
}
double GetTimeInSeconds()
{
return (mqEnd.QuadPart - mqStart.QuadPart) / static_cast<double>(mqFreq.QuadPart);
}
double GetTimeInMilliseconds()
{
return (mqEnd.QuadPart - mqStart.QuadPart) / static_cast<double>(mqFreq.QuadPart)*1000.0;
}
private:
LARGE_INTEGER mqStart;
LARGE_INTEGER mqEnd;
LARGE_INTEGER mqFreq;
};
int main(int argc, char** argv)
{
SetConsoleTitleA("Console");
IQueryTimer Time; //задаем объект для теймера QueryPerformance
Time.Start(); //стартуем получая первое значение счетчика
auto begin = std::chrono::steady_clock::now(); //получаем текущее значение с помощью точных часов
//выполняем цикл
for (size_t i = 0; i < 1000; i++)
{
printf_s("\r%i iterations", i);
}
auto end = std::chrono::steady_clock::now();//получаем новое значение с помощью точных часов
Time.End();//получаем новое значение счетчика
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin); //высчитываем потраценое время в мс
//выводим результаты
std::cout << std::endl;
std::cout << "Chrono Time: " << elapsed_ms.count() << " ms" << std::endl;
std::cout << "Query Time: " << Time.GetTimeInMilliseconds() << " ms" << std::endl;
_getch();
return EXIT_SUCCESS;
}
теперь, используя самый примитивный JunkCode, немного разнообразим программу:
Код:
#define JUNK_CODE \
__asm push eax \
__asm xor eax, eax \
__asm setpo al \
__asm push edx \
__asm xor edx, eax \
__asm sal edx, 2 \
__asm xchg eax, edx \
__asm pop edx \
__asm or eax, ecx \
__asm pop eax
Код:
JUNK_CODE
for (size_t i = 0; i < 1000; i++)
{
printf_s("\r%i iterations", i);
}
JUNK_CODE
Как видим в теле основного выполнения появились "левые" инструкции, что повлекло за собой видоизменение сигнатуры участка кода.
теперь будем вообще "брутальными" и добавим "мусора" в цикл:
Код:
JUNK_CODE
for (size_t i = 0; i < 1000; i++)
{
JUNK_CODE
printf_s("\r%i iterations", i);
}
JUNK_CODE
вообще красота! Детектил бы какой-нибудь античит наш участок памяти в котором выполняется цикл, то после всех манипуляций он бы "ОфЕгел".
А теперь об огромном минусе, который перечеркивает на нет все выше предоставленное: режется скорость))
сравниваем:
1) чистая программа - 32 мс
2) "Мусор вокруг" цикла - 37 мс
2) "Мусор вокруг и внутри" цикла - 41 мс
Цифры будут варьироваться, но увеличении скорости обработки будет расти с количеством добавленного "мусора".
Вывод:
В рамках данного примера цифры не значительные, но если брать во внимание применении данного метода в читах, функционал которого требует быстрых и точных вычислений, а количество итераций некоторых может достигать сотен тысяч - потери огромные. Используйте с умом и пониманием)))