Просмотр полной версии : многократный вызов функции. оптимизация
fucker"ok
07.05.2009, 04:33
Вопрос чисто ради интереса.
for (int i = 0; i < someshit.count(); i++) {}
int arraySize = someshit.count();
for (int i = 0; i < arraySize; i++) {}
В первом случае метод count() вызывается Х количество раз. Во втором - один раз.
Интересует мнение людей, которые знают как это работает на более низком уровне.
Много ли различных вызовов\обращений к памяти экономит второй пример и есть ли вообще смысл оптимизировать код данным образом?
В первом случае метод count() вызывается Х количество раз. Во втором - один раз. в вопросе и ответ, если не хочешь count() вызывать тыщу раз то стоит, проголосовал за второй вариант, хотя пишу в Делфи а там разницы нет
второй вариант значительно будет виден когда someshit.count() будет около 1кк или больше...
смотря конечно что такое someshit если это класс а count() функция, то надо смотреть как она получает результат... тут может быть несколько споссобов
1)ф-ия в цикле считает результат и выдает...
2)в классе предусмотрена переменная для хранения этого результата, которая при каждом изменеии данных пересчитывается и сохраняется в памяти тогда count() просто считывает эту переменную и выдает её результат.
сам понимаеш если первый вариант то это очень много лишних операций для тебя... если же в классе все как во втором случае то ты всеравно на каждый шаг цикла получаеш 1-2 лишних операции...
p.s. если ты уверен что значение count() у тебя всегда будет не больше 1000 то оба споссоба для человека будут одинаковы (человек не сможет заметить разницу выполнения программ в 100-200 тактов (ну может и побольше))
Однозначно второй вариант
второй вариант, только пишу так:
for (int i = 0, arraySize = someshit.count(); i < arraySize; i++) {}
Конечно второй вариант быстрее.
З.Ы.
А если ещё и цикл разложить в последовательные вызовы и таким образом избавиться от условия на каждой итерации, так вообще :) Но это не твой случай, т.к. ты не знаешь count.
Единственное - умные компиляторы всё это дело оптимизируют.
З.З.Ы.
А по поводу названия опроса "Как вы пишете", я пишу как во 2м варианте, но к примеру Фаулер, в книге по рефакторингу рекомендует писать как в первом варианте, для того чтобы избавится от лишних переменных и сделать код более наглядным. Ну это правда к этапу оптимизации не относится, а делается раньше :)
избавится от лишних переменных и сделать код более наглядным
думаю не стоит к этому прислушиваться.... ибо код надо форматировать только потому что мы не машины и если писать код в одну строчку то его сразу не понять...(очень не сразу..) но самое главное для программиста должно быть оптимизация и отсутствие багов в своем детище (не если не отсутствию, то хотябы свести все баги к минимуму...)
а наглядность это не для кода... (но если кодер ставит на первый план наглядность то это его дело)
KIR@PRO, код пишут для людей а не для компов. (Ну без фанатизма, я к тому что код должен быть понятен) В больших проектах за непонятный код надо по пальцам стучать. Представь как его потом апдейтить и багфиксить. Но по мне временные переменные - наоборот могут внести ясность, за счёт нормального имени переменной.
З.Ы.
А оптимизация это уже последний этап, когда код работает и ты по нему бегаешь профайлером, выясняя что именно стоит оптимизировать.
дак я и говорю что элементарную табуляцию и прочее надо соблюдать, названия переменных делать понятными... это уде слишком, этож прямое принебрежение производительностью ради маленькой наглядности...
p.s. может мы просто не поняли друг друга))) я же говорил именно о том что надо избавляться от временных переменных чтоб сделать код более наглядным...
p.p.s. но это мнение каждого из нас)
по-моему такого вообще не должно быть, должна быть переменная Size, которая обновляется сама собой.
проголосовал за первое, случайно.
результат под запуском профайлера:
http://img21.imageshack.us/img21/3968/05072009222211.jpg
результат профайлера:
http://img18.imageshack.us/img18/7264/05072009222305.jpg
используемый код:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
private const int MaxLength = 100000;
private static readonly List<int> list = new List<int>();
static void GetList()
{
int i = 0;
while (i < MaxLength)
{
list.Add(i);
i++;
}
}
static DateTime Method1()
{
for (int i = 0; i < list.Count; i++)
{
int a;
a = i ^ 100;
int b = a >> 2 << 3;
}
return DateTime.Now;
}
static DateTime Method2()
{
int size = list.Count;
for (int i = 0; i < size; i++)
{
int a;
a = i ^ 100;
int b = a >> 2 << 3;
}
return DateTime.Now;
}
static void Main()
{
GetList();
var a = DateTime.Now;
Console.WriteLine(string.Format("Method 1. Time elapsed: {0}", (Method1() - a)));
a = DateTime.Now;
Console.WriteLine(string.Format("Method 2. Time elapsed: {0}", (Method2() - a)));
Console.ReadLine();
}
}
}
надеюсь я не тупанул, хотя как оговаривалось ранее, все зависит от реализации используемого объекта.
p.s. было интересно, а будет-ли разница? Оказалось есть.
Проц Core2Duo E8400
надеюсь я не тупанул, хотя как оговаривалось ранее, все зависит от реализации используемого объекта.
p.s. было интересно, а будет-ли разница? Оказалось есть.
В примере топикстартера используется метод someshit.count(); , а не свойство, как у тебя.
В твоем примере разница между вызовом свойства и локальной переменной - минимальна.
А вот если count() - это метод, со сложной внутренней реализацией, то разница будет намного большей.
PS
Да, и использованный тест не очень показателен. Нужно что бы время выполнения было хотя бы несколько секунд. Разница в 3 сотых - вполне может быть случайной. Особенно в дотнете.
fucker"ok
07.05.2009, 23:07
по-моему такого вообще не должно быть, должна быть переменная Size, которая обновляется сама собой.
В том то и дело, что как правило внутри класса есть такая переменная, но почему-то во многих фреймворках, например в QT делается так, что все свойства закрытые, а доступ к ним получаем через методы.
Довольно часто можно встретить такое:
class a
{
public:
int count() { return size;}
private:
int size;
};
В том то и дело, что как правило внутри класса есть такая переменная, но почему-то во многих фреймворках, например в QT делается так, что все свойства закрытые, а доступ к ним получаем через методы.
На данный момент, доступ к переменным через геттеры/сеттеры - стандарт. (И это действительно оправдано)
З.Ы.
Если не понятно почему, то во первых чтобы со стороны никто не мог внезапно изменить данное поле (в случае public ты можешь записать левое значение в переменную size и класс об этом не узнает, чем черевато - понятно), а так ты можешь сделать поле доступным извне только на чтение, или только на запись, во вторых если требуется изменить внутреннюю структуру класса и удалить/переименовать эту переменную, чтобы не требовалось искать всех пользователей класса, и менять их. (Просто достаточно будет изменить реализацию геттеров/сеттеров, тогда интерфейс класса останется неизменным). Вот по крайней мере 2 плюса использования геттеров сеттеров.
Ну ещё можно при помощи изменения геттеров подгонять объекты под требования пользователя класса, или добавлять в них проверки ввода, но сложная логика в геттерах/сеттерах - не клёво и неправильно.
Проголосовал за 1-й вариант, потом вдумался и пришёл к выводу что пишу всё-же по второму варианту.
В принципе если метод count() имеет какую то сложную реализацию то это существенно скажется на производительность.
fucker"ok
08.05.2009, 01:18
Если не понятно почему, то во первых чтобы со стороны никто не мог внезапно изменить данное поле (в случае public ты можешь записать левое значение в переменную size и класс об этом не узнает, чем черевато - понятно),
Как-то не подумал об этом. Действительно, это страхует. :)
В примере топикстартера используется метод someshit.count(); , а не свойство, как у тебя.
В твоем примере разница между вызовом свойства и локальной переменной - минимальна.
А вот если count() - это метод, со сложной внутренней реализацией, то разница будет намного большей.
PS
Да, и использованный тест не очень показателен. Нужно что бы время выполнения было хотя бы несколько секунд. Разница в 3 сотых - вполне может быть случайной. Особенно в дотнете.
хм так и знал что глупость сделал, но думаю это можно решить если использовать LINQ запрос.
вечером как окажусь дома переделаю...
думаю будет нагляднее.
update:
не выдержал, проверил с LINQ на работе, результат без профайлера:
Method 1. Time elapsed: 00:00:00.0468753
Method 2. Time elapsed: 00:00:00
update2:
с профайлером:
Method 1. Time elapsed: 00:00:00.0781250
Method 2. Time elapsed: 00:00:00.0156250
сколько раз гонялся тест? нужно минимум для каждого варианта прогнать по 10-50 раз. чтобы исключить момент подгрузки кода в кеш.
по поводу опроса - не хватает еще варианта ответа - одно***ственно. ибо потери на вызове метода, пусть даже виртуального, не окажут никакого ощутимого влияния. а лишняя локальная переменная в таком месте, объявленная вне цикла, может позже помешать вынести этот цикл в отдельный метод. так что чисто из соображений дальнейшей поддержки проекта и читабельности я бы предпочел первый вариант.
по поводу форматирования - почитайте Макконнелла.
по поводу переменных - int x = strlen("blabla") - strlen("bla"); на уровне асма будет эквивалентен коду
int blabla_len = strlen("blabla");
int bla_len = strlen("bla");
int blabla_len_dif = blabla_len - bla_len;
суть отказа от локальных переменных как раз в другом. чтобы в больших методах можно было легко читать логику. этот код например в случае нахождения в большом методе выносится в отдельный и заменяется на blabla_len_dif("blabla", "bla"); ибо это понятнее и требует меньше времени на чтения, ибо банально занимает 1 строку. ну и в добавок сей код можно в дальнейшем вызывать из любого места класса, плюс сделать метод публичным и дать другим классам с ним работать.
зы: чтите Фаулера, Макконнелла, GoF.
Совет: пишите так, как проще и меньше кода.
Оптимиировать надо не все подряд, а только узкие места.
Все подряд оптимизируют лишь распиздяи ;)
W!z@rD
попробуй поменять местами тесты
по 10-50 раз
10к-50к я бы сказал.
оптимизировать нада лишь после окончания программирования, после тестов с профайлером. раньше смысла нет. ну и плюс статистика - большая часть выполнения порграммы фокусируется в 10% строках кода. так что если будете оптимизировать весь код - потратите 90% времени впустую.
10к-50к я бы сказал
это актуально если тестится работа с большим объекмом данных, чтобы надежно поместить его в оп или кеш. а тут просто вызов, поэтому нам нужно удостоверицо что код в оп и в кеше проца. а для этого в принципе хватит его 1 раз вызвать. ну а 50 чтоб на случай если вдруг кто вытолкнет код из кеша.
не для больших объемов данных, а для сбора той же самой статистики.
ну пох =) просто имхо для данного случая результат будет принципиально разнИца при первом тесте, в остальных будет уже среднее значение. ну кароч кто как хочет, тот так и тестит
оптимизировать нада лишь после окончания программирования, после тестов с профайлером. раньше смысла нет.
Ага, после "окончания программирования" оптимизировать как правило уже поздно, ибо код к тому моменту представляет собой мусорник без крышки.
Ага, после "окончания программирования" оптимизировать как правило уже поздно, ибо код к тому моменту представляет собой мусорник без крышки. Неправда :) Оптимизированный код читать куда сложнее чем неоптимизированный.
Обычный процесс разработки: программирование как удобно, написание тестов и их прогон, рефакторинг, прогон тестов. И так до полного удовлетворения. Затем работа с профайлером, выяснение узких мест и их оптимизация. И вот после этого код уже вполне может стать не таким красивым и элегантным. (До этого код прост и понятен, в результате рефакторинга, главное рефакторить почаще)
З.Ы.
В общем, присоединюсь к Ra$cal, читайте Фаулера.
З.З.Ы.
Ну вообще тесты рекомендуют писать до написания кода, но у меня на практике так ни разу не получалось.
Кстати да. Забыл сказать, что я за первый вариант для веба.
Больше пары десятков итераций на практике не встречается.
Обычный процесс разработки: программирование как удобно
Лично для меня это не есть нормальный процесс разработки. Нормальный процесс разработки включает в себя имплементацию с соблюдением культуры кодирования, а не "как удобно". А она (культура) подразумевает и оптимизацию в том числе.
Тестирование может выявить слабые места программы, которые затем нужно оптимизировать более тщательно. Но это не означает что остальной код должен быть грязным.
В общем, присоединюсь к Ra$cal, читайте Фаулера.
Фаулер мужик неоднозначный, не нужно на него молиться как на икону, у него много спорных мест. К тому же, книга "Рефакторинг" не имеет никакого отношения к оптимизации, как тут уже замечали.
PS
И кстати на возможное снижение производительности при замене переменной на вызов метода - пишет сам же Фаулер в той же книжке.
Algol, дык рефакторинг для того и придуман, чтобы избавляться от грязного кода. А оптимизация (не считая алгоритмической), часто бывает избыточной, и отнимает слишком много времени, для этого и не следует оптимизировать пока не пробежишься по коду профайлером. Это делается после получения работоспособной версии. А задача тестов, не в том чтобы выявлять слабые места программы, а в том, чтобы гарантировать неизменность её работоспособности при последующих изменениях, в том числе при рефакторинге и оптимизации. (Ну или, если полностью следовать TDD, ещё показывает процент готовности приложения)
К тому же, книга "Рефакторинг" не имеет никакого отношения к оптимизации, как тут уже замечали. Безусловно. Мало того, рефакторинг зачастую замедляет работу программы. Но при этом хорошо структурированный код проще будет оптимизировать.
И кстати на возможное снижение производительности при замене переменной на вызов метода - пишет сам же Фаулер в той же книжке.
он пишет о других типах методов. которые выполняют вычисления. гетеры и сетеры к этим типам методов не относятся.
А оптимизация (не считая алгоритмической), часто бывает избыточной, и отнимает слишком много времени
Ну может ты и прав :)
Хотя, это зависит от предметной области. Не думаю что в графических движках, например, оптимизация бывает "избыточной" :)
Ну может ты и прав :)
Хотя, это зависит от предметной области. Не думаю что в графических движках, например, оптимизация бывает "избыточной" :) Пример из анекдота: "Мы переписали весь код на ассемблер, добившись повышения производительности на 3%, и увеличения времени дебага на 300%" :)
он пишет о других типах методов. которые выполняют вычисления. гетеры и сетеры к этим типам методов не относятся.
А мы и не про геттеры говорим...
Посмотри внимательно изначальный код. Там метод, а не геттер.
Algol, лучше написать 10 программ со 100% производительности, чем это же время потратить на 1 с 150%процентной.
Ведь за деньги с 9 остальных программ можно купить железо на увеличение произвродительности на все 10000%.
А мы и не про геттеры говорим...
Посмотри внимательно изначальный код. Там метод, а не геттер.
все зависит от реализации того класса. он может хранить число элементов и менять переменную при модификации объекта(пушнули запись, проинкрементили коунтер), или же вычислять число элементов при каждом вызове. так вот тут как раз и кроется простор для оптимизации. вместо того, чтобы в каждом цикле делать int arrsize = arr.count() проще сделать внутри класса массива поле размера. собсно к этому и сводится рефакторинг =) все куски кода делаем очевидными, убираем дублирование кода, а потом в случае необходимости правим в некоторых местах, ибо одинаковый код по максималу убран. обрати внимание насколько проще это дело оптимизировать - всего в одном месте, и в самом логичном - в классе массива.
но естественно есть варианты кода, где такое не канает, и понадобится другая оптимизация. ну или в случае, когда нет доступа к исходному коду. но тогда как вариант можно просто наследовать класс от этого или сделать фасад и добавить сию фишку руками.
бла-бла-бла Т.е. лучше завалить проект, но некоторую ее часть сделать чуть быстрее, чем сдать готовый проект?
Дело в приоритетах. Я выбираю сдать проект.
Т.е. лучше завалить проект, но некоторую ее часть сделать чуть быстрее, чем сдать готовый проект?
Дело в приоритетах. Я выбираю сдать проект.
Разве я это сказал? Разве оптимизация ведет к завалу проекта? К чему эти фантазии?
Просто лично я (написать триста раз ИМХО?) привык писать код максимальный по быстродействию.
Если я вижу что вариант с временной переменной быстрее (а для меня еще и читабельнее, интуитивно понятнее!) чем с многократным вызовом функции - то мне что, специально писать заведомо более медленный вариант? Только потому что так сказал Фаулер, и что это по фен-шую? Я автоматом выношу все вызовы функций из цикла, если они не зависят от параметра, и для меня это совершенно не составляет труда и времени, а потому ни о каком завале проекта речь не идет. Конечно, если оптимизация с сомнительной рациональностью занимает пол дня, я ее, разумеется, не делаю. Но если она у меня на автомате, зачем от нее отказываться?
я бы еще добавил - выбираю сдать проект, суметь потом этот проект поддерживать, исправлять и дополнять
[added]
если ты пишешь проект один - то никакой проблемы нету. но когда пишешь в команде - тут главным становится критерий читабельности, а не скорости. поэтому все и пишут читабельный код. потом профайлят, и узкие места приводят к менее читабельному виду. но таких мест не много и все счастливы - и клиент получил быструю программу, и разработчики могут с ней работать дальше. а вариант на ходу оптимазить все операции - нерационален. я раньше так же писал. долго писал =) потом понял что лажа это все. не стоит оно усилий потраченых. но это все дело вкуса. на фаулера никто не молится. просто находим с ним общие решения =)
Но если она у меня на автомате, зачем от нее отказываться? Согласен, это работа программиста - писать адекватный код. Речь идёт о намеренной оптимизации с подобными заморочками, типа замены геттеров/сеттеров, публичным полем класса (в ущерб нормальной инкапсуляции), или к примеру намерянным разворачиванием цикла в N последовательных вызовов (в ущерб читабельности) и т.д. В общем, до прохода профайлером, оптимизации не должна идти во вред читабельности и возможности модификации программы + не должна требовать большого количества доп. усилий, т.к. ты до профайлера не можешь сказать, какие участки стоит оптимизировать в первую очередь, а на какие можно вообще забить.
добавлю еще...
для некоторых людей оптимизацию во время написания проекта делать не стоит, стоит доделать проект до конца и после этого начинать оптимизировать (для тех кто плохо понимает/умеет оптимизировать) для тех кто постоянно старается оптимизировать проект по ходу его разработки и написания удобнее будет сразу задумываться об этом (но из-за этого может возникнуть ряд проблем)
по поводу частичной оптимизаци могу сказать следующее:
она должна иметь место, если кусок оптимизируемого кода очень часто используется в проекте (будь то процедура или функциия которая многократно вызывается за очень маленький промежуток времени (<1сек))
p.s. вообще оптимизировать проект или какой либо отдельный участок кода дело программиста хочет он или нет, ибо это его детище и если оно будет медленно работать или даже подвисать, то это останется на совести/репутации/уровне автора.
(кто то может не согласиться, а я и не против ведь мнение каждого должно быть выслушано)
p.p.s. прочитав всю тему я частично согласен с большинством мнений.
предлагаю закрыть тему т.к. автор темы получил ответ на свой вопрос... и продолжать тему нет смысла, из этого получится "болталка" . Новых объективных мнений и фактов в этой теме уже наверно не появится, на 4 страницах итак есть много повторений. Так что если никто не против в том числе и автор то можно закрывать тему.
p.s. вообще оптимизировать проект или какой либо отдельный участок кода дело программиста хочет он или нет, ибо это его детище и если оно будет медленно работать или даже подвисать, то это останется на совести/репутации/уровне автора. Вообще обычно есть совершенно конкретные требования к производительности и к срокам разработки и от этих требований и нужно отталкиваться. Ты же пишешь не один.
2Qwazar
согласен. но бывают случаи когда производительность не оговаривается и тогда решение остается за программистом.
vBulletin® v3.8.14, Copyright ©2000-2026, vBulletin Solutions, Inc. Перевод: zCarot