LINUX.ORG.RU

Парсер JSON, написанный на D, стал самым быстрым парсером JSON в мире

 ,


7

9

Долго время производительность JSON-парсера на D оставляла желать лучшего. Однако в последнее время ситуация начала меняться. На смену устаревшему парсеру std.json пришел новый экспериментальный парсер stdx.data.json, нацеленный на включение в Phobos. Однако несколько дней назад вышел релиз нового экспериментального парсера fast, который не только обошел все другие реализации, но и сделал парсер JSON на D самым быстрым парсером в мире, обгоняя парсер на Python в более чем 6 раз по памяти и в 14 раз по скорости. Ниже приведены замеры его производительности.

Language 	Time,s 	Memory, Mb
D Gdc Fast 	0.34 	226.7
C++ Rapid 	0.79 	687.1
C++ Gason 	0.83 	582.2
Rust 	 	1.26 	234.7
Crystal Schema 	1.62 	293.2
Crystal 	2.59 	1061.4
Crystal Pull 	2.70 	1.2
Nim Clang 	3.30 	1280.3
Nim Gcc 	3.57 	1284.0
Python Pypy 	4.99 	1365.4
C++ LibJson 	5.49 	2796.3
Go 	 	6.07 	479.4
Ruby YAJL 	8.23 	1085.5
Python 		9.85 	1409.1

>>> Подробности

★★

Проверено: JB ()
Последнее исправление: Wizard_ (всего исправлений: 9)

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

alt-x ★★★★★
()
Ответ на: комментарий от FRWHate

Я тут чисто из любопытства потыкал – там есть вставки типа

@nogc nothrow
void fixedStringCompareSSE4()
...
p.vpcmpistri!(char, key[16 * i .. 16 * i + 16], Operation.equalElem, Polarity.negateValid);

Word allMasks = or(missMask, or(bsMask, dqMask));
		uint skip = bsf((__builtin_ia32_pmovmskb128(allMasks) | 1 << Word.sizeof) >> strOff);
...
// Calculate SSE word boundary before 'str'
	uint off = cast(uint) str.ptr % Word.sizeof;
	// SSE aligned pointer <= 'str.ptr'.
	auto strPtr = cast(const(Word)*) (str.ptr - off);
Что из этого можно сделать на Го?

anonymous
()
Ответ на: комментарий от anonymous

Где Сишные парсеры?

Рискну предположить, что они в жопе т.к. они то UTF-8 не умеют, то на escape подпоследовательностях падают.

Xroft ★★
() автор топика
Ответ на: комментарий от FRWHate

Ты наверно имел ввиду D? Ибо Go в бенче рвут кому не лень

пардон... про своё подумал когда писал :) конечно D :)

hoopoe ★★
()

Для интереса написал тест для Java (jackson-2.6.1).

Исходник:

public class TestJsonGenerator {

    File f = new File("test.json");

    @Before
    public void generate() throws Exception {

        List<Entry> lst = new ArrayList<>(1000000);
        Random rnd = new Random();

        for (int i = 0; i < 1000000; i++) {
            lst.add(new Entry(
                    rnd.nextDouble(),
                    rnd.nextDouble(),
                    rnd.nextDouble(),
                    UUID.randomUUID().toString(),
                    map("1", asList(1, true))));
        }

        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(f))) {
            new ObjectMapper().writeValue(out, new Data(lst));
        }
    }

    @Test
    public void load() throws Exception {

        long millisTaken;
        try (InputStream in = new BufferedInputStream(new FileInputStream(f))) {
            long startTime = System.currentTimeMillis();
            Data d = new ObjectMapper().readValue(in, Data.class);
            millisTaken = System.currentTimeMillis() - startTime;

            System.out.printf("Time taken to parse %d elements %d ms.", d.data.size(), millisTaken);
        }


    }

    public static class Data {

        private List<Entry> data;

        public Data() {}

        public Data(List<Entry> data) {
            this.data = data;
        }

        //region get set

        public List<Entry> getData() {
            return data;
        }

        public void setData(List<Entry> data) {
            this.data = data;
        }

        //endregion

    }

    public static class Entry {

        private double x, y, z;

        private String name;

        private Map<String, List<?>> opts;

        public Entry() {}

        public Entry(double x, double y, double z, String name, Map<String, List<?>> opts) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.name = name;
            this.opts = opts;
        }

        //region get set

        public double getX() {
            return x;
        }

        public void setX(double x) {
            this.x = x;
        }

        public double getY() {
            return y;
        }

        public void setY(double y) {
            this.y = y;
        }

        public double getZ() {
            return z;
        }

        public void setZ(double z) {
            this.z = z;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Map<String, List<?>> getOpts() {
            return opts;
        }

        public void setOpts(Map<String, List<?>> opts) {
            this.opts = opts;
        }

        //endregion
    }

}

Результаты:

Time taken to parse 1000000 elements 3665 ms.
C:\Users\Nagakhl>java -version
java version «1.8.0_51»
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
Nagwal ★★★★
()
Ответ на: комментарий от Nagwal

Ради интереса попробовал запустить не по одному разу, дать jit-у разогреться.

@Test
    public void load() throws Exception {

        List<Pair<Long, Integer>> times = new ArrayList<>();

        for (int i = 0; i < 1000; i++) {
            long millisTaken;
            try (InputStream in = new BufferedInputStream(new FileInputStream(f))) {
                long startTime = System.currentTimeMillis();
                Data d = new ObjectMapper().readValue(in, Data.class);
                millisTaken = System.currentTimeMillis() - startTime;

                times.add(new ImmutablePair<>(millisTaken, d.data.size()));
            }
        }

        LongSummaryStatistics statistics = times.stream().collect(Collectors.summarizingLong(Pair::getLeft));

        System.out.printf("Average time taken to parse %d * 1000 elements %f ms. Min: %d ms, max: %d ms", times.get(0).getRight(), statistics.getAverage(), statistics.getMin(), statistics.getMax());

    }
Average time taken to parse 1000000 * 1000 elements 2905.770000 ms. Min: 2243 ms, max: 6308 ms
Nagwal ★★★★
()

JSON уродлив. JSON не поддерживает бинарные строки. Приходится рекурсивно кодировать в base64, а потом декодировать. Ужас.

anonymous
()

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

anonymous
()

Зачем сравнивать D и все остальное? И почему нет замеров других (старых) парсеров?

qI__Ip
()

обгоняя парсер на Python

это типа троллинг Python'а? Или имеется в виду сравнение самой быстрой реализации на скриптовом языке по сравнению с реализацией на компилируемом языке? В любом случае троллинг уныл

Sahas ★★★★☆
()

Нет си, зато есть какие-то crystal и nim. Замеры волшебных сусликов в вакууме

makoven ★★★★★
()

Самый быстрый в мире ретард-хипстер на костылях.

anonymous
()
Ответ на: комментарий от makoven

А какой формат конфигов одновременно человекочитаем и поддерживает бинарники?

JSON одновременно ни то, ни другое xD

anonymous
()
Ответ на: комментарий от anonymous

Ты думаешь что JSON парсер пайтона написан на пайтоне?

Нет, он он вынужден создавать питоновские значения на выходе. А это оверхед.

anonymous
()
Ответ на: комментарий от anonymous

А это оверхед.

Не такой уж и значительный

я без понятия, что там внутри питоновского парсера, моё внимание привлёк сам факт сравнения именно с питоном. Почему не с Go и C++? Это языки одного класса с D

Sahas ★★★★☆
()

Имхо, заголовок не к месту. Это не самый быстрый парсер на D, это просто быстрый парсер. Если его переписать на С/С++ будет еще быстрее, так как там от D рожки да ножки: 1) @forceinline @nogc nothrow pure (плюсовики поймут) 2) куча SIMD 3) куча asm 4) ручная работа с памятью, в обход средств D

Вон есть xml парсер на asm который в разы уделывает все другие.

anonymous
()

Из этого теста можно сделать вывод что будущее за Python, ибо ресурсы ничто, время написания все!

anonymous
()
Ответ на: комментарий от anonymous

Очередной свидетель бога INI. В реальных проектах нужны и масивы и вложенность на пару-тройку уровней. Конечно всегда можно рас******сить пару уровней вложенности на файловую систему, как в systemd

makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от anonymous

Не такой уж и значительный

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

anonymous
()
Ответ на: комментарий от anonymous

JSON это объекты JavaScript в чистом виде, не умеет JavaScript не умеет и JSON вестимо.

uin ★★★
()
Ответ на: комментарий от makoven

А какой формат конфигов одновременно человекочитаем и поддерживает бинарники?

[толстота]Конфиги systemd![/толстота]

Gonzo ★★★★★
()

парсер на Python

Парсер на Python действительно написан на Python или все-таки на C, а из питона вызывается через биндинги?

slonopotamus
()
Ответ на: комментарий от Deleted

Просто ты таблицу не посмотрел.

посмотрел. Вижу, что на первых местах стоят (видимо) компилируемые языки. И что?

Sahas ★★★★☆
()
Ответ на: комментарий от anonymous

Да при чем тут вообще го? Заладили все го, го, го, го... Го вообще никогда и не заявлялся как системный/низкоуровневый язык. Вот и сравнивайте свои д со своими рустами...

Indexator ★★★
()
Ответ на: комментарий от makoven

Очередной свидетель бога INI.

Причём тут это говно, дурашка? я про него и слова ни сказал

anonymous
()
Ответ на: комментарий от Sahas

я без понятия, что там внутри питоновского парсера, моё внимание привлёк сам факт сравнения именно с питоном

Согласен, надо было про С++ написать лучше. Я просто тут хотел подчеркнуть крайне большой разрыв с динамически типизируемым языком. D лишь немногим сложнее Python, зато выигрыш в скорости и потреблении памяти может быть в 10-15 раз.

Xroft ★★
() автор топика
Ответ на: комментарий от makoven

Вообще ini умеет во вложенность. А вот со списками да, печалька. Хотя можно извернуться и забирать только значения в определённой секции.

Все очень зависит от проекта.

arcanis ★★★★
()
Ответ на: комментарий от Sahas

D обогнал Срр, Го, Раст. Сравнили. Если ты про само предложение, где идет сравнение с питоном, то это вброса ради же) сам посмотри на эффект.

Deleted
()
Ответ на: комментарий от arcanis

Как по мне понятнее yaml ничего нет. Авторы пакетного менеджера dub за каким-то чертом json в нем заменили даже не на ini, а на богом забитый негуглимый формат sdl. Теперь dub стало пользоваться просто невыносимо. Сейчас думаю над тем, чтобы dub форкнуть и на yaml его перевести.

Xroft ★★
() автор топика
Ответ на: комментарий от Deleted

D обогнал Срр, Го, Раст.

Все знают, что их обгоняет С, а С в свою очередь в два раза обгоняет Java.

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

anonymous
()
Ответ на: комментарий от anonymous

Все знают, что их обгоняет С

Да что вы говорите! Ради интереса сравните Си парсеры с самым быстрым парсером C++ Rapid. Единственный Си парсер обходящий C++ Rapid на столько бажный и может работать только в таких стирильных условиях, что пользоваться им в реальных проектах, а не тестах просто невозможно.

Xroft ★★
() автор топика
Ответ на: комментарий от Deleted

S-expr

таки не ясно, зачем оно в конфиг-файле, например
конфиг файл для админов же

anonymous
()
Ответ на: комментарий от anonymous

подобные бенчмарки должны проводиться незаангажированными людьми

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

По существу - насколько я понял, результат не вполне корректен, так как для fast использовался режим исполнения без проверки невалидного UTF-8 (и с неполной поддержкой float типов), т.е. эта скорость достигается в ущерб общему соответствию спецификации.

В остальном - честная хорошая работа.

Dicebot
()
Ответ на: комментарий от Xroft

Да что вы говорите!

Это был тонкий сарказм, вероятно слишком тонкий.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.