понедельник, 23 мая 2022 г.

Amazon такой Amazon



Изучая установку сервера minecraft, наткнулся на дистрибут java от Amazon.

Сначала гордо пишут:

Amazon Corretto 17 is a no-cost, multiplatform, production-ready distribution of the Open Java Development Kit (OpenJDK). Corretto 17 comes with long-term support that includes performance enhancements and security fixes. Corretto 17 is certified as compatible with the Java SE standard and is used internally at Amazon for many production services.

Обещают различные улучшения и правки, а потом приводят список этих правок:

Updates to vendor-related metadata.
    Identifies Amazon as the vendor of this OpenJDK distribution and adds hyperlinks for reporting issues.
То есть просто переименовали OpenJDK и все?..



 

воскресенье, 20 декабря 2020 г.

Сравнение флотов (чисел с плавающей точкой) в NUnit

0.9999999999
vs
1.0000000001
У чисел с плавающей запятой (float, double) есть такая особенность, что из-за проблем потери точности, они могут получаться не равными, даже если должны быть такими с точки зрения математики. И это зависит не только от используемых операций, но и от архитектуры железа, а также флагов процессора, установленных в момент вычислений.
Например, 72/6 может быть не равно 3*4.
Но сравнивать их все равно нужно. Поэтому используется сравнение с определенной точностью. Берется модуль (абсолютное значение) разности этих чисел и сравнивается с небольшой константой. Если модуль разницы больше этой константы, числа считаются разными.
В библиотеке NUnit для таких сравнений есть специальные конструкции
Вместо велосипедов
Assert.IsTrue(Mathf.Abs(actualValue - expectedValue) < tolerance);
и
Assert.Less(Mathf.Abs(actualValue - expectedValue), tolerance);
В NUnit есть
Assert.AreEqual(expectedValue, actualValue, tolerance); 

пятница, 18 декабря 2020 г.

Доступная память в Linux



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

Я попробовал установить на одну и ту же машину c 2Гб памяти Debian 10, CentOS 8 и Ubuntu 20.

Такой получился объем доступной памяти по умолчанию:

# cat /proc/meminfo | grep MemTotal

Debian
MemTotal:        1993756 kB

CentOS:
MemTotal:        1811096 kB

Ubuntu:
MemTotal:        1986080 kB

На CentOS ситуацию можно улучшить, отключив kdump, который изначально включен и съедает 160 Мб.

Редактируем конфиг grub:
# vi /etc/default/grub

Меняем crashkernel=auto на crashkernel=no в GRUB_CMDLINE_LINUX

Применяем изменения:
# grub2-mkconfig -o /boot/grub2/grub.cfg

Отключаем kdump:
# systemctl disable kdump

Перезагружаемся:
# shutdown -r now

Уже лучше, но все еще хуже, чем в Debian и Ubuntu:
MemTotal:        1974584 kB

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


понедельник, 1 июня 2020 г.

Совмещение логирования и сбора метрик

Как сосчитать количество ошибок и предупреждений в сервисе?
В обертку над логгером встраиваем счетчики, собирающие телеметрию.

class Telemetry
{
	public int Warnings;
	public int Errors;
}

static class Log
{
	public static Telemetry Telemetry = new Telemetry();

	public static void Warning(string msg)
	{
		Telemetry.Warnings++;
		Console.WriteLine($"[Warning] {msg}");
} public static void Error(string msg) { Telemetry.Errors++; Console.WriteLine($"[Error] {msg}"); } }

вторник, 23 октября 2018 г.

Просто взять и сжать массив байтов в C#




К сожалению, .NET имеет API только для сжатия и распаковки потоков данных. А если нужно сжать просто массив байтов в другой массив байтов, приходится использовать внешние библиотеки. Или написать вспомогательный класс, например, такой:

using System.IO;
using System.IO.Compression;
 
namespace Compression
{
    public static class Zip
    {
        public static byte[] Compress(byte[] src)
        {
            using (var input = new MemoryStream(src))
            {
                using (var output = new MemoryStream())
                {
                    using (var compressor = new GZipStream(output, CompressionMode.Compress))
                    {
                        input.CopyTo(compressor);
                    }
                    return output.ToArray();
                }
            }
        }
 
        public static byte[] Decompress(byte[] src)
        {
            using (var input = new MemoryStream(src))
            {
                using (var decompressor = new GZipStream(input, CompressionMode.Decompress))
                {
                    using (var output = new MemoryStream())
                    {
                        decompressor.CopyTo(output);
 
                        return output.ToArray();
                    }
                }
            }
        }
    }
}
 

суббота, 17 февраля 2018 г.

Потокобезопасный Singleton на C#

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

public class Singleton
{
    private static readonly object syncObj = new object();
    private static volatile Singleton instance;

    public Singleton Shared()
    {
        if (instance == null)
        {
            lock (syncObj)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }

    private Singleton()
    {            
    }
    
    // ...
}

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

public class Singleton
{
    private static Singleton instance= null;

    public static Singleton Shared
    {
        get { return Volatile.Read(ref instance) ?? EnsureSharedCreated(); }
    }

    private static Singleton EnsureSharedCreated()
    {
        Interlocked.CompareExchange(ref instance, Create(), null);
        return instance;
    }

    public static Singleton Create()
    {
        return new Singleton();
    }
    
    private Singleton()
    {
    }
    
    // ...
}

пятница, 2 июня 2017 г.

Олимпиадное vs Промышленное программирование

Задачи

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


Время

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

Код

В промышленном программировании код пишется для дальнейшего сопровождения (то есть многократного чтения для расширения и правок). Он должен быть гибким, расширяемым. И самое главное - хорошо читаемым.
В олимпиадном программировании код пишется на один раз (write-only). Никто не собирается его в дальнейшем вычитывать, расширять или править баги.

Навыки

Главный навык в промышленном программировании - это общение. У кого-то спросить, кому-то подсказать. Пройти и провести ревью. Сделать проект лучше, чем был до внесения изменений.
В олимпиадном программировании в приоритете - алгоритмы и структуры данных. Максимально быстро и "грязно" придумать решение, уложившись в формальные требования, набрать и отладить. Все. Дальше хоть трава не расти.

Выводы

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

понедельник, 22 мая 2017 г.

FizzBuzz на Python

Программа, печатает числа от 1 до 100, но если число кратно трем вместо числа выводит "Fizz", если число кратно пяти выводит "Buzz". Если число одновременно кратно и трем и пяти - "FizzBuzz".

Python 2
for i in range(1, 101):  # последнее число исключается из цикла
    if i % 15 == 0:
        print 'FizzBuzz'
    elif i % 3 == 0:
        print 'Fizz'
    elif i % 5 == 0:
        print 'Buzz'
    else:
        print i

Если хочется немного оптимизировать, можно заменить range на xrange, тогда не будет создаваться список из исходных чисел. В python 3 range это и есть xrange.

В Python 3 нужно вызывать print как функцию
for i in range(1, 101):
    if i % 15 == 0:
        print('FizzBuzz')
    elif i % 3 == 0:
        print('Fizz')
    elif i % 5 == 0:
        print('Buzz')
    else:
        print(i)

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

for i in range(1, 101):
    s = ''
    if i % 3 == 0:
        s = 'Fizz'
    if i % 5 == 0:
        s += 'Buzz'
    if not s:
        s = str(i)
    print s

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

пятница, 19 мая 2017 г.

Метод Main() в C#

Метод Main служит для задания точки входа в программу.
Visual Studio по умолчанию создает метод Main следующего вида:
class Program
{
    static void Main(string[] args)
    {    
    }
}

Расположение

Метод Main может быть расположен в любом классе программы. Но он должен быть только один. Если их будет несколько, компилятор выдаст ошибку:
error CS0017: Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point.
То есть в такой ситуации нужно будет явно указывать метод, который будет являться точкой входа.

Область видимости

Метод является private, хотя явно это и не указано. Делается это для того, чтобы случайно не вызвать его из другого места. Хотя можно объявить его и public, все продолжит работать.

Возвращаемое значение

По-умолчанию метод Main не возвращает значения, но он может быть объявлен с возвращаемым значением типа int.
class Program
{
  static int Main(string[] args)
  {
    return 0;
  }
}

Почему static?

Метод Main должен быть статическим для того, чтобы его вызов был возможен без создания экземпляра класса, в котором он объявлен.

Аргументы

Метод Main принимает массив строк. В нем содержатся аргументы, переданные программе при запуске. Если ваша программа не обрабатывает переданные ей аргументы, можно их опустить:
class Program
{
  static int Main()
  {
    return 0;
  }
}

Самый короткий вариант не содержит аргументов, и не возвращает значения:
class Program
{
  static void Main()
  {
  }
}

четверг, 18 мая 2017 г.

Algorithms, Part I на Coursera

О курсе

На Сoursera есть хороший курс по алгоритмам и структурам данных для начинающих и повторяющих. Он на английском языке, но это не должно быть большой проблемой, так как лекции технические и основная часть слов будет знакома любому программисту.
Авторы курса Кевин Уэйн (Kevin Wayne) и Роберт Седжвик (Robert Sedgewick).

Плюсы


  • Курс бесплатен и постоянно перезапускается (его можно проходить снова и снова);
  • Все лекции можно найти также и на Youtube;
  • Много интересных практических заданий;
  • Замечательная система проверки, требующая сдавать довольно хорошо отлаженный код;
  • Для углубленного изучения материала можно взять книгу авторов.


Минусы


  • Проблемы со звуком в лекциях. Лектор часто и сильно кашляет в микрофон. Берегите уши;
  • Довольно поверхностное покрытие материала в лекциях (можно компенсировать книгой авторов);
  • Несоответствие лекций и заданий. В лекциях описываются алгоритмы и структуры данных;
  • Обещается, что мы будем реализовывать их сами. Вместо этого же приходится использовать структуры данных, написанные авторами курса для решения прикладных задач.


Вывод

Хороший курс. Рекомендую всем интересующимся программированием, алгоритмами и структурами данных.

вторник, 21 февраля 2017 г.

Python import this или дзен питона

Если в питоне написать строку import this, то в консоль будет выведен дзен питона.
По-моему, это можно считать дзеном разработки на любом языке программирования.
В оригинале оно выводится на английском, здесь я публикую его со своим переводом.

The Zen of Python, by Tim Peters
Дзен питона от Тима Питерса

Beautiful is better than ugly.
Красивое лучше уродливого.

Explicit is better than implicit.
Явное лучше неявного.

Simple is better than complex.
Простое лучше составного.

Complex is better than complicated.
Составное лучше сложного.

Flat is better than nested.
Плоское лучше вложенного.

Sparse is better than dense.
Разреженный лучше плотного.

Readability counts.
Читаемость важна.

Special cases aren't special enough to break the rules.
Особые случае недостаточно особенные для того, чтобы нарушать правила.

Although practicality beats purity.
Хотя практичность важнее чистоты.

Errors should never pass silently.
Ошибки никогда не должны происходить незаметно.

Unless explicitly silenced.
За исключением случаев намеренного подавления.

In the face of ambiguity, refuse the temptation to guess.
Перед лицом неоднозначности сопротивляйтесь искушению угадать.

There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Должен быть один, желательно только один, очевидный путь сделать это.
Хотя это может быть сначала неочевидно, если вы не голландец.

Now is better than never.
Although never is often better than *right* now.
Сейчас лучше чем никогда.
Хотя никогда часто лучше, чем прямо сейчас.

If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Если реализацию сложно объяснить, это плохая идея.
Если реализацию просто объяснить, это может быть хорошей идеей.

Namespaces are one honking great idea -- let's do more of those!
Пространства имен это просто великолепная идея - давайте делать больше пространств имен.

четверг, 26 января 2017 г.

Идеальное тестовое задание для программиста

This is not fine

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

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

Но кто же пишет такие программы, и, следовательно, хорошо справится с таким заданием?

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

Таким образом, компании набирают не тех, кто лучше будет справляться с работой, а тех, кто лучше делает тестовые задания.
Итак, каким же, по моему мнению, должно быть идеальное тестовое задание?
В идеале тестовое задание должно быть максимально похоже на реальную работу. И задание должно быть добавить фичу/исправить баг/провести рефакторинг/написать юнит-тест.
То есть это должна быть та самая программа с которой предстоит в будущем работать. И в этой системе, разумеется будет обычный букет всех реальных программ: куча багов, недоделанных и полувырезанных фич, дублирование кода и логики и т.д.
К сожалению, это, как правило, возможно лишь для open-source разработки, а также в случае наличия open-source альтернативы вашего продукта. Тогда задания можно давать на ней.
Разумным компромиссом мне кажется специальная программа (возможно, разработанная для выполнения предыдущих вариантов тестового задания), содержащая баги, недофичи, не полностью покрытая тестами. Она должна содержать хотя бы 10 тысяч строк кода.
Таким образом можно будет посмотреть на работу программиста в условиях гораздо ближе к его реальной будущей работе.