Влияют ли геттеры и сеттеры на производительность в C ++ / D / Java? - PullRequest
30 голосов
/ 10 июля 2009

Это довольно старая тема: хороши или вредны сеттеры и геттеры?

Мой вопрос здесь: сделать компиляторы в C ++ / D / Java встроить геттеры и сеттеры?

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

Ответы [ 10 ]

23 голосов
/ 10 июля 2009

Это зависит. Не существует универсального ответа, который всегда будет правдой.

В Java JIT-компилятор, вероятно, рано или поздно вставит его . Насколько я знаю, JIT-компилятор JVM оптимизирует только интенсивно используемый код, так что вы могли видеть издержки при вызове функции изначально, до тех пор, пока метод get / setter не был вызван достаточно часто.

В C ++ он почти наверняка будет встроенным (при условии, что оптимизация включена). Однако есть один случай, когда этого, вероятно, не будет:

// foo.h
class Foo {
private:
  int bar_;

public:
  int bar(); // getter
};

// foo.cpp
#include "foo.h"

int Foo::bar(){
  return bar_;
}

Если определение функции невидимо для пользователей класса (который будет включать foo.h, но не будет видеть foo.cpp), то компилятор не сможет встроить вызов функции.

MSVC должен иметь возможность встроить его, если генерация кода времени соединения включена в качестве оптимизации. Я не знаю, как GCC решает эту проблему.

По сути, это также означает, что, если получатель определен в другом .dll / .so, вызов может не быть встроенным.

В любом случае, я не думаю, что тривиальные get / setters обязательно являются «хорошей практикой ООП», или что есть «все другие причины их использования». Многие люди считают тривиальные get / setters для 1) признаком плохого дизайна и 2) пустой тратой текста.

Лично это не то, что меня беспокоит в любом случае. Для меня, чтобы что-то квалифицировать как «хорошую ООП-практику», оно должно иметь определенные количественные положительные эффекты. Тривиальные методы получения / установки имеют некоторые незначительные преимущества, а некоторые - столь же незначительные недостатки. Поэтому я не думаю, что это хорошая или плохая практика. Это просто то, что вы можете сделать, если действительно хотите.

9 голосов
/ 10 июля 2009

В D все методы классов, но не структур, по умолчанию являются виртуальными. Вы можете сделать метод не виртуальным, сделав метод или весь класс окончательным. Кроме того, D имеет синтаксис свойств, который позволяет вам сделать что-то открытым полем, а затем изменить его на метод получения / установки без нарушения совместимости на уровне источника. Поэтому в D я бы рекомендовал просто использовать открытые поля, если у вас нет веских причин поступить иначе. Если по какой-то причине вы хотите использовать тривиальные методы получения / установки, например, иметь только геттер и делать переменную доступной только для чтения вне класса, сделайте их окончательными.


Редактировать: Например, строки:

S s;
s.foo = s.bar + 1;

будет работать для обоих

struct S
{
     int foo;
     int bar;
}

и

struct S
{
     void foo(int) { ... }
     int bar() { ... return something; }
}
6 голосов
/ 15 апреля 2012

Я пробовал в Java: то же самое, с и без getter и setter. Результат: нет существенной разницы между временем выполнения двух версий. Вот код:

class Person
{
    public int age;
    String name;
    public Person(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }

}
class GetSetPerson
{
    private int age;
    String name;
    public GetSetPerson(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }
    public void setAge(int newage)
    {
        age=newage;
    }
    public int getAge()
    {
    return age; 
    }
}
class Proba
{
//Math.hypot kb 10-szer lassabb, mint a Math.sqrt(x*xy*y)!!!

public static void main(String args[])
{
long startTime, endTime, time;
int i;int agevar;
//Person p1=new Person("Bob",21);
GetSetPerson p1=new GetSetPerson("Bob",21);
startTime=System.nanoTime();
/*
for (i=0;i<1000000000;i++)
     {
     p1.age++;

     }

*/    
for (i=0;i<1000000000;i++)
     {
     agevar=p1.getAge();
     agevar++;
     p1.setAge(agevar);
     }

endTime=System.nanoTime();
time=endTime-startTime;
System.out.println(""+time);
System.out.println(p1.name+"'s age now  is "+p1.getAge());
}

}

Я знаю, в конце Боб немного старше меня, но для этого все должно быть в порядке.

6 голосов
/ 11 июля 2009

У меня был точно такой же вопрос один раз.

Чтобы ответить, я запрограммировал две маленькие программы:

Первый:

#include <iostream>

class Test
{
     int a;
};

int main()
{
    Test var;

    var.a = 4;
    std::cout << var.a << std::endl;
    return 0;
}

Второй:

#include <iostream>

class Test
{
     int a;
     int getA() { return a; }
     void setA(int a_) { a=a_; }
};

int main()
{
    Test var;

    var.setA(4);
    std::cout << var.getA() << std::endl;
    return 0;
}

Я скомпилировал их на ассемблере, с -O3 (полностью оптимизирован) и сравнил два файла. Они были идентичны. Без оптимизации они были разные.

Это было под g ++. Итак, чтобы ответить на ваш вопрос: компиляторы легко оптимизируют геттеры и сеттеры.

4 голосов
/ 10 июля 2009

В зависимости от ожидаемой эволюции вашего класса, get / setters могут загромождать ваш код и давать вам гибкость в расширении вашей реализации, не затрагивая клиентский код.

Часто я сталкиваюсь с классами, которые используются как «контейнеры данных», в которых есть как getter, так и setter для каждого члена. Это чепуха. Если вы не ожидаете, что вам понадобятся какие-то «специальные» функциональные возможности при получении или настройке члена, не пишите это.

Учитывая скорость машины, усилия по написанию дополнительной абстракции будут стоить вашим клиентам больше денег.

Большинство компиляторов могут оптимизировать тривиальные методы получения / установки, но как только вы объявляете их виртуальными (в C ++), вы платите дополнительный поиск - часто стоит усилий.

4 голосов
/ 10 июля 2009

Любая JVM (или компилятор), достойная своей соли, должна поддерживать встраивание В C ++ компилятор включает геттеры и сеттеры. В Java JVM указывает их во время выполнения после того, как они были вызваны «достаточно» раз. Я не знаю о Д.

3 голосов
/ 11 июля 2009

Цитирование из здесь Относительно программирования D D Langiage

Я думаю, что DMD в настоящее время не может де-виртуализировать виртуальные геттеры и сеттеры. Виртуальные вызовы немного медленнее сами по себе, но они также не позволяют вставлять, поэтому последовательные стандартные оптимизации не могут быть выполнены. Таким образом, если такой доступ к атрибуту является виртуальным вызовом, и это происходит в «горячей» части кода, то это может значительно замедлить ваш код. (если это происходит в не горячих частях кода, это обычно не имеет никакого эффекта. Вот почему Java Hot Spot не нужно оптимизировать весь ваш код для создания очень быстрой программы).

Я призвал Фрица ван Боммеля улучшить возможности НРС по девиртуализации:

Теперь НРС может сделать это в нескольких очень простых ситуациях, но в большинстве случаев ситуация остается неизменной по сравнению с МДД. Со временем LLVM улучшится, поэтому ситуация может улучшиться сама собой. Но интерфейс тоже может что-то с этим сделать.

Вот некоторая документация по этой теме, более старая , более современная :

3 голосов
/ 10 июля 2009

В некоторых случаях для некоторых участников потребуется получатель. Однако предоставление метода получения и установки для каждого члена данных класса не является хорошей практикой. Это усложнит интерфейс класса. Кроме того, сеттеры могут вызвать проблемы с владением ресурсами, если не будут обработаны должным образом.

2 голосов
/ 10 июля 2009

В C ++, когда оптимизация компилятора включена, геттеры и сеттеры могут быть «встроены»: то есть реализованы с использованием тех же машинных инструкций, что и для прямого доступа к базовым данным элемента.

1 голос
/ 10 июля 2009

Я повторю Xtofl.

Геттеры и сеттеры - одна из тех вещей, которые иногда полезны, но вы заставляете тех докторантов, которые каким-то образом прыгают из «оказался полезен в некоторых случаях», использовать все время, а если вы их не используете ты еретик, которого следует убить ".

Если у вас есть побочные эффекты для получения или установки, во что бы то ни стало используйте геттер или сеттер.

Но если нет, то написание методов получения и установки просто загромождает код. Есть небольшое снижение производительности. В Java, если он выполняется только несколько раз, штраф за перфорацию не должен иметь значения, а если он выполняется часто, он должен быть встроен, чтобы штраф был смягчен. Поэтому я бы больше беспокоился о том, чтобы сделать код труднее для чтения.

И ни на минуту не покупайте аргумент, что это устраняет проблему глобальных данных. Глобальные данные плохие и их следует избегать. Но объявление поля-члена частным, а затем создание общедоступных методов получения и установки для ничего не решает проблему.

...