macroevolution: (anomalocaris)
macroevolution ([personal profile] macroevolution) wrote2015-01-11 02:41 pm

Посоветуйте язык программирования

Я всю жизнь программировал только на бейсике, на разных его версиях.  Так получилось. Когда писал кандидатскую, набралось очень много таблиц с данными (это были морфологические признаки морских ежей), и я задолбался обсчитывать их на калькуляторе. Поэтому быстренько освоил бейсик (тогда персональные компьютеры IBM только начали появляться, и к ним прилагался язык GW-Basic). Освоил - и сразу почувствовал себя человеком. С тех пор не переучивался, сейчас пишу все свои программки на VBA в MS Access. То есть в программировании я дилетант, но опытный. Программированием пользуюсь сейчас для имитационного моделирования эволюционных процессов в популяциях. Подумываю об одной новой модели, но понимаю, что на VBA она будет работать невыносимо медленно. Насколько я понимаю, программа, написанная почти на любом другом языке, компилируемом, будет работать в разы быстрее. Вопрос такой: какой из этих языков мне будет быстрее и проще всего освоить? Времени, сил и желания преодолевать трудности и вникать в программистские проблемы - не имеется. Мне бы этот язык просто скачать (можно купить, если не слишком дорого), освоить за пару-тройку дней - и вперед. Т.е. главное, чтобы он был максимально простым в освоении для того, кто знает бейсик, без всяких интеллектуальных "понтов", но работал хотя бы раз в 10 быстрее.

[identity profile] pssshik.livejournal.com 2015-01-11 03:08 pm (UTC)(link)
жаба намного проще и си и си++ так как управление памятью отдано гц. А какие-такие фокусы из си в си++ вызывают ошибки?

[identity profile] agalakhov.livejournal.com 2015-01-11 04:53 pm (UTC)(link)
На C++ типичная ошибка - это написать пару malloc+free или new+delete. Это нормальная техника в Си, но недопустимо в языках, поддерживающих исключения. Потому что исключение перепрыгивает delete. Следует использовать unique_ptr или shared_ptr. И здесь есть еще отдельная пакость в использовании переменной после delete, потому что с большой вероятностью лежит она все еще в нашем адресном пространстве и даже может все еще иметь прошлое значение. Получается программа, которая ОБЫЧНО работает (ведь с вероятностью 99% переменная все еще имеет верное значение!), но время от времени жестоко глючит. И если программист не имеет представления об инструментах вроде Electric Fence, то из-за такого кода он рискует оказаться в глубокой заднице.

Другая типичная ошибка - забывать слово const и использовать string вместо const string&, а еще злоупотреблять присваиванием строк. Хотя это работает, это работает кошмарно медленно и жрет очень много памяти. В глубоком цикле такая ошибка может привести к высвоповыванию или даже к срабатыванию OOM killer, не говоря уж о том, что из-за этого жалкие несколько килобайт текста могут обрабатываться несколько минут.

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

[identity profile] guga50.livejournal.com 2015-01-11 06:06 pm (UTC)(link)
>> Обычно это бывает, если программист ленится передавать переменную через параметры функции и потому делает переменную членом класса. Такая практика - вообще очень большое зло, но на языках без GC программист очень быстро получает по лбу граблями от деструктора этой переменной

не понял, если честно. это как?

[identity profile] agalakhov.livejournal.com 2015-01-11 06:45 pm (UTC)(link)
Это когда человек, вместо того, чтобы написать примерно так:

int x = 5;
f1(x);
f2(x);

делает переменную x членом класса и пишет примерно так:

this.x = 5;
f1();
f2();

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

[identity profile] guga50.livejournal.com 2015-01-11 07:55 pm (UTC)(link)
это я понял. как по жопе граблями получить в смысле по лбу я не понял.

[identity profile] agalakhov.livejournal.com 2015-01-11 09:49 pm (UTC)(link)
А очень просто. Если эта штука - объект, то она наверняка где-то конструируется и где-то уничтожается. Конструируют ее скорее всего не вместе с классом, а позднее - там, где в первый раз присвоили. На C++ это приводит к примерно такому коду:

class Foo {
public:
Foo() : x(nullptr) { }
~Foo() { if (x) delete x; }
void do() {
x = new int(42);
...
}
private:
int* x;
};

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

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

[identity profile] alex-197.livejournal.com 2015-01-12 12:32 am (UTC)(link)
за второе - нужно вообще лишать компа на год:)
особенно сладко будет тому, кто потом будет это г... отлаживать:)

[identity profile] agalakhov.livejournal.com 2015-01-12 08:49 am (UTC)(link)
Увы, реальность такова, что такой код присутствует в 99.9% закрытого софта. Как раз когда код пишется по идеологии ООП, чтобы "в разработке могли участвовать все, а не только узкие специалисты", такие штуки обязательно появляются. Особенно когда разработчики обучены исключительно ООП на Java/C# и никогда не проходили хардкорных курсов вроде построения и доказательства алгоритмов. Я встречал довольно много вполне успешных и развивающихся фирм, в которых подобная разработка считается нормой - ведь продукт выпускается, работает и хорошо продается.

[identity profile] psilogic.livejournal.com 2015-01-11 07:12 pm (UTC)(link)
отвечу за вашего собеседника

1. пусть есть временная переменная tmp, ей что-то присвоено

2. ссылку на эту переменную отдают какому-то долгоживущему объекту obj, прикапывают в каком-нибудь его поле obj.member

3. мы выходим из области видимости для tmp
на этом шаге в языке без GC tmp уничтожается
на этом шаге в языке с GC tmp остается т.к. на него есть ссылка из obj

4. мы пытаемся использовать obj.member
на этом шаге в языке без GC возможен краш
на этом шаге в языке с GC краша не будет, но будет обращение к вроде как валидному объекту, который давно не нужен и, скорее всего, никак не соответствует происходящему
Edited 2015-01-11 19:13 (UTC)

[identity profile] guga50.livejournal.com 2015-01-11 08:01 pm (UTC)(link)
как же можно такой дикий огород намутить?
если тэмп часть объекта такого не будет пока он живёт.

[identity profile] psilogic.livejournal.com 2015-01-11 08:20 pm (UTC)(link)
часть объекта не tmp, а member


struct HEX
{
   KakoiToTip *member;
};

void do_something(HEX *hex)
{
   KakoiToTip tmp;
   hex->member= &tmp;
   ...
   //forget clear hex->member
}

void do_something2(HEX *hex)
{
   if (hex->member)
      printf("%s", hex->member->name());
}

void main()
{
   HEX hex;
   do_something(&hex);
   do_something2(&hex); //ups!
}

(no subject)

[identity profile] guga50.livejournal.com - 2015-01-11 20:28 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2015-01-11 20:39 (UTC) - Expand

(no subject)

[identity profile] alex-197.livejournal.com - 2015-01-12 00:37 (UTC) - Expand

(no subject)

[identity profile] guga50.livejournal.com - 2015-01-12 04:38 (UTC) - Expand

(no subject)

[identity profile] alex-197.livejournal.com - 2015-01-12 04:40 (UTC) - Expand

(no subject)

[identity profile] alex-197.livejournal.com - 2015-01-12 04:45 (UTC) - Expand

(no subject)

[identity profile] guga50.livejournal.com - 2015-01-12 07:49 (UTC) - Expand

[identity profile] agalakhov.livejournal.com 2015-01-11 04:56 pm (UTC)(link)
Кстати, для вычислительных алгоритмов над большими массивами языки с чистым GC (вроде java) имеют очень спорную пригодность именно из-за склонности GC освобождать память слишком поздно. Когда вычисления и так требуют гигабайты (а для решения уравнений, сводящихся к диагонализации матриц, это не редкость), дополнительный расход от опоздания GC часто становится фатальным и приводит к тому, что программа вообще не работает на данном железе. Именно поэтому программы для научных расчетов крайне редко пишутся на Java, чаще используются Fortran и Python, чуть реже C.

[identity profile] pssshik.livejournal.com 2015-01-11 05:18 pm (UTC)(link)
Ну память нынче очень дешевая - это не проблема (смотрю на жаба веб сервер с 45 гб выделенной под него памяти). Жаба просто не приспособлена для научных расчетов - ну не удобно создавать самому всю инфраструктуру, ввод и вывод - в жабе нет по умолчанию даже комплексных векторов и матриц, библиотеки, которые с ними работают - какие-то дикие. Еще минус - сверхжесткая типизация: описывать каждую финтифлюшку как отдельный класс, делать дерево наследования, чтобы использовать один метод для разных классов - та еще забава.

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

[identity profile] agalakhov.livejournal.com 2015-01-11 05:29 pm (UTC)(link)
Питон не коряв, он просто очень непривычен. С практической точки зрения он как раз очень удачный - очень мало языковых сущностей, практически все является просто синтаксическим сахаром поверх пары простейших внутренних операций, и за счет этого можно очень быстро писать очень сложные вещи. (Мой личный рекорд - полноценный работающий транслятор PHP-подобного языка, написанный с нуля за 40 минут).

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

[identity profile] agalakhov.livejournal.com 2015-01-11 05:36 pm (UTC)(link)
А память я бы не стал считать "не проблемой". Научные расчеты всегда упирались как раз в память. Большинство практических задач по памяти имеют O(n^2) или O(n^3), поэтому удвоение и даже учетверение памяти - это слону дробина. Я помню, на 256 мегабайтах мы могли едва-едва делать расчет для 20 атомов, а реально хотелось бы для нескольких тысяч. Для таких вещей и терабайта не хватить может. Поэтому приходится очень аккуратно оптимизировать расход памяти: экономия пары байтов в структуре, которая создается миллиард раз - вот уже и пару гигабайт сэкономили.

В обработке больших данных, которой я тоже позаниматься успел, та же петрушка. Когда база весит около 100 гигов и когда стремишься засосать в ОЗУ максимум, сколько туда вообще влезет, чтобы не прыгать головкой по диску, считать память начинаешь тоже уже очень аккуратно. 16 гигов на один массив, 16 гигов на другой, и следишь, чтобы все это объем физической памяти не превысило, а то улетишь в своп.

[identity profile] pssshik.livejournal.com 2015-01-11 05:50 pm (UTC)(link)
ну база на 100 гигов - это фигня, если честно, т.к. можно в сервер и 128гигов набить. если нужно рандомное чтение-запись - ну ссд диски уже вовсю используются. расчитывать терабайт на одном серваке - ну это неэффективно уже по процессорным мощностям, так что терабайт вычислений будет раскидан по 5-10 машинам, что реально уже сейчас, а завтра будет просто общим местом. Проблема обьект-спамминга (когда на простую операцию создается класс обертка, несущий в себе класс, который является посредником для общения с другим классом, а потом передается другому свежесозданному классу,... ) в яве стоит остро, но это уже уровень программиста.

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

[identity profile] agalakhov.livejournal.com 2015-01-11 06:52 pm (UTC)(link)
> ну база на 100 гигов - это фигня, если честно, т.к. можно в сервер и 128гигов набить.

Если база ОБРАБАТЫВАЕТСЯ, то для промежуточных результатов и словарей требуется ОЗУ, в несколько раз превышающее объем базы. Практически при 128 гигабайтах памяти предел объема базы, при котором такая обработка еще эффективна, составляет 30-40 гигабайт. Если надо больше, то обработку приходится делать уже оффлайн, на дисковых файлах и не в реальном времени.

> ООП эффективно для задач, которые легко представляются в виде диаграммы обьекты-акторы-действия

Но в таких задачах уже вовсе необязательно делать ООП на уровне языка, достаточно на уровне библиотек. ООП создавалось как раз для решения таких задач (Smalltalk), но потом были придуманы методы поинтереснее и с меньшим количеством подобных эффектов.

[identity profile] akheront.livejournal.com 2015-01-15 11:36 am (UTC)(link)
Память дешёвая была летом. Сейчас кусается.

[identity profile] pssshik.livejournal.com 2015-01-15 02:22 pm (UTC)(link)
Память кусалась пять (10, 15) лет назад - сейчас дешевая и будет только дешеветь :)

[identity profile] akheront.livejournal.com 2015-01-15 07:06 pm (UTC)(link)
Да что вы говорите. Валюта дорожает, а память дешеветь станет? Она же импортная вся.

[identity profile] chebudzee.livejournal.com 2015-01-29 01:27 pm (UTC)(link)
Что за бред? В питоне тоже есть GC и оно работает гораздо медленне чем java.

[identity profile] agalakhov.livejournal.com 2015-01-29 02:13 pm (UTC)(link)
Вы очень грубо ошиблись. В питоне менеджмент памяти основан не на GC, а на прямом счете ссылок. GC там вспомогательный и исполняется крайне редко (в основном - на неправильно написанном коде, чтобы разрывать кольцевые ссылки).

К тормозам Питона GC отношения вообще не имеет. Все дело в том, что РЕФЕРЕНСНАЯ (и только референсная!!!) реализация Питона - так называемый CPython - это чистый интерпретатор. В нем нет JIT совсем-совсем. Поэтому он, как все чистые интерпретаторы, очень медленный: ему каждую переменную приходится искать в памяти ПО ИМЕНИ, в худшем случае за O(log(N)). К другим реализациям питона это не относится. В частности, PyPy - это JIT, и на тестах арифметики он по производительности ПРЕВОСХОДИТ Java, иногда процентов на двадцать. Во многом именно за счет того, что счет ссылок, в отличие от GC, поддается частичному развертыванию на compile-time (см., например, Ахо, Сети, Ульман, "Компиляторы: принципы, технологии, инструменты", главы 7 и 12 по 2-му изданию).

Что касается NumPy для вычислений, то NumPy даже в CPython работает с максимальной возможной скоростью, ибо написан на Си, местами с оптимизацией на ассемблере. Он быстрее даже фортрана.

[identity profile] pphantom.livejournal.com 2015-01-29 04:32 pm (UTC)(link)
А можно увидеть пример ситуации, когда NumPy обгоняет Фортран?

[identity profile] agalakhov.livejournal.com 2015-01-29 08:43 pm (UTC)(link)
Запросто. numpy.linalg.eig для действительно большой матрицы. Тогда питонный код будет заниматься только чтением исходных данных и выводом результата, т.е. на время работы почти не повлияет, и получится просто соревнование между компилятором Фортрана и компилятором Си. Лет десять назад победил бы фортран, но на современных компиляторах с оптимизацией под суперскалярный процессор фортран и си либо вообще равны (и генерируют почти идентичный машинный код), либо си чуть быстрее (в силу большей свободы оптимизации, которую стандарт си дает по сравнению со стандартом фортрана).

(no subject)

[identity profile] pphantom.livejournal.com - 2015-01-29 21:32 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-29 22:36 (UTC) - Expand

(no subject)

[identity profile] pphantom.livejournal.com - 2015-01-30 12:29 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-30 13:07 (UTC) - Expand

(no subject)

[identity profile] pphantom.livejournal.com - 2015-01-30 17:22 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-30 18:09 (UTC) - Expand

[identity profile] agalakhov.livejournal.com 2015-01-29 08:56 pm (UTC)(link)
На самом деле примерно с тем же успехом можно сравнивать разные реализации BLAS - чисто фортранную и ATLAS, например. Когда быстродействие сводится к библиотекам, написанным на компилируемых в машинный код языках, побеждает та, которая оптимизирована лучше. От языка программирования это зависит постольку, поскольку стандарт языка ограничивает допустимую оптимизацию (например, стандарт фортрана запрещает повышать точность вычислений сверх указанной, даже если это приводит к повышению скорости). Оптимизаторы в современных компиляторах общие для всех языков, после стадии промежуточного кода компилятор "забывает", с какого языка компилирует.

Кстати, современные JIT-движки генерируют код почти такого же качества, как простые компиляторы. Поэтому PyPy такой быстрый. Он работает на том же LLVM, что и clang, и все его тормоза связаны исключительно с рантаймом питона (динамические аллокаторы, приведения типов и т.д.). На чисто числодробильном коде отставание от си и фортрана становится несущественным, хоть и все еще заметным.

(no subject)

[identity profile] pphantom.livejournal.com - 2015-01-29 21:38 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-29 22:20 (UTC) - Expand

(no subject)

[identity profile] pphantom.livejournal.com - 2015-01-30 12:28 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-30 14:00 (UTC) - Expand

(no subject)

[identity profile] pphantom.livejournal.com - 2015-01-30 17:24 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-30 17:52 (UTC) - Expand

(no subject)

[identity profile] agalakhov.livejournal.com - 2015-01-30 14:21 (UTC) - Expand