многократный вызов функции. оптимизация

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by fucker"ok, 7 May 2009.

?
  1. 1-ый вариант

    11 vote(s)
    28.2%
  2. второй

    28 vote(s)
    71.8%
  1. fucker"ok

    fucker"ok Elder - Старейшина

    Joined:
    21 Nov 2004
    Messages:
    580
    Likes Received:
    279
    Reputations:
    91
    Вопрос чисто ради интереса.

    Code:
    for (int i = 0; i < someshit.count(); i++) {}
    
    Code:
    int arraySize = someshit.count();
    for (int i = 0; i < arraySize; i++) {}
    
    В первом случае метод count() вызывается Х количество раз. Во втором - один раз.

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

    RumShun Member

    Joined:
    27 Oct 2008
    Messages:
    283
    Likes Received:
    75
    Reputations:
    6
    в вопросе и ответ, если не хочешь count() вызывать тыщу раз то стоит, проголосовал за второй вариант, хотя пишу в Делфи а там разницы нет
     
  3. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    второй вариант значительно будет виден когда someshit.count() будет около 1кк или больше...
    смотря конечно что такое someshit если это класс а count() функция, то надо смотреть как она получает результат... тут может быть несколько споссобов
    1)ф-ия в цикле считает результат и выдает...
    2)в классе предусмотрена переменная для хранения этого результата, которая при каждом изменеии данных пересчитывается и сохраняется в памяти тогда count() просто считывает эту переменную и выдает её результат.

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


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

    Algol New Member

    Joined:
    29 May 2002
    Messages:
    1,759
    Likes Received:
    4
    Reputations:
    0
    Однозначно второй вариант
     
  5. t4Nk

    t4Nk Elder - Старейшина

    Joined:
    23 Sep 2007
    Messages:
    70
    Likes Received:
    33
    Reputations:
    0
    второй вариант, только пишу так:
    Code:
    for (int i = 0, arraySize = someshit.count(); i < arraySize; i++) {}
    
     
  6. Qwazar

    Qwazar Elder - Старейшина

    Joined:
    2 Jun 2005
    Messages:
    989
    Likes Received:
    904
    Reputations:
    587
    Конечно второй вариант быстрее.

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

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

    З.З.Ы.
    А по поводу названия опроса "Как вы пишете", я пишу как во 2м варианте, но к примеру Фаулер, в книге по рефакторингу рекомендует писать как в первом варианте, для того чтобы избавится от лишних переменных и сделать код более наглядным. Ну это правда к этапу оптимизации не относится, а делается раньше :)
     
    #6 Qwazar, 7 May 2009
    Last edited: 7 May 2009
  7. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    думаю не стоит к этому прислушиваться.... ибо код надо форматировать только потому что мы не машины и если писать код в одну строчку то его сразу не понять...(очень не сразу..) но самое главное для программиста должно быть оптимизация и отсутствие багов в своем детище (не если не отсутствию, то хотябы свести все баги к минимуму...)
    а наглядность это не для кода... (но если кодер ставит на первый план наглядность то это его дело)
     
    _________________________
  8. Qwazar

    Qwazar Elder - Старейшина

    Joined:
    2 Jun 2005
    Messages:
    989
    Likes Received:
    904
    Reputations:
    587
    KIR@PRO, код пишут для людей а не для компов. (Ну без фанатизма, я к тому что код должен быть понятен) В больших проектах за непонятный код надо по пальцам стучать. Представь как его потом апдейтить и багфиксить. Но по мне временные переменные - наоборот могут внести ясность, за счёт нормального имени переменной.

    З.Ы.
    А оптимизация это уже последний этап, когда код работает и ты по нему бегаешь профайлером, выясняя что именно стоит оптимизировать.
     
    #8 Qwazar, 7 May 2009
    Last edited: 7 May 2009
  9. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    дак я и говорю что элементарную табуляцию и прочее надо соблюдать, названия переменных делать понятными... это уде слишком, этож прямое принебрежение производительностью ради маленькой наглядности...

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

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

    scrat кодер

    Joined:
    8 Apr 2007
    Messages:
    625
    Likes Received:
    541
    Reputations:
    3
    по-моему такого вообще не должно быть, должна быть переменная Size, которая обновляется сама собой.

    проголосовал за первое, случайно.
     
    1 person likes this.
  11. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    посмотрел...

    результат под запуском профайлера:
    [​IMG]

    результат профайлера:
    [​IMG]

    используемый код:
    PHP:
    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 (
    MaxLength)
                {
                    list.
    Add(i);
                    
    i++;
                }
            }

            static 
    DateTime Method1()
            {
                for (
    int i 0< list.Counti++)
                {
                    
    int a;
                    
    100;
                    
    int b >> << 3;
                }
                return 
    DateTime.Now;
            }

            static 
    DateTime Method2()
            {
                
    int size = list.Count;
                for (
    int i 0sizei++)
                {
                    
    int a;
                    
    100;
                    
    int b >> << 3;
                }
                return 
    DateTime.Now;
            }

            static 
    void Main()
            {
                
    GetList();
                var 
    DateTime.Now;
                
    Console.WriteLine(string.Format("Method 1. Time elapsed: {0}", (Method1() - a)));
                
    DateTime.Now;
                
    Console.WriteLine(string.Format("Method 2. Time elapsed: {0}", (Method2() - a)));
                
    Console.ReadLine();
            }
        }
    }
    надеюсь я не тупанул, хотя как оговаривалось ранее, все зависит от реализации используемого объекта.

    p.s. было интересно, а будет-ли разница? Оказалось есть.

    Проц Core2Duo E8400
     
    #11 W!z@rD, 7 May 2009
    Last edited: 7 May 2009
    1 person likes this.
  12. Algol

    Algol New Member

    Joined:
    29 May 2002
    Messages:
    1,759
    Likes Received:
    4
    Reputations:
    0
    В примере топикстартера используется метод someshit.count(); , а не свойство, как у тебя.
    В твоем примере разница между вызовом свойства и локальной переменной - минимальна.
    А вот если count() - это метод, со сложной внутренней реализацией, то разница будет намного большей.

    PS
    Да, и использованный тест не очень показателен. Нужно что бы время выполнения было хотя бы несколько секунд. Разница в 3 сотых - вполне может быть случайной. Особенно в дотнете.
     
  13. fucker"ok

    fucker"ok Elder - Старейшина

    Joined:
    21 Nov 2004
    Messages:
    580
    Likes Received:
    279
    Reputations:
    91
    В том то и дело, что как правило внутри класса есть такая переменная, но почему-то во многих фреймворках, например в QT делается так, что все свойства закрытые, а доступ к ним получаем через методы.
    Довольно часто можно встретить такое:
    Code:
    class a
    {
    public:
        int count() {   return size;}
    private:
        int size;
    };
    
     
    1 person likes this.
  14. Qwazar

    Qwazar Elder - Старейшина

    Joined:
    2 Jun 2005
    Messages:
    989
    Likes Received:
    904
    Reputations:
    587
    На данный момент, доступ к переменным через геттеры/сеттеры - стандарт. (И это действительно оправдано)

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

    Ну ещё можно при помощи изменения геттеров подгонять объекты под требования пользователя класса, или добавлять в них проверки ввода, но сложная логика в геттерах/сеттерах - не клёво и неправильно.
     
    #14 Qwazar, 8 May 2009
    Last edited: 8 May 2009
    1 person likes this.
  15. Frimen

    Frimen New Member

    Joined:
    7 May 2009
    Messages:
    1
    Likes Received:
    0
    Reputations:
    0
    Проголосовал за 1-й вариант, потом вдумался и пришёл к выводу что пишу всё-же по второму варианту.

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

    fucker"ok Elder - Старейшина

    Joined:
    21 Nov 2004
    Messages:
    580
    Likes Received:
    279
    Reputations:
    91
    Как-то не подумал об этом. Действительно, это страхует. :)
     
  17. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    хм так и знал что глупость сделал, но думаю это можно решить если использовать 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
     
    #17 W!z@rD, 8 May 2009
    Last edited: 8 May 2009
  18. Ra$cal

    Ra$cal Elder - Старейшина

    Joined:
    16 Aug 2006
    Messages:
    670
    Likes Received:
    185
    Reputations:
    78
    сколько раз гонялся тест? нужно минимум для каждого варианта прогнать по 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.
     
    #18 Ra$cal, 14 May 2009
    Last edited: 14 May 2009
    1 person likes this.
  19. nerezus

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    729
    Reputations:
    266
    Совет: пишите так, как проще и меньше кода.
    Оптимиировать надо не все подряд, а только узкие места.

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

    Forcer Elder - Старейшина

    Joined:
    12 Apr 2007
    Messages:
    321
    Likes Received:
    98
    Reputations:
    12
    W!z@rD
    попробуй поменять местами тесты

    10к-50к я бы сказал.
     
    #20 Forcer, 14 May 2009
    Last edited: 14 May 2009