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

    +73

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    GZIPOutputStream out = new GZIPOutputStream(out) {
        {
            def.setLevel(Deflater.BEST_COMPRESSION);
        }
    };

    Вот так можно выставить максимальную степень сжатия GZIP-потока в жабе.

    Запостил: roman-kashitsyn, 13 Декабря 2013

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

    • И всё равно не работает, т.к. гениальный GZIPOutputStream хардкодит compression method в заголовке. В общем, жабофейспалм.
      Ответить
      • у gzip'а высокая компрессия имеет очень низкую эффективность: может быть только один-два дополнительных процентов компрессии, но в два-четыре раза медленее.

        если тебя не смущает такое время работы, то лучше поковырять bzip2 или lzma.
        Ответить
        • На самом деле я собираю deb пакет в жабе, а debian policy manual требует best compression для всех man-страничек. В общем, вот:
          https://github.com/tcurdt/jdeb/pull/139
          Ворнинги я не люблю, а время сжатия килобайтной странички значения не имеет.
          Ответить
          • > На самом деле я собираю deb пакет в жабе

            ...what?

            мне казалось что в дебиане уже все тулзы для этого есть - просто нужно специально подготовленый каталог какой-то тулзе скормить, и она из него сделает деб?

            > Ворнинги я не люблю, а время сжатия килобайтной странички значения не имеет.

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

            не вижу никаких вариантов - http://www.debian.org/doc/debian-policy/ch-docs.html - и ZGIPOutputStream стандартный класс, что гарантирует что ничего лучше ты не найдешь (апачев точно так же туп).

            другими словами: пытаться запустить внешний gzip/gzip.exe на маны, если оный отсутствует, то жать штатным стримом и терпеть варнинги линтиана.
            Ответить
            • > в дебиане уже все тулзы для этого есть
              я maven пытаюсь прикрутить, в деб-пакеты хочу включать зависимые библиотеки. А о зависимостях лучше всех maven-у и известно, почему бы им и не собирать? К тому же, работает на порядок быстрее, чем сонм перловых тулов, проверено.
              Ответить
              • это я написал до того как почитал что ты делаешь.

                не DD, но вот что нагуглил случайно:

                http://lintian.debian.org/manual/section-2.4.html

                не решение, но может воркараунд.
                Ответить
              • к слову. в
                http://www.debian.org/doc/debian-policy/ch-docs.html
                стоит "should" а не "must". линтиан будет жаловатся работа у него такая. но вроде бы не -9 компрессия не противоречит полиси.
                Ответить
                • Он всё равно подло выдаёт error, а не warning. Это не принципиально, переживу, но осадок остался.
                  Ответить
            • deb builder вроде или как он там. Пишется почти как Borland C++ debuilder .
              Ответить
        • > то лучше поковырять bzip2 или lzma.
          Для HTTP они не годятся, как и для огромного количества других вещей где юзается zlib.
          Ответить
          • Для http высокой степени сжатия в принципе и не нужно, главное - ужать повторяющийся текст из выхлопа cms, и с этим он, вобщем-то, справляется.
            Ответить
            • >http высокой степени сжатия в принципе и не нужно
              Прям так и не нужно. Это только по мнению дебилов ничего более современного не нужно.
              А гугл вон изобретает новые алгоритмы для http сжатия.
              И с других мест тоже звучат предложения: жать не только тело, но и заголовки.
              Ответить
              • Пидорва, не забывай только, что с bzip2 ты влегкую получишь DoS просто от наплыва клиентов. А гугл их наверно _изобретает_, раз существующие не подходят?
                Ответить
                • Отдавай статику предварительно пожатую.
                  Ответить
                • Школьнику не понять разницу между _изобрести_ (давным-давно уже изобретены более эффективные и быстрые чем gzip алгоритмы) и _внедрить_ в инфраструктуру - написать спецификацию, принять стандарт, заставить все популярные браузеры поддерживать сиё.

                  SDCH использует дельта-кодирование (идее которого не менее 20 лет) VCDIFF.
                  Ответить
                  • А тебе не приходило в голову, что тяжелые алгоритмы для каждого запроса не нужны, седомудый?
                    Ответить
                    • Тонко-ю.
                      Ответить
                    • >>давным-давно уже изобретены более эффективные и быстрые чем gzip алгоритмы
                      Повторяю еще раз - проблема в стандартизации, внедрении и патентах.
                      Ответить
                      • Ну и расскажи, где используются алгоритмы лучше гзипа и какой от них профит.
                        Ответить
                        • гугли snappy leveldb cassandra quic
                          Ответить
                          • А можно кратко про эти базворды?
                            Ответить
                            • Snappy - шустрый гугловый алгоритм сжатия, шустрее гзипа, но, быть может, с меньшей степенью сжатия. Используется в базах данных leveldb и cassandra, а также в очередном гуглопротоколе qiuc.
                              Оказывается, сжимать данные в снэпи при записи на диск, и читать их, распаковывая на лету, в среднем раза в полтора быстрее, чем читать/писать непожатые данные.
                              Ответить
                              • читать данные с диска, распаковывая и засылая большому брату на лету, в среднем в полтора раза быстрее, чем можно было бы себе вообразить

                                интересно, кстати
                                а можно прикрутить вообще на уровне файловой системы? заявлено 500МБ/с декомпресс, а это в принципе, на уровне современного ссд без рейда
                                Ответить
                                • > а можно прикрутить вообще на уровне файловой системы?
                                  Ну если запретить открытие файла на read+write и смириться с тормозами seek - скорее всего можно. Чтение целиком и запись целиком будут шустрыми.
                                  Ответить
                              • > Оказывается, сжимать данные в снэпи при записи на диск, и читать их, распаковывая на лету, в среднем раза в полтора быстрее, чем читать/писать непожатые данные.
                                И все тестят скорость на throughput, а не на latency. Сраные SSD и райд контроллеры с батарейками развратили людей ;(

                                Тем временем в линухах до сих пор живет знаменитый баг 12309 - когда все процессы, требующие i/o (в особенности записи) встают колом секунд на 5-10, во время копирования больших файлов...
                                Ответить
                                • > встают колом секунд на 5-10, во время копирования больших файлов...
                                  Это только на флешках вроде? А вообще даже на винде эта проблема есть. Особенно хорошо видна когда торрентов упорото много назапустил. Поднимай приоритет процессу не поднимай, а толку ноль. На ио приоритет не как не влияет и высокоприоритетные задачи будут стоять на ио подгрузки страниц памяти или других данных- просто колом.
                                  Ответить
                                  • > Это только на флешках вроде?
                                    Да хер там. Когда копируешь что-нибудь в районе 20 гиг на HDD или создаешь файлик подобного размера, возникает такое ощущение, что это не 4 ядерная зверюга с 16 гигами памяти, а сраное говнище из 90х годов ;( Типичный лаг в пределах 0.25-0.75с со всплесками до 5-10.

                                    Вот с торрентами, кстати, проблем не замечал. У меня траблы начинаются вроде как именно при непрерывных потоках в районе 100+ мегабайт в секунду.

                                    Планировщики пробовал потюнить, ну снижается лаг, но все равно терпеть невозможно. В винде такого не замечал :(
                                    Ответить
                                  • А что на винде? Я лично проблемы замечал только когда мюторрент выделял место на диске через usb 1.1 (1 метр/сек) и пытался полностью перезаписать файл.
                                    Ответить
                                • На ссд latency на запись отвратительный
                                  Ответить
                                • "подожди сынок, я только дискету доформатирую"
                                  Ответить
                                  • Windows 95 до OSR 2 на время форматирования дискеты годно уходила в себя. В DOS уходила она, в реальный режим процессора для вызова прерываний BIOS int13h в своих драйверах. А путь туда и обратно был очень не быстрый. Через контроллер то клавиатуры. :D
                                    Ответить
                                    • windows 9x. Сравнил, блин. Ты еще блюскрин при извлечении сидюхи вспомни. Что ньюфаги не помнят - тогда перезагрузиться не было проблемой.
                                      Ответить
                                • Что, до сих пор? Вот это линукскапец++ линукскапец++ линукскапец++
                                  Ответить
                              • > Оказывается, сжимать данные в снэпи при записи на диск, и читать их, распаковывая на лету, в среднем раза в полтора быстрее, чем читать/писать непожатые данные.

                                А Тарас то со своим силироном 600 мгц не знал!
                                А ещё говорили, что не гугл сейчас является движущей силой прогресса и придумывает алгоритмы.
                                Ответить
                                • Я ещё не забыл говно мамонта с частотой < 100 МГц. Даже на нём сжимать и распаковывать на лету было выгоднее, чем читать/писать несжатые данные, потому что в ту эпоху дисковые накопители были жутко медленными.

                                  Хотя всё зависит от алгоритма сжатия.
                                  Ответить
                        • http://www.maximumcompression.com/data/summary_mf2.php
                          Ответить
      • То есть, сжимает с одной степенью, а в заголовке пишет другой? Оригинально!
        Ответить
      • > хардкодит compression method в заголовке
        Что-то я не понимаю, в чем жабофейспалм... compression method харкодится потому, что в rfc на gzip описан только deflate: (http://www.gzip.org/zlib/rfc-gzip.html), другие методы GZIPOutputStream не поддерживает, да и их, вроде бы, никто еще и не придумал... Поэтому с хардкодом deflate'а, имхо, все нормально.

        И в заголовках gzip'а и deflate'а никуда не пишется степень сжатия. Видимо lintian тупо пытается распаковать и переупаковать файл на -9, и если у него получилось меньше, то матерится?
        Ответить
        • UPD: lintian смотрит, есть ли слово 'max compression' в выхлопе file -e ascii <имя файла>
          Ответить
          • UPD: покопался в magic'ах и нашел в RFC вот это:
            XFL (eXtra FLags)
            These flags are available for use by specific compression methods. The "deflate" method (CM = 8) sets these flags as follows:
            XFL = 2 - compressor used maximum compression, slowest algorithm
            XFL = 4 - compressor used fastest algorithm


            Собственно их GZIPOutputStream и хардкодит в нолик. Как вариант - можно скопировать исходник GZIPOutputStream'а себе в проект и добавить кошерную поддержку уровней. Жаль что в апстрим эту пару строчек всяко не пропихнуть ;(
            Ответить
            • UPD: Ну или со зловещим смехом подло поправить восьмой байт пожатого файла с нолика на двойку. Линтиан будет удовлетворен, а всем остальным и так похрен на эти 100 байт, которые сэкономит максимальное сжатие :)
              Ответить
            • А что такое тогда степень сжатия (0-9)?
              Ответить
              • Я точно не знаю, но оно по идее влияет или на размер словаря, или на дальность поиска одинаковых строк, или на что-то еще. При упаковке это влияет на время\к-т сжатия. А распаковщик никакой разницы не заметит, т.к. тупо декодирует хаффмана да копирует куски из уже распакованных данных. Поэтому записывать эту цифру в хедер нет смысла.
                Ответить
        • Спасибо за исследование, я уже тоже догнал, что не cm виноват, но идея в целом была верна. С копированием файла странная ситуация - сорцы в openjdk и oraclejdk практически идентичные, но лицензии несовместимы - гну и прориетарщина. И ни одна не подходит под apache...
          Ответить
          • Ну тогда можно переписать вслепую ;) Deflate уже есть, остается посчитать crc32 да записать header и trailer.

            P.S. Если бы writeHeader не был приватным - пофиксить было бы легко ;(
            Ответить
            • >P.S. Если бы writeHeader не был приватным - пофиксить было бы легко ;(
              На похожую парашу наткнулся, когда мутил какой-то binaryoutputstream, там нельзя было что-то перезаписать.
              Ответить
          • А как тебе вот такой грязный хак?
            class GZIPOutputStream extends java.util.zip.GZIPOutputStream
            {
                private boolean constructed = false;
            
                public GZIPOutputStream(OutputStream out, int level) throws IOException {
                    super(out); // write() in writeHeader() suppressed by constructed = false
                    constructed = true;
                    def.setLevel(level);
                    writeCorrectHeader(level);
                    crc.reset();
                }
            
                private void writeCorrectHeader(int level) {
                    byte xflags = 0;
                    if (level == Deflater.BEST_SPEED)
                        xflags = 4;
                    if (level == Deflater.BEST_COMPRESSION)
                        xflags = 2;
                    private final static byte[] header = {
                        (byte) GZIP_MAGIC,                // Magic number (short)
                        (byte)(GZIP_MAGIC >> 8),          // Magic number (short)
                        Deflater.DEFLATED,                // Compression method (CM)
                        0,                                // Flags (FLG)
                        0,                                // Modification time MTIME (int)
                        0,                                // Modification time MTIME (int)
                        0,                                // Modification time MTIME (int)
                        0,                                // Modification time MTIME (int)
                        xflags,                           // Extra flags (XFLG)
                        0                                 // Operating system (OS)
                    };
                    out.write(header);    
                }
            
                public synchronized void write(byte[] buf, int off, int len)
                     throws IOException
                {
                    if (constructed)
                        super.write(buf, off, len);
                }
            }
            Ответить
            • я придумал ещё более грязных хак, уже отписался в пулл-реквесте :)
              У меня страничка пишется в in-memory, я решил постфактум флажок в массиве байт подхачить.
              Ответить
              • Собственно, @bormand уже это предлагал
                http://govnokod.ru/14221#comment204343
                Ответить
              • > У меня страничка пишется в in-memory
                Хм, а почему? Ну не только ради этого хака же?
                Ответить
                • нет, там сразу стримы в архив записываются, зачем временный файл создавать
                  Ответить
                  • Но тогда зачем в память? Или либа, которой ты жмешь в .tar.gz принимает именно блобы в byte[], а не дает какой-нибудь OutputStream, в который можно писать данные?
                    Ответить
                    • api такое, что нужно размер entry заранее указать
                      Ответить
                      • Ну тогда понятно.

                        Можно упаковать два раза - первый раз в стрим, который только считает байты, второй раз - куда надо.
                        Ответить
                        • у InputStream нет метода unsee()
                          Ответить
                          • Зато в крестах там есть unget()
                            Ответить
                            • но он позволяет развидеть совсем немного - лишь последний символ :(
                              Ответить
                              • А циклы на что?
                                Ответить
                                • Some library implementations may support this function to be called multiple times, making the characters available in the reverse order in which they were put back. Although this behavior has no standard portability guarantees, and further calls may simply fail after any number of calls beyond the first.

                                  Короче говоря, не во всех реализациях можно возвращать в поток более 1 символа.
                                  Ответить
    • Для тех кто не умеет готовить жаб поясните рецепт. Если бы был один {...}, то я бы подумал что это изменение свойств во время конструирования как в сишарпике. Может какой-то список инициализации странный?
      Ответить
      • Скобки вида new T() { ... } объявляют создание объекта анонимного подкласса Т (или анонимную реализацию интерфейса T). Скобки внутри этих скобок - блок инициализации, код, который выполняется в конструкторе.
        Ответить
        • Красиво.
          Ответить
          • Codec.Compression.GZip.compressWith defaultCompressParams {
               compressLevel = bestCompression 
            }

            Не пойму что не нравится Роману.
            Ответить
            • поясните мысль
              Ответить
              • Это я не въеду в чем же говно?!

                >хардкодит compression method в заголовке
                Есть DeflaterOutputStream, который ничего не пишет в заголовок и имеет подходящий конструктор:
                public DeflaterOutputStream(OutputStream out, Deflater def)
                Ответить
    • Сори за офтоп, но поговаривают, что Американская Мечта провалилась, это правда?
      Ответить
    • http://svn.apache.org/repos/asf/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/compressors/gzip/GzipCompressorOutputStream.java
      Свершилось. В commons-compress в релизе 1.7 можно будет указать степень сжатия. Ждём.
      Ответить

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