Как много прироста скорости при использовании __INLINE__?

Безопасно ли вызывать _cts.Dispose () прямо после отмены?

Чтобы узнать это, нам нужно понять, что происходит, когда мы отменяем CancellationTokenSource .

Когда вы отмените CancellationTokenSource, он продолжает вызывать любые обратные вызовы, зарегистрированные через CancellationToken, который содержит ссылку на его родительский источник с помощью метода CancellationToken.Register().

Теперь, когда вы размещаете CTS, любой зарегистрированный обратный вызов связи, который был зарегистрирован, пытается быть отменен из токена. Если он выполняется в настоящий момент, он будет ждать завершения делегирования.

Это означает, что, хотя вы разместили свою CTS, на объект все еще ссылается токен. Следовательно, он по-прежнему не подлежит сбору.

Теперь давайте посмотрим на CancellationToken.IsCancellationRequested:

public bool IsCancellationRequested 
{
    get
    {
        return m_source != null && m_source.IsCancellationRequested;
    }
}

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

Как побочная заметка, если вы (по какой-то причине) попытаетесь передать токен через его удаленный CancellationTokenSource, вы будете нажмите ObjectDisposedException.

Изменить:

Две вещи, которые я хочу добавить. Во-первых, позвольте мне сказать, что я не рекомендую использовать этот подход. Он должен работать для некоторых путей выполнения кода, но не для всех. CancellationTokenSource обычно следует удалять, только если вы используете его свойство WaitHandle. В противном случае, это хорошо, чтобы оставить его в GC для очистки. Но, поскольку это дело вкуса, вы можете выбрать то, что вам нравится. Я бы посоветовал вам распоряжаться только после того, как вы уверены, что задача выполнила запрос об аннулировании.

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

0
задан Mark 16 January 2019 в 09:09
поделиться

2 ответа

Причина, по которой я использую спецификатор функции inline (в частности, static inline), заключается не в «скорости», а в том, что часть

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

  2. inline сообщает компилятору, что она может включать реализацию функции при вызове. site, если он хочет, чтобы

  3. static inline сообщали компилятору, что он может полностью пропустить функцию, если она вообще не используется в текущем модуле перевода

    ( В частности, компилятор, который я использую чаще всего с опциями, которые я использую чаще всего, gcc -Wall, выдает предупреждение, если функция, помеченная static, не используется, но не выдает предупреждение, если функция, помеченная static inline, не используется.)

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

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


В моем коде вы видите их связанные с некоторыми структурами данных или иногда с глобальными переменными.

Типичным примером является случай, когда я хочу реализовать генератор псевдослучайных чисел Xorshift в своем собственном коде C:

#include <inttypes.h>

static uint64_t  prng_state = 1; /* Any nonzero uint64_t seed is okay */

static inline uint64_t  prng_u64(void)
{
    uint64_t  state;

    state = prng_state;
    state ^= state >> 12;
    state ^= state << 25;
    state ^= state >> 27;
    prng_state = state;

    return state * UINT64_C(2685821657736338717);
}

static uint64_t prng_state = 1; означает, что prng_state является переменной типа uint64_t, видимый только в текущем модуле компиляции и инициализированный значением 1. Функция prng_u64() возвращает беззнаковое 64-битное псевдослучайное целое число. Однако, если вы не используете prng_u64(), компилятор также не будет генерировать для него код.

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

#ifndef   GRID_H
#define   GRID_H
#include <stdlib.h>

typedef struct {
    int            rows;
    int            cols;
    unsigned char *cell;
} grid;
#define  GRID_INIT { 0, 0, NULL }

#define  GRID_OUTSIDE -1

static inline int grid_get(grid *const g, const int row, const int col)
{
    if (!g || row < 0 || col < 0 || row >= g->rows || col >= g->cols)
        return GRID_OUTSIDE;
    return g->cell[row * (size_t)(g->cols) + col];
}

static inline int grid_set(grid *const g, const int row, const int col,
                           const unsigned char value)
{
    if (!g || row < 0 || col < 0 || row >= g->rows || col >= g->cols)
        return GRID_OUTSIDE;
    return g->cell[row * (size_t)(g->cols) + col] = value;
}

static inline void grid_init(grid *g)
{
    g->rows = 0;
    g->cols = 0;
    g->cell = NULL;
}

static inline void grid_free(grid *g)
{
    free(g->cell);
    g->rows = 0;
    g->cols = 0;
    g->cell = NULL;
}

int grid_create(grid *g, const int rows, const int cols,
                const unsigned char initial_value);

int grid_load(grid *g, FILE *handle);

int grid_save(grid *g, FILE *handle);

#endif /* GRID_H */

Этот заголовочный файл определяет некоторые полезные вспомогательные функции и объявляет функции grid_create(), grid_load() и grid_save(), которые будут реализованы в отдельном файле .c. [ 1140]

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

0
ответ дан alinsoar 16 January 2019 в 09:09
поделиться

Извлечено из здесь :

Да и нет. Иногда. Может быть.

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

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

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

встроенные функции могут сделать его больше. : Это понятие раздувания кода, как описано выше. Например, если система имеет 100 встроенных функций, каждая из которых расширяется до 100 байт исполняемого кода и вызывается в 100 местах, это увеличение на 1 МБ. Это 1 МБ будет вызывать проблемы? Кто знает, но возможно, что последние 1 МБ могут вызвать «зависание» системы, что может замедлить процесс.

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

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

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

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

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

встроенные функции могут не иметь отношения к скорости : большинство систем не связаны с процессором. Большинство систем привязаны к вводу / выводу, к базе данных или к сети, что означает, что узким местом в общей производительности системы является файловая система, база данных или сеть. Если ваш «измеритель ЦП» не установлен на 100%, встроенные функции, вероятно, не сделают вашу систему быстрее. (Даже в системах с привязкой к ЦП встроенный метод поможет только при использовании внутри самого узкого места, а узкое место обычно присутствует лишь в небольшом проценте кода.)

Простых ответов нет [1125 ]: Вы должны поиграть с ним, чтобы увидеть, что лучше. Не соглашайтесь на упрощенные ответы, такие как «Никогда не используйте встроенные функции» или «Всегда используйте встроенные функции» или «Используйте встроенные функции, если и только если функция меньше N строк кода». Эти правила одного размера могут быть легко записаны, но они приведут к неоптимальным результатам.

Copyright (C) Маршалл Клайн

0
ответ дан Sourav Ghosh 16 January 2019 в 09:09
поделиться
Другие вопросы по тегам:

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