elizarov


Блог Романа Елизарова


Previous Entry Share Next Entry
Процессорно-специфичная оптимизация кода
elizarov

Когда я программировал в начале 1990-х годов, вполне нормальным делом был следующий подход к оптимизации кода. Сначала код программы писался на языке высокого уровня, потом в программе выявлялось узкое место и, после исчерпания алгоритмических оптимизаций, самое узкое место переписывалось на ассемблере. Порой, переписывание только внутреннего цикла на ассемблере позволяло увеличить производительность в разы! Я помню момент, когда я это делал последний раз. Я был счастливым обладателем нового процессора Pentium и, написав весьма эффективный алгоритм, остался недоволен его производительностью. Я сел и переписал самый главный цикла алгоритма на встроенном ассемблере. После чего снова произвел измерения и, к своему ужасу, увидел что производительность кода уменьшилась. Новый процессор имел два конвейера и сложные правила, которые определяли условия, при которых возможно выполнение двух операций за такт. Сложные для человека, но подъемные для компилятора. Компилятор не хуже меня справился с задачей распределения регистров и переставил набор ассемблерных команд так, чтобы уменьшить количество необходимых для выполнения кода тактов процессора. Я мог бы научиться это делать и сам, и, посвятив себя этому, наверняка научился бы это делать лучше компилятора, но в тот момент я решил, что есть более интересные области на изучение которых я могу потратить свое время.

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

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

        private int runIteration(int size) { 
            int sum = 0; 
            for (int i = 0; i < size; i++) 
                sum += op.compute(list.getInt(i)); 
            return sum; 
        } 
Прогоним этот код в одном потоке на разных размерах массива при разных операциях op используя Java HotSpot Server 1.7.0_02. Операция ID будет просто возвращать свой аргумент, и результаты будут идентичны тем, которые я получил в прошлый раз, ибо HotSpot Server отлично справляется со встраиванием кода (стало в целом чуть быстрей, ибо я остановил лишние фоновые сервисы на своей машине). Операция X2 будет умножать аргумент на два, X27 на 27 и т.п. В таблице указано сколько миллионов итераций цикла в секунду можно успеть прогнать на моем i5-520M плюс-минус среднеквадратическое отклонение из 17 замеров по 1-ой секунде каждый.

РазмерIDX2X27X31X37
1 0002.49 ± 0.031.16 ± 0.011.26 ± 0.010.83 ± 0.011.26 ± 0.01
10 0002.37 ± 0.021.13 ± 0.001.27 ± 0.000.82 ± 0.011.26 ± 0.01
100 0002.24 ± 0.051.11 ± 0.011.24 ± 0.020.82 ± 0.001.26 ± 0.02
1 000 0001.39 ± 0.020.89 ± 0.010.97 ± 0.010.70 ± 0.010.96 ± 0.01
10 000 0001.33 ± 0.010.88 ± 0.010.95 ± 0.010.70 ± 0.010.95 ± 0.01

Из первой строчки таблицы видно, что даже умножение на два каждого элемента в цикле приводит к более чем 2-х кратному падению производительности. Это происходит не из-за того, что вместо одной операции сложения надо делать две операции (умножить на два и сложить), а из-за того, что в этом случает HotSpot Server перестает создавать оптимальный код, который можно подсмотреть с помощью простого рецепта о котором я писал ранее.

А вот дальнейшие данные в первый строке более интересны. Почему умножение на разные константы работает с разной скоростью? Дело в том, что целочисленное умножение на два реализуется HotSpot-ом через сдвиг влево на один (x * 2 == x << 1). А умножение на 31 через сдвиг на пять и вычитание (x * 31 == (x << 5) - x). В то время как умножение на 27 и на 37 компилируются в честную операцию умножения.

Еще недавно, реализация операции умножения в распространенных процессорах была настолько медленной, что такие приемы оптимизации умножения на константу всегда давали выигрыш в производительности. Современные же процессоры содержат полноразмерный аппаратный умножитель, способный работать в конвейерном режиме. Более того, из-за особенной способа генерации кода HotStop-ом и архитектуры x86, где в одной операции умножения на константу можно совместить загрузку операнда из памяти и запись результата в регистр, даже замена одного умножения на, казалось бы, более простую операцию сдвига, приводит к уменьшению производительности кода. А уж компиляция умножения на 31 в две операции (сдвига и вычитания) и подавно — умножение на 31 самое медленное в этом тесте.

Кстати, этот тест так же является наглядной иллюстрацией к моей предыдущей записи. При последовательной работе с памятью, как в нашем случаем, с ростом сложности кода память быстро перестает быть узким местом, ибо память хорошо оптимизирована на последовательную работу. В то время как в столбце ID переход от массива размером 1000, который полностью влезает в кэш, к массиву в 10М элементов приводит к падению производительности почти в два раза (на 87%), то в столбце X31, где производится больше всего операций над каждым элементом, такой же переход приводит к падению производительности всего на 19%. Там основное время уходит не на ожидание памяти, а на манипуляцию с данными, и компиляция/переписывание этого кода с использованием SIMD инструкций приведет к значительному повышению его производительности.

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


  • 1
"А вот дальнейшие данные в первый строке более интересны. Почему умножение на разные константы работает с разной скоростью? Дело в том, что целочисленное умножение на два реализуется HotSpot-ом через сдвиг влево на один (x * 2 == x << 1). А умножение на 31 через сдвиг на четыре и вычитание (x * 31 == (x << 4) - x). В то время как умножение на 27 и на 37 компилируются в честную операцию умножения."

Ром, а не покажешь ассемблер, что там получается? По моим данным, imul все еще недостаточно быстр, и сдвиг с вычитанием должен работать быстрее. Хотя, могут быть нюансы :)

Да, на атоме 5/6 циклов latency у IMUL, на core 3 цикла, throughput везде 1.

throughput 1 - это конечно хорошо, если мы его загрузим умножениями, чтоб они в него лезли каждый такт. А так, лейтенси никуда не девается. Ну и на сдвиги-сложения-вычитания все-таки больше юнитов, чем на умножение. Т.е. их могут и по 2 штуки в такт выплевывать. Были б данные.

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

Надо будет поэкспериментировать...

> на core2 заметил, что интеловские процы очень чувствительны к длине цикла
LSD 18 простых инструкций

Не, LSD тут не причем. Производительность падает очень сильно и после выхода за размеры LSD.

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

Ассемблер чего тебе показать? Кстати, я опечатался в заметке. Конечно же, x * 31 == (x << 5) - x (cдвиг на 5, а не на 4). Уже исправил.


Edited at 2012-03-27 04:57 pm (UTC)

"Ассемблер чего тебе показать?"

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

Вот тебе развернутый цикл умножения на 27:
01869A90  mov         dword ptr [esp+18h],ebx 
01869A94  imul        ecx,dword ptr [esi+edi*4+40h],1Bh 
01869A99  mov         dword ptr [esp+24h],ecx 
01869A9D  imul        ecx,dword ptr [esi+edi*4+38h],1Bh 
01869AA2  mov         dword ptr [esp+28h],ecx 
01869AA6  imul        ecx,dword ptr [esi+edi*4+48h],1Bh 
01869AAB  mov         dword ptr [esp+2Ch],ecx 
01869AAF  imul        ecx,dword ptr [esi+edi*4+44h],1Bh 
01869AB4  mov         dword ptr [esp+30h],ecx 
01869AB8  imul        ecx,dword ptr [esi+edi*4+3Ch],1Bh 
01869ABD  mov         dword ptr [esp+34h],ecx 
01869AC1  imul        ecx,dword ptr [esi+edi*4+30h],1Bh 
01869AC6  mov         dword ptr [esp+50h],ecx 
01869ACA  imul        ecx,dword ptr [esi+edi*4+2Ch],1Bh 
01869ACF  mov         dword ptr [esp+38h],ecx 
01869AD3  imul        ebx,dword ptr [esi+edi*4+20h],1Bh 
01869AD8  imul        ecx,dword ptr [esi+edi*4+1Ch],1Bh 
01869ADD  mov         dword ptr [esp+3Ch],ecx 
01869AE1  imul        eax,dword ptr [esi+edi*4+10h],1Bh 
01869AE6  imul        ebp,dword ptr [esi+edi*4+0Ch],1Bh 
01869AEB  imul        ecx,dword ptr [esi+edi*4+14h],1Bh 
01869AF0  mov         dword ptr [esp+40h],ecx 
01869AF4  imul        ecx,dword ptr [esi+edi*4+34h],1Bh 
01869AF9  mov         dword ptr [esp+44h],ecx 
01869AFD  imul        edx,dword ptr [esi+edi*4+18h],1Bh 
01869B02  imul        ecx,dword ptr [esi+edi*4+24h],1Bh 
01869B07  mov         dword ptr [esp+48h],ecx 
01869B0B  imul        ecx,dword ptr [esi+edi*4+28h],1Bh 
01869B10  mov         dword ptr [esp+4Ch],ecx 
01869B14  add         ebp,dword ptr [esp+18h] 
01869B18  add         eax,ebp 
01869B1A  add         eax,dword ptr [esp+40h] 
01869B1E  add         eax,edx 
01869B20  mov         ecx,dword ptr [esp+3Ch] 
01869B24  add         ecx,eax 
01869B26  add         ebx,ecx 
01869B28  add         ebx,dword ptr [esp+48h] 
01869B2C  add         ebx,dword ptr [esp+4Ch] 
01869B30  mov         ecx,dword ptr [esp+38h] 
01869B34  add         ecx,ebx 
01869B36  mov         ebx,dword ptr [esp+50h] 
01869B3A  add         ebx,ecx 
01869B3C  add         ebx,dword ptr [esp+44h] 
01869B40  add         ebx,dword ptr [esp+28h] 
01869B44  mov         ecx,dword ptr [esp+34h] 
01869B48  add         ecx,ebx 
01869B4A  add         ecx,dword ptr [esp+24h] 
01869B4E  mov         ebp,dword ptr [esp+30h] 
01869B52  add         ebp,ecx 
01869B54  mov         ebx,dword ptr [esp+2Ch] 
01869B58  add         ebx,ebp 
01869B5A  add         edi,10h 
01869B5D  cmp         edi,dword ptr [esp+1Ch] 
01869B61  jl          01869A90 

А вот умножение на два сдвигами:
0183A510  mov         dword ptr [esp+18h],ebx 
0183A514  mov         ecx,dword ptr [esi+edi*4+40h] 
0183A518  mov         dword ptr [esp+24h],ecx 
0183A51C  mov         ecx,dword ptr [esi+edi*4+38h] 
0183A520  mov         dword ptr [esp+28h],ecx 
0183A524  mov         ecx,dword ptr [esi+edi*4+34h] 
0183A528  mov         dword ptr [esp+2Ch],ecx 
0183A52C  mov         ecx,dword ptr [esi+edi*4+28h] 
0183A530  mov         dword ptr [esp+30h],ecx 
0183A534  mov         ecx,dword ptr [esi+edi*4+24h] 
0183A538  mov         dword ptr [esp+34h],ecx 
0183A53C  mov         ecx,dword ptr [esi+edi*4+18h] 
0183A540  mov         dword ptr [esp+38h],ecx 
0183A544  mov         ecx,dword ptr [esi+edi*4+14h] 
0183A548  mov         dword ptr [esp+3Ch],ecx 
0183A54C  mov         ebx,dword ptr [esi+edi*4+0Ch] 
0183A550  mov         eax,dword ptr [esi+edi*4+10h] 
0183A554  mov         ebp,dword ptr [esi+edi*4+1Ch] 
0183A558  mov         edx,dword ptr [esi+edi*4+20h] 
0183A55C  mov         ecx,dword ptr [esi+edi*4+2Ch] 
0183A560  mov         dword ptr [esp+40h],ecx 
0183A564  mov         ecx,dword ptr [esi+edi*4+30h] 
0183A568  mov         dword ptr [esp+50h],ecx 
0183A56C  mov         ecx,dword ptr [esi+edi*4+3Ch] 
0183A570  mov         dword ptr [esp+44h],ecx 
0183A574  mov         ecx,dword ptr [esi+edi*4+44h] 
0183A578  mov         dword ptr [esp+48h],ecx 
0183A57C  mov         ecx,dword ptr [esi+edi*4+48h] 
0183A580  add         edi,10h 
0183A583  shl         ecx,1 
0183A585  mov         dword ptr [esp+4Ch],ecx 
0183A589  mov         ecx,dword ptr [esp+24h] 
0183A58D  shl         ecx,1 
0183A58F  mov         dword ptr [esp+24h],ecx 
0183A593  mov         ecx,dword ptr [esp+48h] 
0183A597  shl         ecx,1 
0183A599  mov         dword ptr [esp+48h],ecx 
0183A59D  mov         ecx,dword ptr [esp+44h] 
0183A5A1  shl         ecx,1 
0183A5A3  mov         dword ptr [esp+44h],ecx 
0183A5A7  mov         ecx,dword ptr [esp+50h] 
0183A5AB  shl         ecx,1 
0183A5AD  mov         dword ptr [esp+50h],ecx 
0183A5B1  mov         ecx,dword ptr [esp+40h] 
0183A5B5  shl         ecx,1 
0183A5B7  mov         dword ptr [esp+40h],ecx 
0183A5BB  shl         edx,1 
0183A5BD  shl         ebp,1 
0183A5BF  shl         eax,1 
0183A5C1  shl         ebx,1 
0183A5C3  add         ebx,dword ptr [esp+18h] 
0183A5C7  add         eax,ebx 
0183A5C9  mov         ecx,dword ptr [esp+3Ch] 
0183A5CD  shl         ecx,1 
0183A5CF  add         eax,ecx 
0183A5D1  mov         ecx,dword ptr [esp+38h] 
0183A5D5  shl         ecx,1 
0183A5D7  add         eax,ecx 
0183A5D9  add         ebp,eax 
0183A5DB  add         edx,ebp 
0183A5DD  mov         ecx,dword ptr [esp+34h] 
0183A5E1  shl         ecx,1 
0183A5E3  add         edx,ecx 
0183A5E5  mov         ecx,dword ptr [esp+30h] 
0183A5E9  shl         ecx,1 
0183A5EB  add         edx,ecx 
0183A5ED  mov         ecx,dword ptr [esp+40h] 
0183A5F1  add         ecx,edx 
0183A5F3  mov         ebx,dword ptr [esp+50h] 
0183A5F7  add         ebx,ecx 
0183A5F9  mov         ecx,dword ptr [esp+2Ch] 
0183A5FD  shl         ecx,1 
0183A5FF  add         ebx,ecx 
0183A601  mov         ecx,dword ptr [esp+28h] 
0183A605  shl         ecx,1 
0183A607  add         ebx,ecx 
0183A609  mov         ecx,dword ptr [esp+44h] 
0183A60D  add         ecx,ebx 
0183A60F  add         ecx,dword ptr [esp+24h] 
0183A613  mov         ebp,dword ptr [esp+48h] 
0183A617  add         ebp,ecx 
0183A619  mov         ebx,dword ptr [esp+4Ch] 
0183A61D  add         ebx,ebp 
0183A61F  cmp         edi,dword ptr [esp+1Ch] 
0183A623  jl          0183A510 

Тут, как ты правильно заметил выше, нюанс именно в количестве команд. Да и вообще весь нюанс в HotSpot-е.

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

"Да и вообще весь нюанс в HotSpot-е."

Да уж! Такие циклы даже gcc не умеет делать :) Я даже просить не буду ассемблер от умножения на 31. Лучше такое и не видеть, мне потом кошмары сниться будут :)

Ты прав, вряд ли дело в скорости умножения. Эх, не допилил Коля Иготти ХотСпот, а теперь уже вряд ли кто-нибудь нормально допилит :)

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

ЗЫ. Кстати, еще лучше эти циклы на SIMD сделать. Это ХотСпот совсем не потянет?

HotSpot, насколько мне известно, SIMD не умеет и http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6604786 тому свидетель: "SSE optimization for basic elementwise array operations", Priority: 5-Very Low.

Надо же! Когда-то они долбали меня префетчами, т.е. хотели ведь что-то хорошее сделать. Уж SIMD-то гораздо проще. Жаль.

Призываю в thread shipilev..

Леша, неужели? Даже в Harmony не было такой страшной арифметики!

Рома, а жаба действительно начала работать,

в том числе в условиях аптайм от месяца, без выкрутасов с памятью (на стандартной аллокации), с предсказуемым временем отклика?

Или все по-прежнему (вариант 1 - в какой-то момент врубился gс, и долго чистил память, в том числе в свопе; вариант 2 - gc не смог опознать мусор, память ушла в своп, на очередной итерации gc прошелся по памяти, в том числе по свопу, и подвесил ввод-вывод хоста на минуты; финальное состояние - приложение вылетело из-за отказа в памяти/жабовская виртуалка умерла при отказе ОС в запросе памяти/ОС встала в коленно-локтевую при исчерпании свопа; вариант 3 - память виртуалки была насильственно ограничена, и приложение тупо вылетело, не обработав отказ в памяти/сошло с ума при отказе в памяти и виртуалку пришлось кормить девятым сигналом)?

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

Плюснику можно руки помыть, и он все-таки думает про delete, а характерный жабер свято верит во всемогущество gc и даже nullением не занимается. "Ставьте терабайт оперативки".

Не холивара gc vs delete ради, но справедливости для :)

ЗЫ:
1) СЗОТ.
2) Насколько я понимаю, длинный аптайм с достаточно интенсивной аллокацией памяти в вашей практике тоже должен встречаться. Чем боретесь, и есть ли с чем бороться? Интересно было бы увидеть твою заметку на эту тему :)

Edited at 2012-03-28 06:48 am (UTC)

Re: Рома, а жаба действительно начала работать,

Вы сами почти ответили на свой вопрос. Дело же в первую очередь не в языке программирования, а в том, кто на нем программирует. На эту тему даже ряд научных исследований есть. Как-нибудь напишу об этом отдельный пост. Наши программисты и на C/C++ и на Java пишут стабильно работающий код. А если код не работает, то мы говорим "ошибка в ДНК". Поэтому для повышения производительности труда мы пишем почти всё (даже шлюзы к биржам для dxfeed) на Java. Другое дело, что бывают задачи, где Java не подойдет (или будет совсем не оптимальным выбором), но перед нами такие задачи стоят редко (когда стоят -- используем более подходящие языки). Как-нибудь отдельно напишу подробней.

Что касается Java-специфичных вопросов, то в Java c первого дня используется "strict garbage collector" и только он. То есть сценарий 2 ("gc не смог опознать мусор") в Java в принципе невозможен. Всё в руках программиста. Наличие gc не отменяет голову программиста и необходимость думать о памяти. Да, gc уменьшает размер и сложность кода, ибо не надо явно писать delete, не надо делать ручной подсчет ссылок и т.п., что особенно сложно в параллельном коде (кто изучал wait-free алгоритмы и проблему ABA в частности — поймет), но думать-то все-равно надо. Там где в C/C++ надо не забыть написать delete, в Java надо не забыть потереть ссылки на объект.

Другое дело, что в приложениях где часто выделяются и освобождаются объекты да еще и во многих потоках пара new+delete обычно работает медленней чем java new+gc всего чего было выделено. Но все-равно код который плодит миллионы временных объектов в секунду будет медленно работать хоть на C/C++ хоть на Java (gc не спасет). А путь к увеличению скорости такого кода и в C/C++ и в Java общий: минимизировать использование памяти в целом и выделение/освобождение объектов в частности. В этом же секрет стабильной и продолжительной работы. И вообще, основной способ нашей борьбы — улучшение ДНК.


Edited at 2012-03-28 06:55 am (UTC)

1) (глубоко оскорбился... после 21 года со дня знакомства услышать "Вы" - это что-то. ICQ UIN 99164729, nickname rp1810)
2) Я к жабе подхожу только как юзверг. И мне ни разу не попадались сколько-нибудь сложные приложения на ней, которые не текли бы на длинных интервалах времени.
3) "не смог опознать мусор" - сценарий возможный независимо от модели коллектора. Мусор в данном контексте - не то, что коллектор считает мусором, а то, что считает мусором и не планирует более использовать автор кода.
4) Ну а по поводу ДНК - что тут спорить. Проблема лишь в среднем NER (Nucleotid Error Rate) по популяции Жабников и Плюсников. Наличие gc является притягательным для счастливых обладателей ошибок, и даже пакетных ошибок в ДНК.
5) Вечный вопрос. Сродни быстро-дешево-качественно. 1-Время разработки/2-читаемость- поддерживаемость-переносимость кода/3-стабильность работы кода/4-скорость работы кода.
Моя сегодняшняя система приоритетов 2-3-4-1 или 2-3-1-4. Поскольку последствия 1-3-4-2, 1-4-3-2 - я многократно видел. А одну 1 - с игнорированием 2,3,4 - сейчас пытаюсь разгрести.

1) Извиняюсь, но мои экстрасенсорные способности не достаточно развиты, чтобы не только установить личность человека прячущуюся за rp1810, но и даже чтобы установить ICQ UIN по lj user id.
2) Я ежедневно использую и работаю над Java приложениями в миллионы строк кода, которые не текут на длинных интервалах времени. У нас вся компания Devexperts занимается созданием таких приложений профессионально. Да и во всей финансовой индустрии это фактически стандарт.
3) В этом смысле да. Читать мысли не умеет. Мозг программиста не отменяется. Не подумав можно потерять память как на С/C++ так и на Java. На Java как бы меньше надо напрягаться, но совсем мозг отключать нельзя.
4) А кто такие Жабники и Плюсники? Мы нанимаем в первую очередь высококвалифицированных программистов. Знание языков это уже второе. Хороший программист знает и C/C++ и Java и массу других языков. А не знает Java, например, то изучит при необходимости (у нас в компании масса таких примеров).
5) Конечно. Сильно зависит от специфики стоящей задачи.

1) Как бы, правило "никогда не переименовывать контакты", плюс некоторое количество памяти спасает не только от анекдота про "этого мудилу Иванова" (http://bofire.net/vforum/printthread.php?t=65&pp=25&page=67) :)
2) И что, этот код писали люди, НЕ владеющие свободно gc-free (не less :) ) - языками с наличием динамической аллокации?
3) Умеет ли современная молодежь (C++ - "устарел", gc - наше всё) не отключать мозг? Или вы просто отсекаете этот шлак? :)
4) Язык, выбираемый при условии свободы выбора языка :) Для non-GUI.
5) Ты способен придумать пример, в котором достаточно оптимальным выбором языка были бы Java или C++, и при этом был бы возможен приоритет скорости разработки над читаемостью и стабильностью? Опять-таки, желательно в категории non-GUI.

1) К сожалению (или к счастью) моя память под завязку забита знаниями про программированию. На хранение там таблиц соответствия между lj user id и реальными людьми места не остается. Если это соответствие не вычислимая функция, то меня обычно спасает гугл и это хорошо, так как позволяет использовать свою память более эффективно. Контакты я, естественно, не удаляю. Твой ICQ UIN у меня до сих пор в контактах, только вот способа вычислить его по твоему lj user id, как я уже писал, я не знаю (как и обратно — вычислить lj user id по ICQ UIN). Чтобы у других людей не было таких проблем, я аккуратно слежу, чтобы все мои online identities были между собой слинкованы (чтобы, как минимум, был путь хотя бы в одну сторону от каждой к каждой).
2) Естественно это пишут высококвалифицированные программисты владеющими обязательным набором знаний по программированию, который, естественно, включает в себя и знание принципов управления памятью в разных языках программирования (как с gc так и без него). Вообще, умение управлять памятью к языкам в целом и к gc в частности имеет весьма опосредованное отношение. Либо программист умеет думать про память и управлять ей либо... вряд-ли его можно назвать квалифицированным программистом.
3) Мы смотрим не на возраст (молодежь/старики) а на знания. Если программист имеет необходимые знания и мозг, то это наш программист.
4) Хороший специалист выбирает язык исходя из всех аспектов поставленной перед ним задачи. Я уже вскользь упоминал об этом. GUI/non-GUI это всего лишь один из аспектов в постановке задачи, который программист должен анализировать делая этот выбор.
5) Не очень понял чего ты хочешь получить? Еще раз подчеркну, что скорость разработки, читаемость и стабильность в первую очередь зависит от того кто пишет код, а не от того на каком языке он пишется. При прочих равных, если зафиксировать того, кто пишет код (взять меня, например), я могу привести примеры задач которые проще/быстрей решить на C++, так и задач которые проще/быстрей решить на Java, хотя бы в силу наличия или отсутствия тех или иных стандартных библиотек, или в силу наложенных прямо в самой задаче нефункциональных ограничений на производительность, переносимость и т.п. Конечно проще привести "маленький" пример, ибо на большом проекте многие очевидные различия (как то производительность) нивелируются, но начинаются сильно играть неочевидные различия (как то простота поддержки, внесения изменений, и т.п.).

5) Еще раз и по буквам - пререквизит: в задаче оправдано использование языка из категории распространенных объектно-ориентированных общего назначения (C++, Java, C#).
Вопрос - может ли начало списка приоритетов в подобной задаче, по твоему мнению, выглядеть иначе, чем 2-3.

И возвращаясь собственно к моему вопросу. Как ты, может быть, знаешь, я менял сторону силы :) и два с половиной года прожил эксплуатационщиком.
Так вот, с точки зрения эксплуатационщика, ответом на вопрос "всегда ли XXX течет на длинных промежутках" - являются два датированных вывода ps с нужными опциями. Первый, показывающий объем памяти процесса на аптайме 30 дней. Второй - объем памяти процесса на аптайме 90 дней, плюс данные о том, что процесс работал не в холостом режиме. (по-хорошему, подобный тест выполняется уже в собственном окружении эксплуатирующей организации, без возможности разработчика повлиять на процесс - но тебе я поверю на слово) А все, что ты написал - воспринимается как обыкновенная вода из отдела продаж поставщика ... "у нас высококвалифицированные программисты, поэтому наш код никогда не течет, не падает; женщины, пользующиеся нашим программным обеспечением, никогда не стареют; мужчины - застрахованы от импотенции". Закончу переводной цитатой (по памяти) из (Г/Х)ерберта... хотя возможно, у него это тоже цитата - лень искать... "покажите мне идеально сделанную работу - и я покажу вам человека, который скрывает свои ошибки".

Система приоритетов сильно зависит от решаемой задачи. При чем тут объектно-ориентированные языки общего назначения? Мы, например, в компании, пишем код на долгие годы и у нас чаще всего на первом месте стоит читаемость и надежность, но даже в таком коде есть островки в которых нужна производительность, например, и там приоритеты меняются. А есть другой тип задач, когда важно написать код как можно быстрей несмотря не на что, и т.д. и т.п. При желании можно придумать задачу под любой порядок приоритетов, но зачем тебе это?

Если твой вопрос был "всегда ли Java течет на длинных промежутках", то ответ --конечно нет. Всё зависит от кода приложения (и при чем тут Java -- то же верно и про C++)? У нас есть очень нетривиальные серверные процессы написанные на Java с uptime-ом много месяцев, обрабатывающие постоянно запросы от десятков тысяч пользователей и постоянно жрущие при этом несколько десятков процентов CPU на очень много-ядерной машине. Например, наш OnDemand Market Data сервис в dxFeed, который обслуживает клиентов TD Ameritrade (второй по величине брокер в США) в платформе thinkorswim. У самой платформы thinkorswim, которую тоже пишем мы, такого up-time в принципе быть не может (мы добавляем туда новый функционал раз в месяц), а вот серверная часть обслуживающая OnDemand как запущена так работает без остановок уже очень давно, постоянно потребляя несколько десятков гигабайт памяти (это расчетный размер внутренних кэшей).

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

Коллега,

А вы не хотите про это рассказать на HighLoad++?
http://highload.ru/

Почему бы и нет. А когда оно будет (и будет ли) в 2012 году?

  • 1
?

Log in

No account? Create an account