Почему я должен когда-либо использовать встроенный код?

В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.

При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.

Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».

Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this. Возьмем этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте вашего кода:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference и otherReference оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.

31
задан jonrsharpe 19 February 2015 в 21:50
поделиться

16 ответов

  • там большая разница между "обычным" кодом и встроенным кодом?

Да и нет. Нет, потому что подставляемая функция или метод имеют точно те же характеристики как регулярная, при этом самый важный - это, они оба безопасны с точки зрения типов. И да, потому что ассемблерный код, сгенерированный компилятором, будет отличаться; с регулярной функцией каждый вызов будет переведен в несколько шагов: продвижение параметров на стеке, создание перехода к функции, сование параметров, и т.д., тогда как вызов к подставляемой функции будет заменен ее фактическим кодом, как макрос.

  • встроенный код просто "форма" макросов?

Никакой ! Макрос является простой текстовой заменой, которая может привести к серьезным ошибкам. Рассмотрите следующий код:

#define unsafe(i) ( (i) >= 0 ? (i) : -(i) )

[...]
unsafe(x++); // x is incremented twice!
unsafe(f()); // f() is called twice!
[...]

Используя подставляемую функцию, Вы уверены, что параметры будут оценены, прежде чем функция на самом деле выполняется. Они также будут типом, проверенным и в конечном счете преобразованным для соответствия типам формальных параметров.

  • , Какой компромисс должен быть сделан при желании встроить код?

Обычно, выполнение программы должно быть быстрее при использовании подставляемых функций, но с большим двоичным кодом. Для получения дополнительной информации необходимо читать GoTW#33.

39
ответ дан 27 November 2019 в 21:27
поделиться

встраивание является техникой для увеличения скорости. Но используйте профилировщика для тестирования этого в ситуации. Я нашел (MSVC), который встраивание не всегда поставляет и конечно не любым захватывающим способом. Время выполнения, иногда уменьшаемое несколькими процентами, но при немного отличающихся обстоятельствах, увеличенных несколькими процентами.

, Если код работает медленно, выньте своего профилировщика для нахождения troublespots и работы над теми.

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

0
ответ дан 27 November 2019 в 21:27
поделиться

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

Обычно, Вы не должны вручную решать, должна ли функция быть встроена или нет. Например, GCC решит что автоматически в зависимости от оптимизации уровня (-Вол) и в зависимости от других параметров. Это примет вещи во внимание как, "Насколько большой функция?" (количество инструкций), как часто это называют в рамках кода, насколько двоичный файл станет больше путем встраивания его, и некоторые другие метрики. Например, если функция статична (таким образом не экспортируемый так или иначе) и только названный однажды в рамках Вашего кода, и Вы никогда не используете указатель на функцию, возможности хороши, что GCC решит встроить его автоматически, поскольку это не окажет негативного влияния (двоичный файл не станет больше путем встраивания его только однажды).

0
ответ дан 27 November 2019 в 21:27
поделиться

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

0
ответ дан 27 November 2019 в 21:27
поделиться

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

  1. , Когда использовать? Когда когда-нибудь функция имеет очень немного строк (для всех средств доступа и мутатора), но не для рекурсивных функций
  2. Преимущество? Время, потраченное для вызова вызова функции, не включено
  3. , компилятор, встроенный какая-либо собственная функция? да, когда когда-либо функция определяется в заголовочном файле в классе
0
ответ дан 27 November 2019 в 21:27
поделиться

Ответ должен Вы встраивать свестись к скорости. Если Вы находитесь в жестком цикле, вызывающем функцию, и это не супер огромная функция, но тот, где много времени потрачено впустую в ВЫЗЫВАНИИ функции, то делает ту функцию встроенной, и Вы получите много удара для Вашего маркера.

0
ответ дан 27 November 2019 в 21:27
поделиться

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

0
ответ дан 27 November 2019 в 21:27
поделиться

Встраивание обычно включается на уровне 3 оптимизации (-O3 в случае GCC). Это может быть значительное улучшение скорости в некоторых случаях (когда это возможно).

Явное встраивание в Ваших программах может добавить некоторое улучшение скорости со стоимостью увеличенного размера кода.

необходимо видеть, который подходит: размер кода или скорость и решают, необходимо ли включать его в программы.

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

0
ответ дан 27 November 2019 в 21:27
поделиться

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

классический пример А хорошего кандидата на встраивание методы считывания для простых реальных классов.

CPoint
{
  public:

    inline int x() const { return m_x ; }
    inline int y() const { return m_y ; }

  private:
    int m_x ;
    int m_y ;

};

Некоторые компиляторы (например, VC2005) имеют опцию для агрессивного встраивания, и Вы не должны были бы указывать 'встроенное' ключевое слово при использовании той опции.

0
ответ дан 27 November 2019 в 21:27
поделиться

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

Решение объявить встроенную функцию является в основном компромиссом пространства - Ваша программа будет больше, если компилятор решит встроить его (особенно, если это не также статично, в этом случае по крайней мере одна невстроенная копия требуется для использования любыми внешними объектами); действительно, если функция является большой, это могло бы привести к понижению производительности, поскольку меньше Вашего кода помещается в кэш. Общее повышение производительности, однако, состоит просто в том, что Вы избавляетесь от издержек самого вызова функции; для небольшой функции, вызванной как часть внутреннего цикла, это - компромисс, который имеет смысл.

при доверии компилятору отметьте небольшие функции, используемые во внутренних циклах inline подробно; компилятор будет ответственен за Выполнение Правильной Вещи в решении, встроить ли.

1
ответ дан 27 November 2019 в 21:27
поделиться

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

2
ответ дан 27 November 2019 в 21:27
поделиться

Это зависит от компилятора...
Говорят, что у Вас есть немой компилятор. Путем указания на функцию должен быть встроен, она поместит копию содержания функции на каждом возникновении, было это, назван.

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

Неудобство: расширяет сгенерированный двоичный файл.

это макрос? Едва ли, потому что компилятор все еще проверяет тип параметров, и т.д.

Что относительно умных компиляторов? Они могут проигнорировать встроенную директиву, если они "чувствуют", что функция слишком сложна/также большой. И возможно они могут автоматически встроить некоторые тривиальные функции, как простые методы считывания/методы set.

2
ответ дан 27 November 2019 в 21:27
поделиться
  • там большая разница между "обычным" кодом и встроенным кодом?

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

  • Является встроенным кодом просто "форма" макросов?

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

  • , Какой компромисс должен быть сделан при желании встроить код?

    • Макрос: высоко кодируйте использование пространства, быстрое выполнение, трудно чтобы поддержать, если 'функция' долга
    • Функция: низко кодируйте использование пространства, медленнее для выполнения, легкий поддержать
    • Подставляемая функция: высоко кодируйте использование пространства, быстрое выполнение, легкое поддержать

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

-Adam

7
ответ дан 27 November 2019 в 21:27
поделиться

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

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

, Таким образом, встраивание идеально для ускорения маленьких методов, которые много раз называют, но не в слишком многих местах (100 мест прекрасен все еще, хотя - необходимо войти в довольно экстремальные примеры для получения любого значительного чрезмерного увеличения размера кода).

Редактирование: как другие указали, встраивание является только предложением к компилятору. Это может свободно проигнорировать Вас, если это думает, что Вы выполняете глупые запросы как встраивание огромного метода с 25 строками.

16
ответ дан 27 November 2019 в 21:27
поделиться

Если Вы отмечаете свой код как встроенный в f.e. C++ Вы также говорите Вашему компилятору, что код должен быть выполнен встроенный, т.е. что блок кода будет "более или менее" вставлен, где это называют (таким образом удаление продвижения, сование и вскакивание на стек). Так, да... рекомендуется, если функции подходят для такого поведения.

0
ответ дан 27 November 2019 в 21:27
поделиться

Производительность

, Как был предложен в предыдущих ответах, использовании inline ключевое слово, может сделать код быстрее путем встраивания вызовов функции, часто за счет увеличенных исполняемых файлов. Функция “Inlining calls” просто означает заменять вызовом на целевую функцию с фактическим кодом функции после заполнения аргументов соответственно.

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

Объявление функций inline явно ради увеличения производительности (почти?) всегда ненужный!

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

Одно Правило

Определения, Однако объявляя подставляемую функцию с помощью inline ключевое слово имеет другие эффекты и может на самом деле быть необходимо для удовлетворения Одно правила определения (ODR): Это правило в стандарте C++ указывает, что данный символ может быть объявлен многократно, но может только быть определен однажды. Если редактор связей (= компоновщик) встретится с несколькими идентичными определениями символа, то он генерирует ошибку.

Одно решение этой проблемы состоит в том, чтобы удостовериться, что единица компиляции не экспортирует данный символ путем предоставления ему внутренней связи путем объявления его static.

Однако часто лучше отметить функцию inline вместо этого. Это говорит компоновщику объединять все определения этой функции через единицы компиляции в одно определение с одним адресом и совместно использованными функциональными статическими переменными.

Как пример, рассмотрите следующую программу:

// header.hpp
#ifndef HEADER_HPP
#define HEADER_HPP

#include <cmath>
#include <numeric>
#include <vector>

using vec = std::vector<double>;

/*inline*/ double mean(vec const& sample) {
    return std::accumulate(begin(sample), end(sample), 0.0) / sample.size();
}

#endif // !defined(HEADER_HPP)
// test.cpp
#include "header.hpp"

#include <iostream>
#include <iomanip>

void print_mean(vec const& sample) {
    std::cout << "Sample with x̂ = " << mean(sample) << '\n';
}
// main.cpp
#include "header.hpp"

void print_mean(vec const&); // Forward declaration.

int main() {
    vec x{4, 3, 5, 4, 5, 5, 6, 3, 8, 6, 8, 3, 1, 7};
    print_mean(x);
}

Примечание, что и .cpp файлы включают заголовочный файл и таким образом функциональное определение mean. Хотя файл сохранен с, включают, принимает меры против двойного включения, это приведет к двум определениям той же функции, хотя в различных единицах компиляции.

Теперь, при попытке связать те две единицы компиляции — В, например, использование следующей команды:

⟩⟩⟩ g++ -std=c++11 -pedantic main.cpp test.cpp

Вы получите ошибку при высказывании “duplicate символа __ Z4meanRKNSt3 __ 16vectorIdNS_9allocatorIdEEEE” (который является скорректированное имя из нашей функции mean).

, Если, однако, Вы не комментируете inline модификатор перед функциональным определением, компиляциями кода и ссылками правильно.

Шаблоны функций являются особым случаем: они всегда встроены, независимо от того, были ли они объявлены тем путем. Этот doesn’t означает, что компилятор встроит вызовы им, но они won’t нарушают ODR. То же верно для функций членства, которые определяются в классе или структуре.

43
ответ дан 27 November 2019 в 21:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: