"накладные расходы" при работе программы
Добавлено: 02 авг 2018, 12:46
Работа над очередным проектом и дискуссии с IvanLis сподвигли на небольшое тестирование.
Но сначала немного воды.
По моему субъективному мнению программисты делятся на 10 категории.
1 программируют на
10 программируют на С.
Те, кто начинал на "тексте" порой не врубаются в поток данных и злоупотребляют локальными/глобальными переменными. В универе нам сказали дивную фразу "я буду программировтаь на паскале, даже если это будет С". Тут примерно то же самое.
С другой стороны, мне "повезло" начинать с , но вот с классами я до сих пор толком не подружился. Уже года три их использую, но в качестве удобной обёртки на кластеры, чтобы не загромождать ПУ. Ведь чем дальше и больше проекты, тем больше переменных приходится хранить. И тем больше разрастаются typedef-ы. Но порой возникает вопрос «а надо ли»? Сколько «стоит» мне каждый класс, а точнее, его вызов. И вот, наконец, руки дошли до проверки.
Итак, к делу. Что проверяем: сколько времени тратит система на вызов конкретной функции. Для чистоты эксперимента данные должны меняться, иначе оптимизатор наоптимизирует и ничего вызывать не будет. С другой стороны, сама функция должна работать минимальное время, иначе сложно будет отделить мух от котлет. Поэтому выбирают обычный инкремент.
Дальше, данные обычно запрятаны в кластеры, размер которого «а фиг его знает», потомучто могут быть и массивы и строки и т.п. Поэтому «рабочую» переменную прячу между двух элементов «неопределённой» длины.
Список вариантов работы
1. провода. Самое первое и простое. Сколько «стоит» гонять данные по регистрам.
2. кластер. Появляются при усложнении программ. во что обойдётся разобрать/собрать для изменение одного элемента.
3. in place. Аналогично. Предполагается, что этот метод будет быстрее предыдущего.
4. просто функция. Появляется, когда БД уже не влезает на экран.
5. просто функция с in place. Аналогично с 2-3, но с обёрткой в виде простой функции.
6. статический класс. То же самое, что 4, но в виде класса.
7. статический класс с in place. То же самое, что 5, но в виде того же класса.
8. динамический класс. = 6, но с возможностью override
9. динамический класс-наследник с вызовом родителя. Тут непонятно, что тестирую , но т..к прямого доступа к данным родителя нет, проверю такой способ.
10. динамический класс-наследник. В классе появляется новая переменная. Её и увеличиваю.
11. динамический класс-наследник с доступом к данным. У родителя появляются функции get/set. С их помощью и увеличиваю исходную переменную.
Весь код рисовать тут не буду, в принципе функции примерно так выглядят. А это весь тестер. Для пущей достоверности (и чтобы не выключать везде отладку руками) делаю ехе.
Теперь результаты. Ожидаемо провода оказались самыми быстрыми.
Разбор кластера неизбежно тянет расходы. Тоже в принципе ожидаемо. Неожиданно in place тратит на работу с кластером больше времени. вызов функции «стоит» 10^-5c. В 20-30 раз дольше, чем просто кластеры, но параноиком быть не надо, это не повод создавать спагетти-диаграммы. in place и тут подводит… Проверял на массивах длиной 500 и 1 000 000 элементов, цифры схождие, так что объёмы передаваемых данных не влияют на скорость вызова функции.
Дальше самое интересное. Стоит функции стать классом, как она замедляется процентов на 20-30. А in place наконец-то обгоняет unbandle.
Но стоит функции стать динамической, как скорость вызова падает в три раза.
Особо интересно выглядят 9 и 11. «поиск» родителя и его вызов вполне объясняют такой рост времени вызова. Но что странно: вызов внутри функции ещё двух функций родительского класса не замедляет работу, даже наоборот. 8 (изменение переменной в «надстройке») работает чуть дольше, чем 11 (изменение переменной родителя). Впрочем, объяснение скорости нашёл, data member access работают через inline.
выводы: не всё так страшно. Если функция выполняет существенную работу, то затраты на её вызов позволительная роскошь в угоду удобству программирования/отладки.
С другой стороны, если важна скорость работы, то совсем мельчить не стоит, много ресурсов будет тратиться на вызовы, а не полезную работу, что и так было известно, просто в очереднйо раз подтверждаем этот факт.
Ну а классы… пора их осваивать серъёзно :)
Ну и самое непонятное в этом тесте: почему массивы уползают со своего места после билдера.… Тестировал на LV16. Для желающих погонять прикладываю и 12
комменты и предложения ожидаются.
Но сначала немного воды.
По моему субъективному мнению программисты делятся на 10 категории.
1 программируют на
10 программируют на С.
Те, кто начинал на "тексте" порой не врубаются в поток данных и злоупотребляют локальными/глобальными переменными. В универе нам сказали дивную фразу "я буду программировтаь на паскале, даже если это будет С". Тут примерно то же самое.
С другой стороны, мне "повезло" начинать с , но вот с классами я до сих пор толком не подружился. Уже года три их использую, но в качестве удобной обёртки на кластеры, чтобы не загромождать ПУ. Ведь чем дальше и больше проекты, тем больше переменных приходится хранить. И тем больше разрастаются typedef-ы. Но порой возникает вопрос «а надо ли»? Сколько «стоит» мне каждый класс, а точнее, его вызов. И вот, наконец, руки дошли до проверки.
Итак, к делу. Что проверяем: сколько времени тратит система на вызов конкретной функции. Для чистоты эксперимента данные должны меняться, иначе оптимизатор наоптимизирует и ничего вызывать не будет. С другой стороны, сама функция должна работать минимальное время, иначе сложно будет отделить мух от котлет. Поэтому выбирают обычный инкремент.
Дальше, данные обычно запрятаны в кластеры, размер которого «а фиг его знает», потомучто могут быть и массивы и строки и т.п. Поэтому «рабочую» переменную прячу между двух элементов «неопределённой» длины.
Список вариантов работы
1. провода. Самое первое и простое. Сколько «стоит» гонять данные по регистрам.
2. кластер. Появляются при усложнении программ. во что обойдётся разобрать/собрать для изменение одного элемента.
3. in place. Аналогично. Предполагается, что этот метод будет быстрее предыдущего.
4. просто функция. Появляется, когда БД уже не влезает на экран.
5. просто функция с in place. Аналогично с 2-3, но с обёрткой в виде простой функции.
6. статический класс. То же самое, что 4, но в виде класса.
7. статический класс с in place. То же самое, что 5, но в виде того же класса.
8. динамический класс. = 6, но с возможностью override
9. динамический класс-наследник с вызовом родителя. Тут непонятно, что тестирую , но т..к прямого доступа к данным родителя нет, проверю такой способ.
10. динамический класс-наследник. В классе появляется новая переменная. Её и увеличиваю.
11. динамический класс-наследник с доступом к данным. У родителя появляются функции get/set. С их помощью и увеличиваю исходную переменную.
Весь код рисовать тут не буду, в принципе функции примерно так выглядят. А это весь тестер. Для пущей достоверности (и чтобы не выключать везде отладку руками) делаю ехе.
Теперь результаты. Ожидаемо провода оказались самыми быстрыми.
Разбор кластера неизбежно тянет расходы. Тоже в принципе ожидаемо. Неожиданно in place тратит на работу с кластером больше времени. вызов функции «стоит» 10^-5c. В 20-30 раз дольше, чем просто кластеры, но параноиком быть не надо, это не повод создавать спагетти-диаграммы. in place и тут подводит… Проверял на массивах длиной 500 и 1 000 000 элементов, цифры схождие, так что объёмы передаваемых данных не влияют на скорость вызова функции.
Дальше самое интересное. Стоит функции стать классом, как она замедляется процентов на 20-30. А in place наконец-то обгоняет unbandle.
Но стоит функции стать динамической, как скорость вызова падает в три раза.
Особо интересно выглядят 9 и 11. «поиск» родителя и его вызов вполне объясняют такой рост времени вызова. Но что странно: вызов внутри функции ещё двух функций родительского класса не замедляет работу, даже наоборот. 8 (изменение переменной в «надстройке») работает чуть дольше, чем 11 (изменение переменной родителя). Впрочем, объяснение скорости нашёл, data member access работают через inline.
выводы: не всё так страшно. Если функция выполняет существенную работу, то затраты на её вызов позволительная роскошь в угоду удобству программирования/отладки.
С другой стороны, если важна скорость работы, то совсем мельчить не стоит, много ресурсов будет тратиться на вызовы, а не полезную работу, что и так было известно, просто в очереднйо раз подтверждаем этот факт.
Ну а классы… пора их осваивать серъёзно :)
Ну и самое непонятное в этом тесте: почему массивы уползают со своего места после билдера.… Тестировал на LV16. Для желающих погонять прикладываю и 12
комменты и предложения ожидаются.