Когда используется ключевое слово register, как компилятор обрабатывает эту переменную? [Дубликат]

Уже есть много ответов, которые расскажут вам, как сделать правильную копию, но никто из них не говорит, почему ваша оригинальная «копия» не удалась.

Python не сохраняет значения в переменных; он связывает имена с объектами. Ваше исходное назначение взяло объект, на который ссылается my_list, и связал его с new_list. Независимо от того, какое имя вы используете, остается только один список, поэтому изменения, сделанные при обращении к нему как my_list, будут сохраняться при обращении к нему как new_list. Каждый из других ответов на этот вопрос дает вам различные способы создания нового объекта для привязки к new_list.

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

new_list = list(my_list)  # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]

Чтобы сделать копию списка еще на один шаг, скопируйте каждый объект, на который ссылается ваш список, и привяжите эти копии элементов в новый список.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

Это еще не глубокая копия, потому что каждый элемент списка может ссылаться на другие объекты, точно так же, как список привязан к его элементам. Чтобы рекурсивно скопировать каждый элемент в списке, а затем каждый другой объект, на который ссылаются каждый элемент, и т. Д .: выполните глубокую копию.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

Для получения дополнительной информации о копировании в окне [gg] см. документацию .

233
задан Sathish 8 August 2014 в 07:36
поделиться

17 ответов

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

Большинство современных компиляторов делают это автоматически и лучше их собирают чем мы, люди.

303
ответ дан Qix 21 August 2018 в 07:53
поделиться
  • 1
    Что ж, я экспериментировал с регистрацией, чтобы настроить мои ACM-представления, и иногда это помогло. Но вы действительно должны быть внимательны, потому что плохие выборы ухудшают производительность. – ypnos 23 February 2009 в 17:20
  • 2
    Хорошая причина not использовать «register»: вы не можете взять адрес переменной, объявленной «register», – Adam Rosenfield 23 February 2009 в 17:21
  • 3
    Обратите внимание, что некоторые / многие компиляторы полностью игнорируют ключевое слово register (что совершенно законно). – Euro Micelli 23 February 2009 в 18:49
  • 4
    ypnos: На самом деле скорость решения задач ACM ICPC во многом зависит от выбора алгоритма, чем от таких микрооптимизаций. 5-секундного ограничения времени обычно достаточно для правильного решения, особенно при использовании C вместо Java. – Joey 15 October 2009 в 14:12
  • 5
    @Euro: Вы, вероятно, знаете это, но только для того, чтобы быть явным, компилятор должен предотвратить выбор адреса переменной register; это only обязательный эффект ключевого слова register. Даже этого достаточно для улучшения оптимизации, потому что становится тривиальным сказать, что переменная может быть изменена только в этой функции. – Dale Hagglund 9 February 2010 в 19:26

Он сообщает компилятору попытаться использовать регистр ЦП, а не ОЗУ, чтобы сохранить переменную. Регистры находятся в ЦП и намного быстрее доступны, чем оперативная память. Но это всего лишь предложение компилятору, и это может не произойти.

33
ответ дан Andrew Barnett 21 August 2018 в 07:53
поделиться
  • 1
    Стоит добавить для людей, использующих C ++, C ++ позволяет взять адрес переменной регистра – Will 30 March 2012 в 17:07
  • 2
    @Will: ... но компилятор, скорее всего, проигнорирует ключевое слово в результате. См. Мой ответ. – bwDraco 17 December 2012 в 01:03
  • 3
    Да, похоже, что «register» - это плацебо в C ++, он просто позволяет скомпилировать C-код как C ++. И не было бы смысла запрещать & amp; var, позволяя передавать его по ссылке или const-reference, и без перекрестной ссылки вы серьезно нарушили C ++. – greggo 21 February 2015 в 23:23

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

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

2
ответ дан billjamesdev 21 August 2018 в 07:53
поделиться

Я знаю, что этот вопрос касается C, но тот же вопрос для C ++ был закрыт как точный дубликат этого вопроса. Поэтому этот ответ может не применяться для C.


Последний проект стандарта C ++ 11 N3485 говорит об этом в 7.1.1 / 3:

Спецификатор register - это намек на реализацию, которую объявленная переменная будет сильно использоваться. [ note: Подсказка может быть проигнорирована, и в большинстве реализаций она будет игнорироваться, если будет принят адрес переменной. Это использование устарело ... -end note ]

В C ++ (но не в C) в стандарте не указано, что вы не можете принять адрес переменной, объявленной register; однако, поскольку переменная, хранящаяся в регистре CPU на протяжении всего жизненного цикла, не имеет связанного с ней местоположения памяти, попытка получить его адрес будет недействительной, а компилятор будет игнорировать ключевое слово register, чтобы разрешить прием адреса.

18
ответ дан bwDraco 21 August 2018 в 07:53
поделиться

В поддерживаемых компиляторах C он пытается оптимизировать код, так что значение переменной сохраняется в реальном регистре процессора.

2
ответ дан Dana Holt 21 August 2018 в 07:53
поделиться

Я тестировал ключевое слово register под QNX 6.5.0, используя следующий код:

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/neutrino.h>
#include <sys/syspage.h>

int main(int argc, char *argv[]) {
    uint64_t cps, cycle1, cycle2, ncycles;
    double sec;
    register int a=0, b = 1, c = 3, i;

    cycle1 = ClockCycles();

    for(i = 0; i < 100000000; i++)
        a = ((a + b + c) * c) / 2;

    cycle2 = ClockCycles();
    ncycles = cycle2 - cycle1;
    printf("%lld cycles elapsed\n", ncycles);

    cps = SYSPAGE_ENTRY(qtime) -> cycles_per_sec;
    printf("This system has %lld cycles per second\n", cps);
    sec = (double)ncycles/cps;
    printf("The cycles in seconds is %f\n", sec);

    return EXIT_SUCCESS;
}

Я получил следующие результаты:

-> 807679611 истекших циклов

-> Эта система имеет 3300830000 циклов в секунду

-> Циклы в секундах ~ 0.244600

И теперь без регистра int:

int a=0, b = 1, c = 3, i;

Я получил:

-> 1421694077 пройденных циклов

-> Эта система имеет 3300830000 циклов в секунду

-> Циклы в секундах ~ 0.430700

1
ответ дан Daniel B 21 August 2018 в 07:53
поделиться

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

Ключевое слово было использовано long, long back. Когда было только так мало регистров, которые могли подсчитать их всех с помощью указательного пальца.

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

4
ответ дан dirkgently 21 August 2018 в 07:53
поделиться
  • 1
    Некоторые из более старых аппаратных средств имели больше регистров, чем современные процессоры Intel. Регистрирующие счета не имеют никакого отношения к возрасту и всему, что связано с архитектурой процессора. – JUST MY correct OPINION 30 September 2010 в 09:14
  • 2
    @JUSTMYcorrectOPINION Действительно, X86 имеет в основном шесть, оставляя максимум 1 или 2 для посвящения в «register». На самом деле, поскольку так много кода было написано или перенесено на машину с плохой регистрацией, я подозреваю, что это сильно повлияло на то, что ключевое слово «register» становится плацебо - нет смысла указывать регистры, когда их нет. Здесь мы находимся 4+ года спустя, и к счастью x86_64 увеличил его до 14, и теперь ARM - большая вещь. – greggo 21 February 2015 в 23:47

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

Еще одна вещь: если вы объявляете переменную как регистр, вы не можете получить ее адрес, поскольку он не хранится в памяти. он получает свое распределение в регистре CPU.

0
ответ дан flying-high 21 August 2018 в 07:53
поделиться

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

Фактически это является , четко определенным C. Цитирование N1570 draft раздела 6.7.1, пункт 6 (другие версии имеют одинаковую формулировку):

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

Унарный оператор & не может применяться к объекту, определенному с помощью register, а register может не быть используется в внешней декларации.

Существует несколько других (довольно неясных) правил, специфичных для register -qualified objects:

  • Определение объекта массива с помощью register имеет неопределенное поведение. Исправление: законно задавать объект массива с помощью register, но вы не можете ничего использовать с таким объектом (индексирование в массив требует ввода адреса его начального элемента).
  • _Alignas (новый в C11) не может быть применен к такому объекту.
  • Если имя параметра, переданное макросу va_start, [register -qualified, поведение не определено.

Могут быть несколько других; Загрузите черновик стандарта и выполните поиск «register», если вам интересно.

Как следует из названия, значение оригинала для register должно было требовать от объекта храниться в регистре CPU. Но с улучшением оптимизации компиляторов это стало менее полезным. Современные версии стандарта C не относятся к регистрам CPU, потому что они больше не нуждаются (должны) предполагать, что есть такая вещь (существуют архитектуры, которые не используют регистры). Общая мудрость заключается в том, что применение register к объявлению объекта с большей вероятностью ухудшает сгенерированный код, потому что это мешает распределению собственного регистра компилятора. Там может быть несколько случаев, когда это полезно (скажем, если вы действительно знаете, как часто переменная будет доступна, и ваши знания лучше, чем то, что может вычислить современный оптимизирующий компилятор).

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

11
ответ дан Keith Thompson 21 August 2018 в 07:53
поделиться
  • 1
    так что поведение этой программы действительно неопределено в соответствии со стандартом C? Является ли он хорошо определенным в C ++? Я думаю, что он хорошо определен в C ++. – Destructor 19 July 2016 в 16:16
  • 2
    @Destructor: Почему это не определено? Нет объекта register -qualified array, если это то, о чем вы думаете. – Keith Thompson 19 July 2016 в 18:35
  • 3
    Ой, я забыл написать ключевое слово register в объявлении массива в main (). Является ли он хорошо определенным в C ++? – Destructor 20 July 2016 в 06:21
  • 4
    Я ошибался в определении объектов массива register; см. обновленную первую маркерную точку в моем ответе. Юридически определить такой объект, но вы ничего не можете с ним поделать. Если вы добавите register в определение s в вашего примера , программа является незаконной (нарушение ограничений) в C. C ++ не устанавливает те же ограничения на register, поэтому программа была бы действительной C ++ (но использование register было бы бессмысленным). – Keith Thompson 20 July 2016 в 15:43
  • 5
    @KeithThompson: ключевое слово register могло бы служить полезной целью, если было бы право принимать адрес такой переменной, но только в тех случаях, когда семантика не была бы затронута путем копирования переменной во временное место, когда ее адрес был взят, и перезагрузка от временной в следующей точке последовательности. Это позволило бы компиляторам предположить, что переменная может быть безопасно сохранена в регистре во всех обращениях указателей при условии, что она покраснела в любом месте, где его адрес занят. – supercat 20 July 2016 в 21:59

Компилятор Microsoft Visual C ++ игнорирует ключевое слово register, когда включена глобальная оптимизация распределения регистров (флаг компилятора / Oe).

См. регистр ключевого слова в MSDN.

1
ответ дан M. Dudley 21 August 2018 в 07:53
поделиться

Storytime!

C, как язык, является абстракцией компьютера. Это позволяет вам делать то, что делает компьютер, манипулировать памятью, делать математику, печатать вещи и т. Д.

Но C - это только абстракция. И, в конечном счете, то, что он извлекает из вас , является языком ассемблера. Assembly - это язык, который читает процессор, и если вы его используете, вы делаете что-то с точки зрения процессора. Что делает процессор? В принципе, он читает из памяти, выполняет математику и записывает в память. CPU не просто выполняет математику по номерам в памяти. Во-первых, вам нужно переместить число из памяти в память внутри процессора, называемого регистром . Как только вы закончите делать все, что вам нужно сделать, чтобы это число, вы можете переместить его обратно в обычную системную память. Зачем использовать системную память? Регистры ограничены по количеству. В современных процессорах вы получаете около ста байтов, а более старые популярные процессоры были еще более фантастически ограничены (у 6502 было 3 8-битных регистра для вашего бесплатного использования). Итак, ваша средняя математическая операция выглядит так:

load first number from memory
load second number from memory
add the two
store answer into memory

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

Когда вы объявляете переменную register, вы сообщаете компилятору «Yo, я намерен использовать эту переменную много и / или быть коротким. Если бы я был вами, Я постараюсь сохранить его в реестре. Когда в стандарте C говорится, что компиляторам вообще не нужно что-то делать, это потому, что стандарт C не знает, на каком компьютере вы компилируете, и это может быть как 6502 выше, где все 3 регистра необходимы только для работы , и нет запасного регистра, чтобы сохранить свой номер. Однако, когда говорится, что вы не можете принять адрес, это потому, что регистры не имеют адресов. Это руки процессора. Поскольку компилятор не должен указывать вам адрес, и поскольку он вообще не может иметь адрес, в настоящее время для компилятора открываются несколько оптимизаций. Он мог, скажем, всегда держать номер в регистре. Он не должен беспокоиться о том, где он хранится в памяти компьютера (за исключением необходимости вернуть его обратно). Это может даже записать его в другую переменную, передать его другому процессору, дать ему изменение местоположения и т. Д.

tl; dr: Краткоживущие переменные, которые выполняют много математики. Не объявляйте слишком много сразу.

6
ответ дан Orion 21 August 2018 в 07:53
поделиться

Это не актуально в течение как минимум 15 лет, так как оптимизаторы принимают более правильные решения, чем вы можете. Даже когда это было актуально, это имело гораздо больший смысл в архитектуре процессора с большим количеством регистров, таких как SPARC или M68000, чем у Intel с небольшим количеством регистров, большинство из которых зарезервировано компилятором в своих целях.

17
ответ дан Paul Tomblin 21 August 2018 в 07:53
поделиться

Просто небольшое демо (без какой-либо реальной цели) для сравнения: при удалении ключевых слов register перед каждой переменной этот фрагмент кода занимает 3,41 секунды на моем i7 (GCC), с register тот же код завершается через 0,7 секунды.

#include <stdio.h>

int main(int argc, char** argv) {

     register int numIterations = 20000;    

     register int i=0;
     unsigned long val=0;

    for (i; i<numIterations+1; i++)
    {
        register int j=0;
        for (j;j<i;j++) 
        {
            val=j+i;
        }
    }
    printf("%d", val);
    return 0;
}
4
ответ дан Robert 21 August 2018 в 07:53
поделиться
  • 1
    С gcc 4.8.4 и -O3 я не понимаю. Без -O3 и 40000 итераций я получаю возможно 50 мс меньше на общее время 1,5 с, но я не запускал его достаточно времени, чтобы узнать, было ли это даже статистически значимым. – zstewart 22 February 2017 в 18:22
  • 2
    Нет никакой разницы с CLANG 5.0, платформа AMD64. (Я проверил выход ASM.) – ern0 10 October 2017 в 10:10

На самом деле, регистр сообщает компилятору, что переменная не псевдонимы ничем другим в программе (даже не char).

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

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

12
ответ дан Rupert 21 August 2018 в 07:53
поделиться
  • 1
    & quot; сообщает компилятору .. & quot; нет, нет. Все автоматические переменные имеют это свойство, если вы не используете его адрес и , используя его способами, которые превышают определенные анализируемые области использования. Таким образом, компилятор знает это из кода, независимо от того, используете ли вы ключевое слово register. Так получилось, что ключевое слово «register» делает незаконным писать такую ​​конструкцию, но если вы не используете ключевое слово, а dont берет адрес таким образом, то компилятор все еще знает это безопасно. Такая информация имеет решающее значение для оптимизации. – greggo 21 February 2015 в 23:39
  • 2
    @greggo: Слишком плохо register запрещает брать адрес вообще, так как в противном случае было бы полезно сообщить компиляторам о случаях, когда компилятор сможет применять оптимизацию регистров , несмотря на передаваемый адрес переменной к внешней функции (переменная должна быть сброшена в память для этого конкретного вызова , но как только функция вернется, компилятор снова сможет рассматривать ее как переменную, адрес которой никогда не был выполнен). – supercat 26 May 2016 в 23:11
  • 3
    @supercat Я думаю, все равно будет очень сложный разговор с компилятором. Если вы хотите сообщить компилятору, вы можете сделать это, скопировав первую переменную на вторую, которая не имеет «& amp;»; на нем, а затем никогда больше не использовать первый. – greggo 30 May 2016 в 19:38
  • 4
    @greggo: если bar является переменной register, компилятор может на отдыхе заменить foo(&bar); на int temp=bar; foo(&temp); bar=temp;, но с адресом bar будет запрещено в большинство других контекстов не будет выглядеть слишком сложным правилом. Если переменную в противном случае можно было бы сохранить в регистре, замена заменила бы код меньше. Если в любом случае переменная должна быть сохранена в ОЗУ, замещение сделает код более крупным. Оставив вопрос о том, следует ли сделать подстановку до компилятора, в обоих случаях будет улучшен код. – supercat 31 May 2016 в 14:28
  • 5
    @greggo: разрешая квалификацию register для глобальных переменных, независимо от того, разрешает ли компилятор разрешить адрес, позволил бы сделать некоторые хорошие оптимизации в случаях, когда встроенная функция, использующая глобальную переменную, вызывается многократно в цикле. Я не могу придумать другого способа, чтобы эта переменная хранилась в регистре между итерациями цикла - не так ли? – supercat 31 May 2016 в 14:30

Ключевое слово Register указывает компилятору, чтобы сохранить конкретную переменную в регистре CPU, чтобы она могла быть быстро доступной. С точки зрения программиста ключевое слово register используется для переменных, которые сильно используются в программе, поэтому компилятор может ускорить код. Хотя это зависит от компилятора, следует ли сохранять переменную в регистры процессора или в основной памяти.

1
ответ дан Sanjeev Kumar 21 August 2018 в 07:53
поделиться

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

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

61
ответ дан twk 21 August 2018 в 07:53
поделиться
  • 1
    На самом деле есть причина. Сам факт, что вы не можете взять адрес переменной, дает некоторые возможности оптимизации: компилятор может доказать, что переменная не будет сглажена. – Alexandre C. 9 February 2012 в 22:14
  • 2
    @Alexandre C. Достаточно честный. Я не думал об этом. – qrdl 10 February 2012 в 11:30
  • 3
    Компиляторы, как известно, ужасны, доказывая, что псевдонимы не встречаются в нетривиальных случаях, поэтому register полезен для этого, даже если компилятор не помещает его в регистр. – Miles Rout 7 June 2014 в 06:33
  • 4
    @AlexandreC, Miles, компиляторы отлично справляются с проверкой: & amp; берется за переменную где угодно. Поэтому, независимо от других трудностей с обнаружением сглаживания, пересчитывая то, что ничего не покупает. Когда K + R сначала создавал C, было действительно полезно знать заранее , что & amp; не будет использоваться, поскольку этот компилятор фактически принял решение о распределении регистра при просмотре декларации, прежде чем смотреть на следующий код. Вот почему запрет существует. Ключевое слово «register» в настоящее время устарело. – greggo 21 February 2015 в 23:31
  • 5
    По этой логике const также бесполезно, поскольку оно ничего не выигрывает, вы теряете способность изменять переменную. register можно использовать, чтобы убедиться, что никто не примет адрес переменной в будущем, не подумав. У меня никогда не было причин использовать register. – Tor Klingberg 29 October 2015 в 14:11

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

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

Так много людей ошибочно рекомендуют не использовать ключевое слово register.

Давайте посмотрим, почему!

Ключевое слово register имеет связанную сторону Эффект: вы не можете ссылаться (получить адрес) на переменную типа регистра.

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

Однако простой факт, что вы не можете взять адрес переменной регистра, позволяет компилятор (и i ts optimizer), чтобы знать, что значение этой переменной не может быть изменено косвенно через указатель.

Когда в определенной точке потока команд переменная регистра имеет свое значение, назначенное в регистре процессора, и регистр не использовался, так как для получения значения другой переменной компилятор знает, что нет необходимости перегружать значение переменной в этом регистре. Это позволяет избежать дорогостоящего бесполезного доступа к памяти.

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

c_register_side_effect_performance_boost

2
ответ дан user 21 August 2018 в 07:53
поделиться
Другие вопросы по тегам:

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