Недавно я беседовал на Amazon. Во время сеанса кодирования, интервьюер спрашивает, почему я объявил переменную в метод. Я объяснил свой процесс и он бросил мне вызов, чтобы решить ту же проблему с меньшим количеством переменных. Например (это было'т из интервью), я начал с метод то улучшенная ее метод B, убрав инт ы
. Он был доволен и сказал, что это позволит сократить использование памяти Этим методом.
Я понимаю логику, стоящую за этим, но мой вопрос:
Когда целесообразно использовать метод A и метод B, и наоборот?
Вы можете видеть этот метод будет иметь более высокое использование памяти, с инт ыобъявлена, но это только для выполнения одного расчета, т. е.
А + Б. С другой стороны, *метод B* имеет низкое использование памяти, но должен выполнить два расчета, т. е.
А + Б` дважды. Когда я использую одну технику над другими? Или, является одним из методов всегда предпочтительнее другого? Что нужно учитывать при оценке двух методов?
в
private bool IsSumInRange(int a, int b)
{
int s = a + b;
if (s > 1000 || s < -1000) return false;
else return true;
}
private bool IsSumInRange(int a, int b)
{
if (a + b > 1000 || a + b < -1000) return false;
else return true;
}
Вместо того чтобы размышлять о том, что может или не может случиться, пусть's просто посмотрим, Да? Я'll должны использовать C++, так как я Дон'т иметь компилятор C# удобный (хотя см. пример на языке C# от VisualMelon), но я'уверен, что те же принципы применяются независимо от.
Мы'МР включают две альтернативы, с которой вы столкнулись в интервью. Мы'МР также есть версия, которая использует АБС
, как полагают некоторые ответы.
в
#include <cstdlib>
bool IsSumInRangeWithVar(int a, int b)
{
int s = a + b;
if (s > 1000 || s < -1000) return false;
else return true;
}
bool IsSumInRangeWithoutVar(int a, int b)
{
if (a + b > 1000 || a + b < -1000) return false;
else return true;
}
bool IsSumInRangeSuperOptimized(int a, int b) {
return (abs(a + b) < 1000);
}
Теперь его скомпилировать без оптимизации: г++ -С-О тест.о test.cpp
Теперь мы можем видеть именно то, что это порождает: `objdump -d проверит.о'
0000000000000000 <_Z19IsSumInRangeWithVarii>:
0: 55 push %rbp # begin a call frame
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d ec mov %edi,-0x14(%rbp) # save first argument (a) on stack
7: 89 75 e8 mov %esi,-0x18(%rbp) # save b on stack
a: 8b 55 ec mov -0x14(%rbp),%edx # load a and b into edx
d: 8b 45 e8 mov -0x18(%rbp),%eax # load b into eax
10: 01 d0 add %edx,%eax # add a and b
12: 89 45 fc mov %eax,-0x4(%rbp) # save result as s on stack
15: 81 7d fc e8 03 00 00 cmpl $0x3e8,-0x4(%rbp) # compare s to 1000
1c: 7f 09 jg 27 # jump to 27 if it's greater
1e: 81 7d fc 18 fc ff ff cmpl $0xfffffc18,-0x4(%rbp) # compare s to -1000
25: 7d 07 jge 2e # jump to 2e if it's greater or equal
27: b8 00 00 00 00 mov $0x0,%eax # put 0 (false) in eax, which will be the return value
2c: eb 05 jmp 33 <_Z19IsSumInRangeWithVarii+0x33>
2e: b8 01 00 00 00 mov $0x1,%eax # put 1 (true) in eax
33: 5d pop %rbp
34: c3 retq
0000000000000035 <_Z22IsSumInRangeWithoutVarii>:
35: 55 push %rbp
36: 48 89 e5 mov %rsp,%rbp
39: 89 7d fc mov %edi,-0x4(%rbp)
3c: 89 75 f8 mov %esi,-0x8(%rbp)
3f: 8b 55 fc mov -0x4(%rbp),%edx
42: 8b 45 f8 mov -0x8(%rbp),%eax # same as before
45: 01 d0 add %edx,%eax
# note: unlike other implementation, result is not saved
47: 3d e8 03 00 00 cmp $0x3e8,%eax # compare to 1000
4c: 7f 0f jg 5d <_Z22IsSumInRangeWithoutVarii+0x28>
4e: 8b 55 fc mov -0x4(%rbp),%edx # since s wasn't saved, load a and b from the stack again
51: 8b 45 f8 mov -0x8(%rbp),%eax
54: 01 d0 add %edx,%eax
56: 3d 18 fc ff ff cmp $0xfffffc18,%eax # compare to -1000
5b: 7d 07 jge 64 <_Z22IsSumInRangeWithoutVarii+0x2f>
5d: b8 00 00 00 00 mov $0x0,%eax
62: eb 05 jmp 69 <_Z22IsSumInRangeWithoutVarii+0x34>
64: b8 01 00 00 00 mov $0x1,%eax
69: 5d pop %rbp
6a: c3 retq
000000000000006b <_Z26IsSumInRangeSuperOptimizedii>:
6b: 55 push %rbp
6c: 48 89 e5 mov %rsp,%rbp
6f: 89 7d fc mov %edi,-0x4(%rbp)
72: 89 75 f8 mov %esi,-0x8(%rbp)
75: 8b 55 fc mov -0x4(%rbp),%edx
78: 8b 45 f8 mov -0x8(%rbp),%eax
7b: 01 d0 add %edx,%eax
7d: 3d 18 fc ff ff cmp $0xfffffc18,%eax
82: 7c 16 jl 9a <_Z26IsSumInRangeSuperOptimizedii+0x2f>
84: 8b 55 fc mov -0x4(%rbp),%edx
87: 8b 45 f8 mov -0x8(%rbp),%eax
8a: 01 d0 add %edx,%eax
8c: 3d e8 03 00 00 cmp $0x3e8,%eax
91: 7f 07 jg 9a <_Z26IsSumInRangeSuperOptimizedii+0x2f>
93: b8 01 00 00 00 mov $0x1,%eax
98: eb 05 jmp 9f <_Z26IsSumInRangeSuperOptimizedii+0x34>
9a: b8 00 00 00 00 mov $0x0,%eax
9f: 5d pop %rbp
a0: c3 retq
Мы можем видеть из стека адресов (например, - признаки 0x4 " в "мову %Эди,-признаки 0x4(%РБП)
против -0x14" в " мову %Эди,-0x14(%РБП)
), что IsSumInRangeWithVar()
используется 16 лишних байт на стеке.
Потому что IsSumInRangeWithoutVar()выделяет нет места в стеке для хранения промежуточных значений
Ыон должен пересчитать ее, в результате чего это станет 2 инструкция больше. Забавно, IsSumInRangeSuperOptimized()
выглядит как IsSumInRangeWithoutVar (), за исключением сравнивает первых -1000 и 1000 второй. Теперь давайте'ы составить только самое основное оптимизация:
г++ -О1 -с-о тест.о test.cpp`. Результат:
0000000000000000 <_Z19IsSumInRangeWithVarii>:
0: 8d 84 37 e8 03 00 00 lea 0x3e8(%rdi,%rsi,1),%eax
7: 3d d0 07 00 00 cmp $0x7d0,%eax
c: 0f 96 c0 setbe %al
f: c3 retq
0000000000000010 <_Z22IsSumInRangeWithoutVarii>:
10: 8d 84 37 e8 03 00 00 lea 0x3e8(%rdi,%rsi,1),%eax
17: 3d d0 07 00 00 cmp $0x7d0,%eax
1c: 0f 96 c0 setbe %al
1f: c3 retq
0000000000000020 <_Z26IsSumInRangeSuperOptimizedii>:
20: 8d 84 37 e8 03 00 00 lea 0x3e8(%rdi,%rsi,1),%eax
27: 3d d0 07 00 00 cmp $0x7d0,%eax
2c: 0f 96 c0 setbe %al
2f: c3 retq
эквивалентно
а + б + 1000 <= 2000учитывая
setbeделает сравнение без знака, поэтому отрицательное число становится очень большим положительным числом. В
Леа` инструкция на самом деле могут выполнять все эти дополнения в одну инструкцию, и устранить все условные переходы.
Чтобы ответить на ваш вопрос, almost always, что нужно оптимизировать по памяти или скорости, но readability. Чтение кода гораздо сложнее, чем ее написание и чтение кода, что'ы были подогнаны, чтобы "оптимизировать" это гораздо труднее, чем чтение кода, что'было написано для ясности. Чаще чем не, эти-то "оптимизации" имеют ничтожно мала, или как в данном случае exactly zero фактическое влияние на производительность. последующий вопрос, какие изменения, когда этот код на интерпретируемом языке вместо компиляции? Тогда оптимизация дело или не иметь тот же результат? Позвольте'ы измерения! Я'вэ переписал примеры на Python:
def IsSumInRangeWithVar(a, b):
s = a + b
if s > 1000 or s < -1000:
return False
else:
return True
def IsSumInRangeWithoutVar(a, b):
if a + b > 1000 or a + b < -1000:
return False
else:
return True
def IsSumInRangeSuperOptimized(a, b):
return abs(a + b) <= 1000
from dis import dis
print('IsSumInRangeWithVar')
dis(IsSumInRangeWithVar)
print('\nIsSumInRangeWithoutVar')
dis(IsSumInRangeWithoutVar)
print('\nIsSumInRangeSuperOptimized')
dis(IsSumInRangeSuperOptimized)
print('\nBenchmarking')
import timeit
print('IsSumInRangeWithVar: %fs' % (min(timeit.repeat(lambda: IsSumInRangeWithVar(42, 42), repeat=50, number=100000)),))
print('IsSumInRangeWithoutVar: %fs' % (min(timeit.repeat(lambda: IsSumInRangeWithoutVar(42, 42), repeat=50, number=100000)),))
print('IsSumInRangeSuperOptimized: %fs' % (min(timeit.repeat(lambda: IsSumInRangeSuperOptimized(42, 42), repeat=50, number=100000)),))
Работать с Python 3.5.2, это производит вывод:
IsSumInRangeWithVar
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 BINARY_ADD
7 STORE_FAST 2 (s)
3 10 LOAD_FAST 2 (s)
13 LOAD_CONST 1 (1000)
16 COMPARE_OP 4 (>)
19 POP_JUMP_IF_TRUE 34
22 LOAD_FAST 2 (s)
25 LOAD_CONST 4 (-1000)
28 COMPARE_OP 0 (<)
31 POP_JUMP_IF_FALSE 38
4 >> 34 LOAD_CONST 2 (False)
37 RETURN_VALUE
6 >> 38 LOAD_CONST 3 (True)
41 RETURN_VALUE
42 LOAD_CONST 0 (None)
45 RETURN_VALUE
IsSumInRangeWithoutVar
9 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 BINARY_ADD
7 LOAD_CONST 1 (1000)
10 COMPARE_OP 4 (>)
13 POP_JUMP_IF_TRUE 32
16 LOAD_FAST 0 (a)
19 LOAD_FAST 1 (b)
22 BINARY_ADD
23 LOAD_CONST 4 (-1000)
26 COMPARE_OP 0 (<)
29 POP_JUMP_IF_FALSE 36
10 >> 32 LOAD_CONST 2 (False)
35 RETURN_VALUE
12 >> 36 LOAD_CONST 3 (True)
39 RETURN_VALUE
40 LOAD_CONST 0 (None)
43 RETURN_VALUE
IsSumInRangeSuperOptimized
15 0 LOAD_GLOBAL 0 (abs)
3 LOAD_FAST 0 (a)
6 LOAD_FAST 1 (b)
9 BINARY_ADD
10 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
13 LOAD_CONST 1 (1000)
16 COMPARE_OP 1 (<=)
19 RETURN_VALUE
Benchmarking
IsSumInRangeWithVar: 0.019361s
IsSumInRangeWithoutVar: 0.020917s
IsSumInRangeSuperOptimized: 0.020171s
Разборки в Python это'т страшно интересно, поскольку байт-код "и компилятор" и не'т сделать многое на пути оптимизации.
Выполнение трех функций практически идентичен. Мы могли быть уговорены для того чтобы пойти с IsSumInRangeWithVar () из-за его'с предельной набирать скорость. Хотя я'будете добавлять, как я пробую разные параметры для
раз все, иногда
IsSumInRangeSuperOptimized()` вышла быстро, так что я подозреваю, что это могут быть внешние факторы, отвечающие за разницу, а не какие-либо значительные преимущества от реализации.
Если это действительно критическое для производительности кода, интерпретируемого языка-это очень плохой выбор. Под управлением той же программы с PyPy я вам:
IsSumInRangeWithVar: 0.000180s
IsSumInRangeWithoutVar: 0.001175s
IsSumInRangeSuperOptimized: 0.001306s
Просто используя PyPy, который использует JIT-компиляцию, чтобы устранить большое переводчику накладные, дало повышение производительности на 1 или 2 порядка. Я был совершенно потрясен, чтобы увидеть IsSumInRangeWithVar () - это на порядок быстрее, чем другие. Поэтому я изменила порядок приоритетов и снова побежал:
IsSumInRangeSuperOptimized: 0.000191s
IsSumInRangeWithoutVar: 0.001174s
IsSumInRangeWithVar: 0.001265s
Так что, похоже, это's не действительно что-нибудь о реализации, которая делает это быстро, а порядок, в котором я делаю бенчмаркинга! Я'd любовь, чтобы копать еще глубже, потому что, честно говоря, я не'т знаю, почему это происходит. Но я считаю, что точка была сделана: микро-оптимизаций, как объявлять ли промежуточное значение переменной или не редко актуален. С интерпретируемый язык или оптимизированный компилятор, первой целью по-прежнему писать ясный код. Если дальнейшая оптимизация может потребоваться, benchmark. Помните, что лучшей оптимизации не мелкие детали, но чем больше алгоритмическую картину: он будет на порядок быстрее, для повторной оценки одной и той же функции, чем с CPython, поскольку он использует более быстрые алгоритмы (JIT-компилятор против интерпретации), чтобы оценить программу. И там'ы закодированный алгоритм, чтобы рассмотреть, а также: обыск с помощью B-дерева будет быстрее, чем связанный список. После того как вы're, используя правильные инструменты и алгоритмы для работы, будьте готовы к погружению deep в подробности системы. Результат может быть очень неожиданным, даже для опытных разработчиков, и именно поэтому вы должны быть эталоном для количественной оценки изменений.
Чтобы ответить на поставленный вопрос:
когда для оптимизации памяти скорость работы метода?
Есть две вещи, которые вы должны установить:
Для того, чтобы ответить на первый вопрос, вы должны знать, что требования к производительности для вашего приложения. Если нет требований, то нет смысла оптимизировать так или иначе. Требования по производительности могут помочь вам добраться до места "и достаточно хорошо и".
Метод, который вы предоставили по себе не'т вызвать какие-либо проблемы с производительностью на собственного, но, возможно, в цикле и обрабатывать большие объемы данных, вы должны начать думать немного иначе о том, как вы приближаетесь проблема.
Начните глядя на поведение вашего приложения с помощью системного монитора. Отслеживать использование процессора, диска, сети и памяти, пока он's работает. Один или несколько элементов будет превышен, а все остальное-в меру использовать ... если вы попали в идеальный баланс, но этого почти никогда не происходит).
Когда вам нужно смотреть глубже, как правило, вы будете использовать profiler. Есть memory profilers и process profilers, и они измеряют разные вещи. Акт профилирования имеет значительное влияние на производительность, но вы не инструментирование кода, чтобы выяснить, что'ы не так.
Позвольте'говорят, что вы видите вашего процессора и диска достигла максимума. Вы бы сначала проверять на "горячих точках" или код, который либо называется чаще, чем в остальных и занимает значительно больше процент обработки.
Если вы можете'т найти любые горячие точки, тогда вам будет начать поиски в памяти. Возможно, вы создаете больше объектов, чем необходимо, и ваш сборка мусора работает сверхурочно.
Критически мыслить. Следующий список изменений в порядок сколько возврата инвестиций вы'll получить:
В таких ситуациях нужно применять научный метод. Придумать гипотезу, внести изменения, и проверить его. Если вы встречаете ваших целей производительности, вы'повторно сделано. Если нет, переходим к следующему в списке.
Отвечая на вопрос жирным шрифтом:
когда целесообразно использовать метод A и метод B, и наоборот?
Честно, это последний шаг в попытке решить проблему производительности или проблемы с памятью. Влияние метод A и метод B будет очень разным в зависимости от языка и платформа (в некоторых случаях).
Просто о каких-либо compiled language с мало-мальски приличный оптимизатор будет генерировать подобный код с любой из этих структур. Однако эти предположения не'т должен оставаться верным в фирменной и игрушки языков, что Дон'Т есть оптимизатор.
Точно, у которых будет лучшее воздействие зависит от того, сумма
- это переменная стека или кучи переменных. Это выбор языка реализации. В C, C++ и Java, например, количество примитивов, как инт
являются переменными стека по умолчанию. Ваш код имеет большее влияние памяти путем присвоения переменной стека, чем вы бы с полностью встроен код.
Других оптимизаций, которые вы можете найти в библиотеках с (особенно старые), где вы можете решить между копированием 2 мерный массив первый или во-первых, это зависит от платформы оптимизации. Это требует некоторых знаний о том, как чипсет вы ориентируетесь лучше оптимизирует доступ к памяти. Есть тонкие различия между архитектурами.
Суть в том, что оптимизация-это сочетание искусства и науки. Это требует критического мышления, а также степень гибкости в том, как подходить к проблеме. Искать большие вещи, прежде чем упрекать по мелочам.
и"это уменьшило бы память" - ЭМ, нет. Даже если это окажется правдой (что, для любого приличного компилятора нет), то разница скорее всего будет незначительным для любой реальной ситуации в мире.
Однако, я бы рекомендовал использовать метод* (метод с небольшим изменением):
private bool IsSumInRange(int a, int b)
{
int sum = a + b;
if (sum > 1000 || sum < -1000) return false;
else return true;
// (yes, the former statement could be cleaned up to
// return abs(sum)<=1000;
// but let's ignore this for a moment)
}
но за два совершенно разных причин:
давая переменная с
объяснение имя, код становится понятнее
он избегает иметь ту же логику суммирования дважды в код, так что код становится более сухой, а значит ошибок меньше подвержены изменениям.
Вы можете сделать лучше, чем эти с
return (abs(a + b) > 1000);
Большинство процессоров (и, следовательно, компиляторы) может сделать АБС() в одной операции. Вы не только меньшую сумму, но и меньшее количество сравнений, которые, как правило, более в вычислительном отношении дорого. Он также удаляет ветвления, что гораздо хуже для большинства процессоров, так как она не конвейеризация возможным.
Интервьюер, как и другие ответы уже сказал, Жизнь растений и не проводит техническое собеседование.
Что сказал, на его вопрос действителен. И ответа на вопрос: когда вы оптимизируете и как, это когда вы'вэ доказал это's необходимый, и вы'вэ профилированного это, чтобы доказать, какие именно части нужно. Кнут лихо заявил, что преждевременная оптимизация-корень всех зол, потому что он's слишком легко, чтобы попытаться золочение неважных разделов, или внести изменения (например, ваш интервьюер'ов), которые не имеют никакого эффекта, пока хватает мест, которые действительно нуждаются в этом. Пока вы've получили веские доказательства его's действительно необходимо, чистота кода-это более важная цель.
Редактировать FabioTurati правильно указывает, что это противоречит логике смысла оригинала, (моя ошибка!), и что это иллюстрирует дальнейшее влияние от кнута'ы цитату, где мы рискуем сломать код, пока мы'вновь пытается оптимизировать его.
когда целесообразно использовать метод A и метод B, и наоборот?
Оборудование дешево; программисты стоят дорого. Так что стоимость времени, что вы зря на этот вопрос, наверное, гораздо хуже, чем любой ответ.
Несмотря на это, большинство современных компиляторов бы найти способ оптимизировать локальную переменную в регистр (вместо того, чтобы выделить пространство стека), поэтому эти методы, вероятно, идентичны с точки зрения исполняемого кода. По этой причине большинство разработчиков выбрали бы тот вариант, который взаимодействует наиболее четко намерение (см. писать действительно очевидный код (ОКР)). На мой взгляд, это было бы методу А.
С другой стороны, если это чисто академическое упражнение, вы можете иметь лучшее из обоих миров с метод c:
private bool IsSumInRange(int a, int b)
{
a += b;
return (a >= -1000 && a <= 1000);
}
Я бы оптимизировать для удобства чтения. Метод Х:
private bool IsSumInRange(int number1, int number2)
{
return IsValueInRange(number1+number2, -1000, 1000);
}
private bool IsValueInRange(int Value, int Lowerbound, int Upperbound)
{
return (Value >= Lowerbound && Value <= Upperbound);
}
Небольшие методы, которые делать только 1 вещь, но легко рассуждать об.
(Это личное предпочтение, мне нравится положительный тестирование вместо негативных, ваш исходный код-это на самом деле испытание, является ли значение не выходит за пределы диапазона.)
Короче, я не'т думаю, что вопрос имеет актуальность в текущих вычислений, но с исторической точки зрения это'Очень интересный мысленный эксперимент.
Ваш интервьюер, вероятно, поклонник Мифический человеко месяц. В книге, Фред Брукс дает понять, что программисты, как правило, нужны две версии ключевых функций в свой набор: память-оптимизированная версия и процессор-оптимизированные версии. Фред основываясь на своем опыте ведущих развитие системы IBM/360 операционная система, где машины могут иметь до 8 килобайт оперативной памяти. В таких машинах, требуемый объем памяти для локальных переменных в функциях может быть важно, особенно если компилятор не оптимизировать их (или если код был написан на языке ассемблера напрямую).
В нынешнюю эпоху, я думаю, вы были бы в затруднении, чтобы найти систему, в которой наличие или отсутствие локальной переменной в методе внесет заметной разницы. Для переменной к вопросу, метод должен быть рекурсивный с глубокой рекурсии ожидается. Даже тогда, это'вероятность того, что глубина стека будет превышен вызывает исключение переполнения стека перед самой переменной вызвало проблему. Единственный реальный сценарий, где это может быть проблема с очень большой, массивы в стеке в рекурсивный метод. Но это также маловероятно, как я думаю, что большинство разработчиков будет думать дважды о ненужных копий больших массивов.
После выполнения задания с = А + Б; переменные a и B не используется. Поэтому, нет памяти используется для S Если вы не используете полностью поврежден мозг компилятора; память, которая была так или иначе использована для A и B используется повторно.
Но оптимизации эта функция полнейший бред. Если вы можете сэкономить пространство, было бы может быть 8 байт, а функция выполняется (который восстанавливается, когда функция возвращает), поэтому абсолютно бессмысленно. Если вы могли бы сэкономить время, это будет один число наносекунд. Оптимизация это пустая трата времени.
Локальные переменные тип значения размещаются в стеке или (что более вероятно для таких маленьких кусочков кода) использовать регистры в процессоре и не вижу каких-либо рам. В любом случае они недолговечны и не о чем беспокоиться. Вы начинаете рассматривать использование памяти, когда вы нуждаетесь в буфер или очередь элементов данных в коллекции, которые являются оба потенциально большие и долго живут.
Тогда это зависит от того, что вы заботитесь о Больше всего для вашего приложения. Скорость обработки? Время отклика? След памяти? Ремонтопригодность? Последовательность в дизайне? Все зависит от вас.
Как и другие ответы уже сказал, нужно думать, что вы'вэ оптимизации для.
В этом примере, я подозреваю, что любой приличный компилятор будет генерировать эквивалентный код для обоих методов, поэтому решение никак не повлияет на время выполнения or памяти!
Что это -- как раз это влияет на читаемость кода. (код для людей, чтобы читать, а не только компьютеры.) там's не слишком большая разница между двумя примерами; при прочих равных условиях, я считаю, краткость достоинство, поэтому я'д, наверное, выберет метод Б. но все остальные вещи редко бывают равными, и в более сложном реальном мире дело, это может иметь большой эффект.
Вещи, чтобы рассмотреть:
[1]: я https://en.wikiquote.org/wiki/Donald_Knuth#Computer_Programming_as_an_Art_(1974) &; сказал"
Как и многие ответы подчеркивали, пытаясь настроить эту функцию с современными компиляторами выиграл'т делать какие-либо разница. Оптимизатор, можно с большой вероятностью выяснить, лучшее решение (проголосовать в ответ, что показал ассемблерный код, чтобы доказать это!). Вы заявили, что код в интервью не совсем тот код, который вы попросили сравнить, поэтому, возможно, реальный пример делает немного больше смысла.
Но позвольте's по-другому взглянуть на этот вопрос: это интервью вопрос. Таким образом, реальный вопрос, как вы должны ответить на него, предполагая, что вы хотите попробовать и получить работу?
Позвольте's также предполагают, что собеседник знает, о чем они говорят, и они просто пытаются посмотреть, что вы знаете.
Я хотел бы отметить, что игнорирование оптимизатор, первый может создать временную переменную на стеке, а второй не't, но будет выполнять два расчета. Поэтому первый использует больше памяти, но быстрее.
Можно отметить, что в любом случае, расчет может потребоваться временная переменная для хранения результата (так, чтобы быть по сравнению), так ли вы имя переменной или не может, не имеет никакого значения.
Затем я хотел бы отметить, что в реальности код будет оптимизирован и, скорее всего, эквивалентный машинный код будет сгенерирован, так как все переменные являются локальными. Однако, это никак зависеть от того, какой компилятор вы используете (это было не так давно, что я мог бы сделать полезное улучшение производительности при объявлении локальной переменной, как "окончательной" в языке Java).
Можно отметить, что стек в любом случае живет в своей собственной страницы памяти, так что, если ваш лишний переменной вызвало стек переполнять страницы, он выиграл'т в реальности выделить больше памяти. Если это произойдет переполнения, он будет хоть всю новую страницу.
Я хотел бы отметить, что более реалистичным примером может быть выбор, использовать ли кэш для хранения результатов многочисленных расчетов или нет, и это подняло бы вопрос о памяти процессора против.
Все это показывает, что вы знаете, о чем вы говорите.
Я бы оставил его до конца, чтобы сказать, что было бы лучше, чтобы вместо этого сосредоточиться на readabilty. Хотя это и верно в данном случае, в контексте интервью это может быть interpretted как "Я не'т знаю насчет производительности, но мой код читается как Джанет и Джона история".
То, что вы должны сделать, это не пускать в обычный мягкий заявлениями о том, как оптимизация кода не надо, Дон'т оптимизировать, пока вы не профилировали код (это просто означает, что вы можете'т вижу плохой код для себя), затраты на оборудование меньше, чем программистов, и, пожалуйста, пожалуйста, Дон'т цитата кнута "и преждевременной бла-бла ..." по.
Производительность кода является подлинной проблемой для многих организаций и многих организаций нужны программисты, которые это понимают.
В частности, с такими организациями, как Amazon, часть кода имеет огромное кредитное плечо. Фрагмент кода может быть развернут на тысячи серверов и миллионы устройств и можно назвать миллиарды раз в день каждый день в году. Причин может быть тысячи подобных фрагментов. Разница между плохой алгоритм и хорошим можно быть в тысячу. Цифры и несколько все это: это делает разницу. Потенциальные расходы на организацию неработающих код может быть весьма значительным или даже смертельным, если системе не хватает мощности.
Furthmore, многие из этих организаций работают в условиях конкуренции. Поэтому вы не можете просто сказать своим клиентам возможность купить больший компьютер, если ваш конкурент'программное обеспечение уже работает на оборудовании, которое у них есть или если программное обеспечение работает на мобильный телефон и он может'т быть повышен. Некоторые приложения, в частности критическое для производительности (игры и мобильные приложения приходят на ум) и может жить или умереть по своей оперативности и скорости.
У меня лично за два десятилетия работал на многих проектах, где системы не были использованы из-за проблем с производительностью, и я был призван в оптимизации этих систем и во всех случаях она была из-за плохой код, написанный программистами, которые я'т понять влияние, что они пишут. Furthmore, это не один кусок кода, это всегда и везде. Когда я обращаюсь, это способ поздно, чтобы начать думать о производительности: повреждение было сделано.
Понимание производительности кода-это хорошие навыки, точно так же, как понимание корректности кода и стиля кода. Он выходит из практики. Сбои производительность может быть так плохо, как функциональные сбои. Если система не'т работу, он не'т работу. Не'т важно, почему. Аналогично, производительность и функциональные возможности, которые не используются как плохо.
Так что, если вас спросят о производительности, я бы рекомендовал попробовать и продемонстрировать как можно больше знаний. Если вопрос кажется плохой, вежливо указать, почему вы думаете, что это не будет проблемой в этом случае. Дон'т цитата кнута.
Вы должны сначала оптимизировать на правильность.
Ваша функция не выполняется для значений входного сигнала, близких к Инт.Максвеллову:
int a = int.MaxValue - 200;
int b = int.MaxValue - 200;
bool inRange = test.IsSumInRangeA(a, b);
Это возвращает true, потому что сумма переполняется до -400. Функция также не't работа для = инт.Параметр minvalue + 200. (неправильно складывает, чтобы "400" - а)
Мы выиграли'т знаю, что интервьюер ищет, если он или она вмешивается, но на"переполнение реальной и".
В ситуации интервью, задать вопросы, чтобы прояснить масштаб проблемы: что есть допустимое максимальное и минимальное значения входного сигнала? Если у вас есть эти, вы можете бросить исключение, если абонент отправляет значения за пределами диапазона. Или (в C#), вы можете использовать проверенный {раздел}, который будет бросать исключение на переполнении. Да, это's больше работы и сложным, но иногда это's то, что он принимает.
Твой вопрос должен был звучать: "и мне нужно, чтобы оптимизировать это все?&и".
Версии A и B отличаются на одну важную деталь, что делает предпочтительным, но это не имеет отношения к оптимизации: вы не повторите код.
Собственно, что "оптимизации" это называется устранение общих подвыражений, что почти каждый компилятор делает. Некоторые делают это базовая оптимизация даже при оптимизации выключены. Так что это'т действительно оптимизация (сгенерированный код почти наверняка будет точно таким же в любом случае).
Но если он _isn'быть, с оптимизацией, то почему это выгоднее? Ладно, вы не'т повторить код, который заботится!
Ну, во-первых, у вас нет риска в accidentially получать половину условного неправильное положение. Но что еще более важно, кто-то из читающих этот код может Грок сразу то, что вы'вновь пытается сделать, а не если((((ВТФ||это||это||longexpression)))) опыт. То, что читатель получает, чтобы увидеть, если(один || Другие)
, который является хорошей вещью. Не редко, у меня бывает так, что вы, что другой человек, читая свой собственный код через три года и думать "и ВТФ это значит?&и". В этом случае он's всегда полезно, если ваш код сразу же сообщает, что умысел был. С общих подвыражений был назван правильно, что'ы в случае.
Также, если в любое время в будущем, вы решите, что, например, нужно менять А+B
до А-Б
, вы должны изменить одном месте, а не два. И там's нет риска (снова) становится второй не так случайно.
О ваш фактический вопрос, что вы должны оптимизировать, в первую очередь код должен быть correct. Это абсолютно самая важная вещь. Код, что это'т исправить плохой код, тем более, если несмотря на то, что неправильное ее "работает" ну, или, по крайней мере, это looks like он отлично работает. После этого код должен быть читаемым (читаемый кем-то незнаком с ним).
Что касается оптимизации... определенно стоит'т сознательно пишу анти-оптимизированный код, и, конечно, я'м не говорю, что вы должны'т тратить мысли на дизайн прежде чем вы начнете (например, выбор правильного алгоритма для задачи, не наименее эффективным).
Но для большинства приложений, большую часть времени, на выполнение которых вы получаете после выполнения правильный, читабельный код, используя разумный алгоритм через оптимизирующий компилятор-это просто отлично, там'не нужно беспокоиться.
Если это'т делу, т. е. если приложение'спектакль действительно не'т отвечать требованиям, и только then, вы должны беспокоиться о таких оптимизаций, как вы пытались. Желательно, правда, вы бы пересмотреть верхнего уровня алгоритма. Если вы вызываете функцию в 500 раз вместо 50000 раз из-за лучшего алгоритма, это имеет большее влияние, чем спасение трех тактов на микро-оптимизации. Если вы don't в ларьке на несколько сотен циклов в оперативной памяти все время, это имеет большее влияние, чем несколько дешевых расчеты дополнительные, и т. д. и т. п.
Оптимизация-это сложный вопрос (можно написать целые книги о том, что и вам до конца), и тратить время на слепо optimizting определенном месте (даже не зная, что'ы вообще узкое место!) обычно потраченное время. Без профилирования, оптимизации очень трудно получить права.
Но как правило, когда вы'вновь вслепую и просто нуждаетесь/хотите сделать something, или в качестве общей стратегии по умолчанию, я бы предложил оптимизировать ПО quot&; Память".
Оптимизация "Память" (в частности, пространственная локальность доступа и узоры) обычно дает пользу, потому что в отличие от давным-давно, когда все было на "такая же" в наше время доступа к оперативной памяти является одним из самых дорогих вещей (короткое чтение из диска!) что можно в принципе сделать. В то время как АЛУ, с другой стороны, это дешево и быстрее получать каждую неделю. Пропускная способность памяти и латентность не't улучшить почти так же быстро. Хороший район и хорошие модели доступа можно легко сделать 5х разница (20х в крайности, contrieved примеры) во время выполнения по сравнению с плохим моделей доступа в данных тяжелых приложений. Быть хорошим для своих схронов, и вы будете счастливым человеком.
Чтобы положить предыдущем пункте, в перспективе, считают, что различные вещи, которые вы можете сделать бесплатно. Выполняя что-то вроде А+Б
принимает (если не оптимизированные) один или два цикла, но процессор могут обычно начинают несколько инструкций за один цикл, а может трубопровод non-зависимые инструкции, чтобы более реально это стоит всего что-то около половины цикла или меньше. В идеале, если компилятор хорош в планировании, и в зависимости от ситуации, это может стоить ноль.
Выборки данных ("Память") стоит вам либо 4-5 циклов, если вы'вновь повезло, и он's в Л1, и около 15 циклов, если вам не так повезло (Л2 бить). Если данные не'т в кэше на все, это займет несколько сотен циклов. Если ваш бессистемный узор доступа превышает ТЛБ'возможности S (это легко сделать с только ~50 записей), добавить еще несколько сотен циклов. Если ваш бессистемный узор открыть на самом деле вызывает ошибку страницы, это будет стоить вам несколько десятков тысяч циклов в лучшем случае, а в худшем миллионов.
Сейчас думаю об этом, что'ы, что вы хотите, чтобы избежать наиболее остро?
когда для оптимизации памяти скорость работы метода?
После того, как функциональность право первый. Затем selectivity заниматься микро-оптимизациями.
Как интервью вопрос по оптимизации, кода не спровоцировать обычная дискуссия, но не попадает в цель высшего уровня код функционально правильное?
C++ и C и другие связи инт
переполнение как проблема с А + Б
. Это не хорошо определены и C называет его undefined behavior. Это не указано, чтобы "обернуть" и хотя это общее поведение.
bool IsSumInRange(int a, int b) {
int s = a + b; // Overflow possible
if (s > 1000 || s < -1000) return false;
else return true;
}
Такая функция называется IsSumInRange()
можно было бы ожидать, чтобы быть четко определены и правильно выполнять для всех значений типа int
в А,Б
. Сырым а + б
- нет. Решение c можно использовать:
#define N 1000
bool IsSumInRange_FullRange(int a, int b) {
if (a >= 0) {
if (b > INT_MAX - a) return false;
} else {
if (b < INT_MIN - a) return false;
}
int sum = a + b;
if (sum > N || sum < -N) return false;
else return true;
}
Приведенный выше код может быть оптимизирован с помощью более широкого целого типа, чем инт
, если таковая имеется, а ниже или распространяя сумма > П,
сумму в < -Н-тестов в рамках `Если (а >= 0) логика. Но такая оптимизация не может по-настоящему руководить, чтобы "быстрее" и излучаемый код приведенный умный компилятор и не стоит лишний обслуживанию лукавите.
long long sum a;
sum += b;
Даже с помощью АБС(сумма)склонен к проблемам, когда
сумма == INT_MIN`.
Какие компиляторы мы говорим, и что-то вроде "Память"? Потому что в вашем примере, предполагая, что разумный оптимизатор, выражение a+b
и должен вообще быть сохранен в регистре (в виде памяти), прежде чем приступать к такой арифметике.
Так что если мы're говоря о тупой компилятор, который обнаруживает а+в
два раза, это'ы собирается выделить дополнительные регистры (память) в свой второй пример, потому что ваш первый пример может быть просто магазин, это выражение еще в одном регистре отображается в локальной переменной, но мы're говоря о очень глупо компиляторы на данный момент... если вы'работаем с другим типом глупый компилятор, который стек разливов каждой переменной повсюду, и в этом случае может быть первая вызовет больше горя для оптимизации, чем второе*.
я все еще хочу, чтобы поцарапать, что и второй, скорее всего, будет использовать больше памяти с тупой компилятор, даже если это'ы склонны к стек разливов, потому что это может в конечном итоге выделение трех регистров
а+б
разливA
иB
больше. Если мы'заново говорить самый примитивный оптимизатор захватаА+Б
Вs
, вероятно, "Помоги", он использует меньше регистров/стека разливов.
Все это крайне спекулятивный, а глупые способы отсутствуют измерения/разборке и даже в худшем сценарии, это не "Память и представление" в случае (потому что даже среди худших оптимизаторов, я думаю, мы'повторно не говорим о чем угодно, но временную память, как стек/регистры), это'ы чисто и "производительность" в случае, если в лучшем случае, а у любого разумного оптимизатора два эквивалентны, и если один не использует разумный оптимизатор, почему зацикливается оптимизация настолько микроскопический характер и особенно отсутствующих измерений? Что's, как инструкция выбора/сборки-уровень распределения регистров фокус, который я никогда не ожидал, что кто хочет оставаться продуктивным при использовании, скажем, переводчика, которые распространяются стек все.
когда для оптимизации памяти скорость работы метода?
Как на этот вопрос, если я могу решить ее в более широком смысле, часто я Дон'т найти две диаметрально противоположные. Особенно, если ваши модели доступа являются последовательными, и учитывая скорость кэша процессора, часто уменьшение количества байтов, последовательно нетривиальные входов переводит (до определенного момента), чтобы пахать через эти данные быстрее. Конечно, есть критические точки, где, если данных гораздо меньше в обмен на гораздо более инструкции, это может быть быстрее, чтобы процесс последовательно в большей форме в обмен на меньшее количество инструкций.
Но я'вэ нашел многие разработчики склонны недооценивать, сколько сокращение использования памяти в подобных случаях можно перевести на пропорциональное сокращение времени, затраченного на обработку. Это's очень по-человечески понятный, чтобы перевести затраты на выполнение инструкций, а не доступ к памяти до момента достижения для большой Луц в некоторых тщетных попытках ускорить некоторые небольшие расчеты, только чтобы найти производительность деградирует с дополнительным доступом к памяти.
Для последовательного доступа случаях через какой-то огромный массив (не говорю локальных скалярных переменных, как в вашем примере), я иду правилом, что меньше памяти для последовательного пашут приводит к большей производительности, особенно, когда результирующий код-это проще, чем иначе пока он не'т, пока мои измерения и профилирования убеждать меня в обратном, и это важно, таким же образом я полагаю, что последовательное чтение небольших двоичный файл на диске будет быстрее пахать через не больше одного (даже если меньше требует более какие-то инструкции), пока что предположение показано, что перестают действовать в моих измерений.