1. Java / Говнокод #13940

    +171

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    public abstract class BaseDateTime
            extends AbstractDateTime
            implements ReadableDateTime, Serializable {
    
        /** The millis from 1970-01-01T00:00:00Z */
        private volatile long iMillis;
        /** The chronology to use */
        private volatile Chronology iChronology;
    
    /////////////////////////////////////////////////////////////////	
    /*	
     * DateTime is thread-safe and immutable, provided that the Chronology is as well.
     * All standard Chronology classes supplied are thread-safe and immutable.
     *
     * @see MutableDateTime
     */
    public final class DateTime
            extends BaseDateTime

    Любителям joda-time.
    Cмущает меня этот volatile, который приходит в немутабельный класс от родителя.

    Запостил: 3.14159265, 14 Октября 2013

    Комментарии (73) RSS

    • "BaseDateTime subclasses may be mutable and not thread-safe." Видимо, имелось в виду "mutable and/or not thread-safe".
      Ответить
      • Понятно что имелось ввиду.
        Но я говорю что в мутабельном наследнике надо было volatile-поля сделать, а в немутабельном - final.
        Ведь доступ идёт по геттерам через интерфейс.
        Ответить
        • http://joda-interest.219941.n2.nabble.com/BaseDateTime-volatile-fields-td7572486.html
          Ответить
          • А я о чем говорю?
            Ответить
            • Согласен, говно в изначальном дизайне, который не могут пофиксить без потери совместимости. Там у них был еще и final с записью через рефлексию. https://github.com/JodaOrg/joda-time/commit/67f1a30fc0fceda751b6347b2b16a5081878ac1e
              Ответить
              • Потому пост и озаглавлен "Любителям joda-time".
                Я раньше его редко использовал. Но вот теперь смотрю пристально, а тут хуйни всякой хватает.
                new DateTime(null) - не кидает NPE, а создает DateTime.now().
                Кстати аргумент Object (а доступны Date, Calendar и String) - можно и проебать. Зачем убирать типобезопасность - неясно. Это не жс, не пхп и не питон.

                Ну и совсем классика типа
                if (iOffsetParsed == true) {
                return this;
                }
                Ответить
              • Но с другой стороны, несмотря на обилие копипасты местами радует глаза байтоебство:
                if (value < 10) {
                            for (; size > 1; size--) {
                                out.write('0');
                            }
                            out.write(value + '0');
                        } else if (value < 100) {
                            for (; size > 2; size--) {
                                out.write('0');
                            }
                            // Calculate value div/mod by 10 without using two expensive
                            // division operations. (2 ^ 27) / 10 = 13421772. Add one to
                            // value to correct rounding error.
                            int d = ((value + 1) * 13421772) >> 27;
                            out.write(d + '0');
                            // Append remainder by calculating (value - d * 10).
                            out.write(value - (d << 3) - (d << 1) + '0');
                        } else {
                            int digits;
                            if (value < 1000) {
                                digits = 3;
                            } else if (value < 10000) {
                                digits = 4;
                            } else {
                                digits = (int)(Math.log(value) / LOG_10) + 1;
                            }
                            for (; size > digits; size--) {
                                out.write('0');
                            }
                            out.write(Integer.toString(value));
                        }

                Там проёбут, тут экономят. Авторы, то разные!
                Ответить
    • Не знаю кто тут анонимные минусаторы и эксперты в жабе, но дальнейшие поиски привели меня к тесту c закономерным результатом - производительность от volatile снизилась более чем в 15 раз!
      http://sourceforge.net/p/joda-time/bugs/153/
      Ответить
      • Volatile в жабе и шарпе одинаковые команды?

        я всегда предпочитаю лочить или интерлок юзать
        Ответить
        • >>я всегда предпочитаю лочить или интерлок юзать
          >DateTime is thread-safe and immutable
          А в хацкеле поцоны как-то обходятся.
          Ответить
          • > thread-safe and immutable
            А разве immutable может быть не threadsafe? Ну кроме случаев с утечкой this из конструктора.
            Ответить
            • Нет. Написали для тех кому это неочевидно.
              >кроме случаев с утечкой this из конструктора
              Ради прикола конечно можно соорудить такой объект.
              Ответить
            • Как зис может утечь и к чему это приведет?
              Ответить
              • Насколько помню, в джаве, в отличие от крестов, final-члены классов (в крестах их аналог - const, но суть не меняется) можно инициализровать в любом месте конструктора (в крестах только в специальном initialization list). Так что если передать this в какую-нибудь функцию до присвоения этих final, то эта функция увидит null вместо значений, которые присваиваются позднее. Так что final окажется не таким уж final.
                Ответить
                • наверное не нул, а поля недоинициализированные?
                  Ответить
                  • Ну джава-то не C++ с его UB, значение по умолчанию тут null (для ссылочных типов). Для остальных типов это будет нуль или пустая строка.
                    Ответить
              • Вот как-то так:
                class Test {
                    public static Test instance = null;
                    final int x;
                    public Test() {
                        instance = this;
                        x = long_calculation();
                    }
                    int long_calculation() {
                        System.out.println("Sleeping...");
                        try { Thread.sleep(1000); } catch (Exception e) {}
                        System.out.println("Sleeping done");
                        return 42;
                    }
                
                    public static void main(String[] args) {
                        Thread t = new Thread(new Runnable() {
                            public void run() {
                                Test test = new Test();
                            }
                        });
                        t.start();
                        System.out.println("Waiting... ");
                        while (Test.instance == null);
                        System.out.println("Gotcha! " + Test.instance.x);
                        try { Thread.sleep(2000); } catch (Exception e) {}
                        System.out.println("Changed?! " + Test.instance.x);
                    }
                }
                final становится совсем не final ;)
                Ответить
          • НУ так в функциональщине впринципе не нужны локи
            Ответить
            • >>в функциональщине впринципе не нужны локи
              Неужели?
              А чего так?
              Ответить
              • Все переменные доступны только на чтение. просто в принципе нельзя написать что то типа
                let x = 2
                x =3

                А если переменные нельзя менять, то и проблем к доступу к ним нет
                Ответить
            • > НУ так в функциональщине впринципе не нужны локи
              Весьма полезны для коммуникации их функцианальные аналоги - MVar-ы (коробки с последовательным доступом, по сути синхронизированные каналы без буфера). Локи иногда просто вынесены в рантайм.
              Ответить
      • Я сомневаюсь в адекватности того теста / тестера. Производительность volatile сильно зависит от архитектуры процессора. У интела чтение довольно дешевое, запись немного дороже. Потери производительности есть, но не настолько.
        Ответить
        • >что от добавления volatile заметно снизилась производительность, то ты однозначно делаешь что-то неправильно.
          > Производительность volatile сильно зависит от архитектуры процессора. У интела чтение довольно дешевое, запись немного дороже.

          Простите за хабр, но статью писал вменяемый человек.
          http://habrahabr.ru/post/143390/
          Вот тесты на интеле. Просто убираем чтение лишнего volatile. Всего-то.
          VolatileDCL	        394 ± 15	405 ± 3		402 ± 13	3977 ± 89	4576 ± 101	4620 ± 92
          VolatileCachedDCL	454 ± 8		465 ± 3		460 ± 6		4778 ± 250	4946 ± 143	5071 ± 11

          Если не знаете специфики лучше молчите.

          если у тебя программа так часто опрашивает время,
          Ответить
          • >фабрика синглтонов
            Гыгы.
            Ответить
            • Сразу же вспоминается этот линк
              http://ws.apache.org/xmlrpc/apidocs/org/apache/xmlrpc/server/RequestProcessorFactoryFactory.RequestSp ecificProcessorFactoryFactory.html
              Ответить
          • Воу, воу, палехче. Где там 15х разница? Откуда вы, кстати, взяли 15? Поделили 2995 на 169? :) Может из этого времени 95% занимал запуск VM и загрузка классов?

            Кроме того, в том тесте из багтрекера предполагается low contention использование, в такой ситуации разница в чтении volatile и обычных полей минимальна на интеле. В условиях high contention, действительно, чтение volatile может стать довольно дорогим. Запись же я вобще здесь не рассматриваю, т.к. для иммутабельного DateTime запись происходит однажды при инициализации, и она явно сильно дешевле создания самого класса.

            К слову, запустил я тест на 1.5.2 и 2.1 версиях с volatile и без volatile (i5, java 6, ubuntu 13.04 x86). В рамках одной версии разница между volatile и не-volatile около ~3% (не задрачивался с точностью, так как не суть). В то же время, разница между 1.5.2 и 2.1 огромна, 2.1 медленне 1.5.2 в ~10 раз. Насколько я понял, разница возникает из-за добавленного в 2.0 фрагмента в DateTimeZone.getOffsetFromLocal() ( http://sourceforge.net/p/joda-time/svn/1596/tree//trunk/JodaTime/src/main/java/org/joda/time/DateTimeZone.java?diff=1595 ), который вызывает дорогой метод previousTransition(), включающий Array.binarySearch() и довольно много другого кода.

            Выводы:
            1. Performance regression действительно имеет место быть при обновлении с 1.х до 2.х, но это является результатом некоего багфикса и не зависит от добавленных модификаторов volatile.
            2. Тест бестолковый и зависит от часового пояса и времени там, где он запускается.

            Такие дела.

            PS. Парочка ссылок:
            http://stackoverflow.com/a/4635571/100237
            http://beautynbits.blogspot.com/2012/11/the-cost-of-volatile.html
            https://dl.dropboxusercontent.com/u/1980587/joda-perf-test.7z (исходник теста и jar'ы)
            Ответить
            • >>Воу, воу, палехче. Где там 15х разница? Откуда вы, кстати, взяли 15?
              >>У интела чтение довольно дешевое, запись немного дороже.

              На чтении двухкратная разница.
              Ответить
              • В Нью Йорке в два раза холоднее, чем во Флориде.
                Ответить
                • Ну насчёт временных поясов вы правы. И насчёт 15 раз тоже.
                  Тем не менее, если немного модифицировать тест:
                  private static void runTestJ ()
                  	{
                  		DateTime dt = new DateTime(ISOChronology.getInstance().withUTC ());
                  		for (int i = 0; i < 10000000; i++) {
                  			dt=dt.plusDays ( 1 );
                  		}
                  	}

                  То легко заметить что volatile-версия медленее в 2-2.4 раза (i5, j7 -server =2.4 | j6 -client =2), чем обычная. Что тоже ощутимо - потеря половины скорости на ровном месте.
                  Ответить
            • >>К слову, запустил я тест на 1.5.2 и 2.1 версиях с volatile и без volatile
              Какой именно тест?

              >>Запись же я вобще здесь не рассматриваю, т.к. для иммутабельного DateTime запись происходит однажды при инициализации

              Это правильно. Вот у нас есть система где надо периодически получать объект таймстампа с текущим временем, то есть генерировать такие объекты с большой частотой. Запись рассматривать нет нужды - надо подгонять результаты под свои слова.
              Ответить
              • >Какой именно тест?
                Тест из багтрекера.

                >Вот у нас есть система где надо периодически получать объект таймстампа с текущим временем, то есть генерировать такие объекты с большой частотой.
                Конкретно речь шла о тесте, на основе которого были сделаны выводы о 15х разнице. Да и если сравнить создание объекта (плюс учесть будущие затраты на GC) и запись volatile, что будет медленнее?
                Ответить
                • Да. Скачал @ разобрался. Вы правы.
                  В слоу-версии volatile не более 3%. В старой ощутимей.
                  Ответить
            • >исходник теста и jar'ы
              Ну, во-первых, я предлагал поставить final вместо volatile.

              Запустил. Очень странно. У меня 2.1 regular работает в те же 20 раз медленее чем 2.1 volatile.
              Именно так volatile быстрее regulae. Я не путаю. Запускал трижды.
              Ответить
      • детский сад. синтетический тест это не тест.

        если у тебя программа так часто опрашивает время, что от добавления volatile заметно снизилась производительность, то ты однозначно делаешь что-то неправильно.
        Ответить
        • >>если у тебя программа так часто опрашивает время
          >>детский сад. синтетический тест это не тест.
          Конечно же.
          Безусловно надо написать тест так чтоб, например, бутылочным горлышком было чтение данных с жесткого диска и тестировать HDD, а не код.
          Вот тогда получится правильно - как код не пиши - а скорость одинаковая.
          А ВДРУГ У ПОЛЬЗОВАТЕЛЯ ЖЕСТКИЙ ДИСК МЕДЛЕННЫЙ?
          Ответить
          • А вдруг у пользователя ОЗУ медленная?
            Ответить
          • > Безусловно надо написать тест так чтоб, например, бутылочным горлышком было чтение данных с жесткого диска и тестировать HDD, а не код.

            вот именно про такой детский сад я и говорю. из крайности в крайность.

            тест нужно делать реалистичный, целое приложение/систему приложений, с нагрузкой похожей на рабочую.

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

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

            как можно догадатся, официальная интерпретация результатов теста была весьма смешной. типа все тормозит как и раньше, но так как это (по)менять нельзя, то это значит что приложение уже идеально соптимизировано.
            Ответить
    • По-моему это какая-то фигня: этот volatile не делает пару iMillis/iChronology атомарной. То есть если один тред изменяет эту пару, то другой тред сможет увидеть старый iMillis и новый iChronology (или наоборот).
      Ответить
      • А это нигде и не гарантируется.
        Ответить
        • Мьютексы не спасут разве?
          Ответить
          • Я имел в виду, что BaseDateTime не может сам по себе гарантировать атомарности и в то же время предлагая отдельные setMillis() и setChronology().
            Ответить
    • Ничего, вот выпустят JSR-310, и заживём.
      Ответить
      • Еще каких-то десять лет, еще каких-то десять лет...
        Ответить
      • Еще непонятно что в том JSR-310. Где гарантия что там проёбов не будет?
        Теперь понятно чего они отказались от joda. Хотя либа в целом годная.
        Ответить
        • А почему ты наезжаешь на Жабу? Это же любимый твой язык.
          Ответить
          • вероятно потому что адекватные люди критически смотрят даже на их любимые вещи
            Ответить
            • Любовь противоречит критическому взгляду, иначе это не любовь.
              Ответить
              • Почему? Любовь - она иррациональна. В крайнем случае можно все понимать, но продолжать любить PHP
                Ответить
          • Написал подробный коммент. crsf verification error и браузер наебнулся. Лень снова всё набирать.

            Это не мой "любимый язык". И не близко.
            Жава - уродлива во многих аспектах.
            В общем, пишем на том что сейчас больше всего востребовано.
            Возможно, к некому сожалению, востребованы сейчас сишкоблядские языки, желательно с GC.
            И пхп с крестами прельщают еще меньше
            Перестанут платить - буду "наезжать" на другие языки.
            "Любимый язык" - это обычно у детей. На сосаче таких полно.
            Ответить
            • А какой язык самый няшный на твой взгляд? Интересно мнение программиста с опытом
              Ответить
              • > мнение программиста с опытом
                Няшных языков нет. Есть только языки, которые позволяют решать конкретный класс задач быстрее и надежней чем другие ;)
                Ответить

    Добавить комментарий