PDA

Просмотр полной версии : многократный вызов функции. оптимизация


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() вызывается Х количество раз. Во втором - один раз.

Интересует мнение людей, которые знают как это работает на более низком уровне.
Много ли различных вызовов\обращений к памяти экономит второй пример и есть ли вообще смысл оптимизировать код данным образом?

RumShun
07.05.2009, 06:59
В первом случае метод count() вызывается Х количество раз. Во втором - один раз. в вопросе и ответ, если не хочешь count() вызывать тыщу раз то стоит, проголосовал за второй вариант, хотя пишу в Делфи а там разницы нет

KIR@PRO
07.05.2009, 07:50
второй вариант значительно будет виден когда someshit.count() будет около 1кк или больше...
смотря конечно что такое someshit если это класс а count() функция, то надо смотреть как она получает результат... тут может быть несколько споссобов
1)ф-ия в цикле считает результат и выдает...
2)в классе предусмотрена переменная для хранения этого результата, которая при каждом изменеии данных пересчитывается и сохраняется в памяти тогда count() просто считывает эту переменную и выдает её результат.

сам понимаеш если первый вариант то это очень много лишних операций для тебя... если же в классе все как во втором случае то ты всеравно на каждый шаг цикла получаеш 1-2 лишних операции...


p.s. если ты уверен что значение count() у тебя всегда будет не больше 1000 то оба споссоба для человека будут одинаковы (человек не сможет заметить разницу выполнения программ в 100-200 тактов (ну может и побольше))

Algol
07.05.2009, 10:41
Однозначно второй вариант

t4Nk
07.05.2009, 11:10
второй вариант, только пишу так:

for (int i = 0, arraySize = someshit.count(); i < arraySize; i++) {}

Qwazar
07.05.2009, 11:30
Конечно второй вариант быстрее.

З.Ы.
А если ещё и цикл разложить в последовательные вызовы и таким образом избавиться от условия на каждой итерации, так вообще :) Но это не твой случай, т.к. ты не знаешь count.

Единственное - умные компиляторы всё это дело оптимизируют.

З.З.Ы.
А по поводу названия опроса "Как вы пишете", я пишу как во 2м варианте, но к примеру Фаулер, в книге по рефакторингу рекомендует писать как в первом варианте, для того чтобы избавится от лишних переменных и сделать код более наглядным. Ну это правда к этапу оптимизации не относится, а делается раньше :)

KIR@PRO
07.05.2009, 19:06
избавится от лишних переменных и сделать код более наглядным
думаю не стоит к этому прислушиваться.... ибо код надо форматировать только потому что мы не машины и если писать код в одну строчку то его сразу не понять...(очень не сразу..) но самое главное для программиста должно быть оптимизация и отсутствие багов в своем детище (не если не отсутствию, то хотябы свести все баги к минимуму...)
а наглядность это не для кода... (но если кодер ставит на первый план наглядность то это его дело)

Qwazar
07.05.2009, 19:09
KIR@PRO, код пишут для людей а не для компов. (Ну без фанатизма, я к тому что код должен быть понятен) В больших проектах за непонятный код надо по пальцам стучать. Представь как его потом апдейтить и багфиксить. Но по мне временные переменные - наоборот могут внести ясность, за счёт нормального имени переменной.

З.Ы.
А оптимизация это уже последний этап, когда код работает и ты по нему бегаешь профайлером, выясняя что именно стоит оптимизировать.

KIR@PRO
07.05.2009, 19:42
дак я и говорю что элементарную табуляцию и прочее надо соблюдать, названия переменных делать понятными... это уде слишком, этож прямое принебрежение производительностью ради маленькой наглядности...

p.s. может мы просто не поняли друг друга))) я же говорил именно о том что надо избавляться от временных переменных чтоб сделать код более наглядным...

p.p.s. но это мнение каждого из нас)

scrat
07.05.2009, 20:15
по-моему такого вообще не должно быть, должна быть переменная Size, которая обновляется сама собой.

проголосовал за первое, случайно.

W!z@rD
07.05.2009, 20:21
результат под запуском профайлера:
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

Algol
07.05.2009, 22:42
надеюсь я не тупанул, хотя как оговаривалось ранее, все зависит от реализации используемого объекта.

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;
};

Qwazar
08.05.2009, 00:19
В том то и дело, что как правило внутри класса есть такая переменная, но почему-то во многих фреймворках, например в QT делается так, что все свойства закрытые, а доступ к ним получаем через методы.
На данный момент, доступ к переменным через геттеры/сеттеры - стандарт. (И это действительно оправдано)

З.Ы.
Если не понятно почему, то во первых чтобы со стороны никто не мог внезапно изменить данное поле (в случае public ты можешь записать левое значение в переменную size и класс об этом не узнает, чем черевато - понятно), а так ты можешь сделать поле доступным извне только на чтение, или только на запись, во вторых если требуется изменить внутреннюю структуру класса и удалить/переименовать эту переменную, чтобы не требовалось искать всех пользователей класса, и менять их. (Просто достаточно будет изменить реализацию геттеров/сеттеров, тогда интерфейс класса останется неизменным). Вот по крайней мере 2 плюса использования геттеров сеттеров.

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

Frimen
08.05.2009, 01:08
Проголосовал за 1-й вариант, потом вдумался и пришёл к выводу что пишу всё-же по второму варианту.

В принципе если метод count() имеет какую то сложную реализацию то это существенно скажется на производительность.

fucker"ok
08.05.2009, 01:18
Если не понятно почему, то во первых чтобы со стороны никто не мог внезапно изменить данное поле (в случае public ты можешь записать левое значение в переменную size и класс об этом не узнает, чем черевато - понятно),
Как-то не подумал об этом. Действительно, это страхует. :)

W!z@rD
08.05.2009, 07:35
В примере топикстартера используется метод 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

Ra$cal
14.05.2009, 00:08
сколько раз гонялся тест? нужно минимум для каждого варианта прогнать по 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.

nerezus
14.05.2009, 01:13
Совет: пишите так, как проще и меньше кода.
Оптимиировать надо не все подряд, а только узкие места.

Все подряд оптимизируют лишь распиздяи ;)

Forcer
14.05.2009, 01:15
W!z@rD
попробуй поменять местами тесты

по 10-50 раз
10к-50к я бы сказал.

Ra$cal
14.05.2009, 01:32
оптимизировать нада лишь после окончания программирования, после тестов с профайлером. раньше смысла нет. ну и плюс статистика - большая часть выполнения порграммы фокусируется в 10% строках кода. так что если будете оптимизировать весь код - потратите 90% времени впустую.

Ra$cal
14.05.2009, 01:34
10к-50к я бы сказал
это актуально если тестится работа с большим объекмом данных, чтобы надежно поместить его в оп или кеш. а тут просто вызов, поэтому нам нужно удостоверицо что код в оп и в кеше проца. а для этого в принципе хватит его 1 раз вызвать. ну а 50 чтоб на случай если вдруг кто вытолкнет код из кеша.

Forcer
14.05.2009, 01:39
не для больших объемов данных, а для сбора той же самой статистики.

Ra$cal
14.05.2009, 01:59
ну пох =) просто имхо для данного случая результат будет принципиально разнИца при первом тесте, в остальных будет уже среднее значение. ну кароч кто как хочет, тот так и тестит

Algol
14.05.2009, 10:36
оптимизировать нада лишь после окончания программирования, после тестов с профайлером. раньше смысла нет.

Ага, после "окончания программирования" оптимизировать как правило уже поздно, ибо код к тому моменту представляет собой мусорник без крышки.

Qwazar
14.05.2009, 11:55
Ага, после "окончания программирования" оптимизировать как правило уже поздно, ибо код к тому моменту представляет собой мусорник без крышки. Неправда :) Оптимизированный код читать куда сложнее чем неоптимизированный.

Обычный процесс разработки: программирование как удобно, написание тестов и их прогон, рефакторинг, прогон тестов. И так до полного удовлетворения. Затем работа с профайлером, выяснение узких мест и их оптимизация. И вот после этого код уже вполне может стать не таким красивым и элегантным. (До этого код прост и понятен, в результате рефакторинга, главное рефакторить почаще)

З.Ы.
В общем, присоединюсь к Ra$cal, читайте Фаулера.

З.З.Ы.
Ну вообще тесты рекомендуют писать до написания кода, но у меня на практике так ни разу не получалось.

nerezus
14.05.2009, 15:31
Кстати да. Забыл сказать, что я за первый вариант для веба.
Больше пары десятков итераций на практике не встречается.

Algol
14.05.2009, 18:47
Обычный процесс разработки: программирование как удобно
Лично для меня это не есть нормальный процесс разработки. Нормальный процесс разработки включает в себя имплементацию с соблюдением культуры кодирования, а не "как удобно". А она (культура) подразумевает и оптимизацию в том числе.
Тестирование может выявить слабые места программы, которые затем нужно оптимизировать более тщательно. Но это не означает что остальной код должен быть грязным.
В общем, присоединюсь к Ra$cal, читайте Фаулера.
Фаулер мужик неоднозначный, не нужно на него молиться как на икону, у него много спорных мест. К тому же, книга "Рефакторинг" не имеет никакого отношения к оптимизации, как тут уже замечали.

PS
И кстати на возможное снижение производительности при замене переменной на вызов метода - пишет сам же Фаулер в той же книжке.

Qwazar
14.05.2009, 19:01
Algol, дык рефакторинг для того и придуман, чтобы избавляться от грязного кода. А оптимизация (не считая алгоритмической), часто бывает избыточной, и отнимает слишком много времени, для этого и не следует оптимизировать пока не пробежишься по коду профайлером. Это делается после получения работоспособной версии. А задача тестов, не в том чтобы выявлять слабые места программы, а в том, чтобы гарантировать неизменность её работоспособности при последующих изменениях, в том числе при рефакторинге и оптимизации. (Ну или, если полностью следовать TDD, ещё показывает процент готовности приложения)

К тому же, книга "Рефакторинг" не имеет никакого отношения к оптимизации, как тут уже замечали. Безусловно. Мало того, рефакторинг зачастую замедляет работу программы. Но при этом хорошо структурированный код проще будет оптимизировать.

Ra$cal
14.05.2009, 20:16
И кстати на возможное снижение производительности при замене переменной на вызов метода - пишет сам же Фаулер в той же книжке.
он пишет о других типах методов. которые выполняют вычисления. гетеры и сетеры к этим типам методов не относятся.

Algol
15.05.2009, 10:54
А оптимизация (не считая алгоритмической), часто бывает избыточной, и отнимает слишком много времени
Ну может ты и прав :)
Хотя, это зависит от предметной области. Не думаю что в графических движках, например, оптимизация бывает "избыточной" :)

Qwazar
15.05.2009, 11:12
Ну может ты и прав :)
Хотя, это зависит от предметной области. Не думаю что в графических движках, например, оптимизация бывает "избыточной" :) Пример из анекдота: "Мы переписали весь код на ассемблер, добившись повышения производительности на 3%, и увеличения времени дебага на 300%" :)

Algol
15.05.2009, 11:15
он пишет о других типах методов. которые выполняют вычисления. гетеры и сетеры к этим типам методов не относятся.
А мы и не про геттеры говорим...
Посмотри внимательно изначальный код. Там метод, а не геттер.

nerezus
15.05.2009, 17:38
Algol, лучше написать 10 программ со 100% производительности, чем это же время потратить на 1 с 150%процентной.
Ведь за деньги с 9 остальных программ можно купить железо на увеличение произвродительности на все 10000%.

Ra$cal
15.05.2009, 18:54
А мы и не про геттеры говорим...
Посмотри внимательно изначальный код. Там метод, а не геттер.
все зависит от реализации того класса. он может хранить число элементов и менять переменную при модификации объекта(пушнули запись, проинкрементили коунтер), или же вычислять число элементов при каждом вызове. так вот тут как раз и кроется простор для оптимизации. вместо того, чтобы в каждом цикле делать int arrsize = arr.count() проще сделать внутри класса массива поле размера. собсно к этому и сводится рефакторинг =) все куски кода делаем очевидными, убираем дублирование кода, а потом в случае необходимости правим в некоторых местах, ибо одинаковый код по максималу убран. обрати внимание насколько проще это дело оптимизировать - всего в одном месте, и в самом логичном - в классе массива.
но естественно есть варианты кода, где такое не канает, и понадобится другая оптимизация. ну или в случае, когда нет доступа к исходному коду. но тогда как вариант можно просто наследовать класс от этого или сделать фасад и добавить сию фишку руками.

Algol
15.05.2009, 22:26
бла-бла-бла

nerezus
15.05.2009, 23:55
бла-бла-бла Т.е. лучше завалить проект, но некоторую ее часть сделать чуть быстрее, чем сдать готовый проект?

Дело в приоритетах. Я выбираю сдать проект.

Algol
16.05.2009, 01:17
Т.е. лучше завалить проект, но некоторую ее часть сделать чуть быстрее, чем сдать готовый проект?

Дело в приоритетах. Я выбираю сдать проект.

Разве я это сказал? Разве оптимизация ведет к завалу проекта? К чему эти фантазии?
Просто лично я (написать триста раз ИМХО?) привык писать код максимальный по быстродействию.
Если я вижу что вариант с временной переменной быстрее (а для меня еще и читабельнее, интуитивно понятнее!) чем с многократным вызовом функции - то мне что, специально писать заведомо более медленный вариант? Только потому что так сказал Фаулер, и что это по фен-шую? Я автоматом выношу все вызовы функций из цикла, если они не зависят от параметра, и для меня это совершенно не составляет труда и времени, а потому ни о каком завале проекта речь не идет. Конечно, если оптимизация с сомнительной рациональностью занимает пол дня, я ее, разумеется, не делаю. Но если она у меня на автомате, зачем от нее отказываться?

Ra$cal
16.05.2009, 01:17
я бы еще добавил - выбираю сдать проект, суметь потом этот проект поддерживать, исправлять и дополнять

[added]
если ты пишешь проект один - то никакой проблемы нету. но когда пишешь в команде - тут главным становится критерий читабельности, а не скорости. поэтому все и пишут читабельный код. потом профайлят, и узкие места приводят к менее читабельному виду. но таких мест не много и все счастливы - и клиент получил быструю программу, и разработчики могут с ней работать дальше. а вариант на ходу оптимазить все операции - нерационален. я раньше так же писал. долго писал =) потом понял что лажа это все. не стоит оно усилий потраченых. но это все дело вкуса. на фаулера никто не молится. просто находим с ним общие решения =)

Qwazar
16.05.2009, 01:25
Но если она у меня на автомате, зачем от нее отказываться? Согласен, это работа программиста - писать адекватный код. Речь идёт о намеренной оптимизации с подобными заморочками, типа замены геттеров/сеттеров, публичным полем класса (в ущерб нормальной инкапсуляции), или к примеру намерянным разворачиванием цикла в N последовательных вызовов (в ущерб читабельности) и т.д. В общем, до прохода профайлером, оптимизации не должна идти во вред читабельности и возможности модификации программы + не должна требовать большого количества доп. усилий, т.к. ты до профайлера не можешь сказать, какие участки стоит оптимизировать в первую очередь, а на какие можно вообще забить.

KIR@PRO
16.05.2009, 11:33
добавлю еще...
для некоторых людей оптимизацию во время написания проекта делать не стоит, стоит доделать проект до конца и после этого начинать оптимизировать (для тех кто плохо понимает/умеет оптимизировать) для тех кто постоянно старается оптимизировать проект по ходу его разработки и написания удобнее будет сразу задумываться об этом (но из-за этого может возникнуть ряд проблем)

по поводу частичной оптимизаци могу сказать следующее:
она должна иметь место, если кусок оптимизируемого кода очень часто используется в проекте (будь то процедура или функциия которая многократно вызывается за очень маленький промежуток времени (<1сек))


p.s. вообще оптимизировать проект или какой либо отдельный участок кода дело программиста хочет он или нет, ибо это его детище и если оно будет медленно работать или даже подвисать, то это останется на совести/репутации/уровне автора.
(кто то может не согласиться, а я и не против ведь мнение каждого должно быть выслушано)

p.p.s. прочитав всю тему я частично согласен с большинством мнений.


предлагаю закрыть тему т.к. автор темы получил ответ на свой вопрос... и продолжать тему нет смысла, из этого получится "болталка" . Новых объективных мнений и фактов в этой теме уже наверно не появится, на 4 страницах итак есть много повторений. Так что если никто не против в том числе и автор то можно закрывать тему.

Qwazar
16.05.2009, 11:38
p.s. вообще оптимизировать проект или какой либо отдельный участок кода дело программиста хочет он или нет, ибо это его детище и если оно будет медленно работать или даже подвисать, то это останется на совести/репутации/уровне автора. Вообще обычно есть совершенно конкретные требования к производительности и к срокам разработки и от этих требований и нужно отталкиваться. Ты же пишешь не один.

KIR@PRO
16.05.2009, 11:46
2Qwazar
согласен. но бывают случаи когда производительность не оговаривается и тогда решение остается за программистом.