LINUX.ORG.RU

Тест java vs nodejs по жору памяти

 , , ,


1

1

java:

package test_java;

import java.util.HashMap;
import java.util.Scanner;

public class Main {

    class Table {
        Long x2;
        Long x3;
        String str;
        public Table(Long i) {
            x2 = i*2;
            x3 = i*3;
            str = x3.toString();
        }
    }
    private HashMap<Long, Table> hash = new HashMap<Long, Table>();

    public Main() {
    
    }

    private void log(String s) {
        System.out.println(s);
    }

    public void test() {
        for (long i = 0; i < 1000L*1000L; i++) {
            hash.put(i, new Table(i));
        }
        log("Generated");
        Scanner scan = new Scanner(System.in);
        scan.nextInt();
    }

    public static void main(String[] args) {
       Main m = new Main();
       m.test();
    }
}

Nodejs:
h = {}
for (i=0; i < 1000*1000; i++) h[i] = {x2:i*2, x3:i*3, str : (i*3)+""}
Жор памяти с htop после нескольких минут простоя: https://i.imgur.com/6VY2zqfl.png

Ъ: 267мб - ява, 127 нода (было ~180 сразу после запуска)

И что получается? Ява сосуна по памяти больше чем в 2 раза? (и во много раз по коду, лол) Как дальше жить? Что я не так сделал?

$ java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

$ node --version
v10.14.2
★★★★★

Последнее исправление: crutch_master (всего исправлений: 2)

Ответ на: комментарий от Legioner

Думаю можно наляпать с Uint32Array. Правда у меня дома x86.

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

Используй массивы примитивов вместо хештаблицы.

Нужен хешмап. Как я по массиву что-то искать буду?

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

Мне не надо, чтобы оно валилось от того, что памяти нет.

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

Это не long, это bigint. Предполагается, что long-и на современных процессорах очень быстрые и компактные. Твой вариант будет очень быстрым и компактным на значениях между 2^53 и 2^64? Так-то и руками нашкондыбать длинную арифметику можно было хоть в 95-м году.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 2)
Ответ на: комментарий от crutch_master

Ну тогда в жаваскрипте юзай хешмап, а не массив. Тогда будет что сравнивать. Твой код любой вменяемый JavaScript-интерпретатор скомпилирует в массив. А в Java ты сам выбираешь структуры данных. Сделай что-то вроде h["key_" + i] = ... или h[i * i].

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 2)
Ответ на: комментарий от maxcom

Сам хеш жрёт сносно. Ставим примитивные типы и ест оно +- столько же.

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

Ну вот я бы совсем не против был если бы у Java были профили. Кстати как раньше было -client/-server. Только больше, например -desktop-compact, -desktop-high-performance, -low-latency, -web-server

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

Производительность BigInt в JS зависит не от моих-твоих фантазий, а от реализации движка.

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

Ораклу пофиг на не-серверы. Они даже 32 бита перестали собирать, лол. Да и память отдавать операционке можно было научиться сто лет назад.

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

Но он же не совместим с Number.

Разве?

> 999999999999999999999999999999999999999999999999999999999999n+BigInt(3);
1000000000000000000000000000000000000000000000000000000000002n
surefire ★★★
()
Ответ на: комментарий от Deleted

Это не так. В частности, V8 использует int32 для Number до тех пор, пока не понадобится double precision.

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

На втором говне быстрее и проще говнокодить. Правда либ нет и npm похож больше на варезник.

Два чая! Честно, я нигде ещё не видел более точного сравнения java и node.

slovazap ★★★★★
()

Ну круто и закономерно, гугл очень много сил в v8 вложил. Но это видно corner case, по computer language benchmark не все так радужно, включая потребление памяти.

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

Но сам язык так себе. Вот например объект использовать в качестве ассоциативного массива в случае необходимости удаления элементов не рекомендуют, вроде скорость удаления слишком медленная, вместо него лучше использовать Map. Только вот зарефакторить так никто не помешает:

var h = new Map()
h['Key'] = 'value';
Загадка называется - «найди здесь ошибку» :) Плохо жить без статической типизации.

Aber ★★★★★
()
Последнее исправление: Aber (всего исправлений: 2)
Ответ на: комментарий от vertexua

А ты своё не против хотя бы в openjdk озвучил? Или, можно сделать утилиту стороннюю которая будет оборачивать java.exe или выставлять окружение для self-contained бинарей.

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

Не знаю. Может быть. Когда я увидел разницу в жоре, почувствовал себя голым. А мы еще не даже не заикались о всяких ORM и прочем пром. барахле.

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

Плохо жить без статической типизации.

Ну как. Если всю жизнь писать за СТ, потом бац и начать писать с ДТ, то сделаешь typesrcipt. Есть своя специфика.

crutch_master ★★★★★
() автор топика

А теперь попробуй записать в txt файл числа от 1, 100.000.000 и сравни сколько секунд это будет делать жава и сколько минут жаваскрип

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

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

писать --- может быть. поддерживать --- однозначно нет.

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

Да и на си это не очень быстро. Разве что генерировать сразу много и потом писать. А к чему такие синтетические задачи?

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

Да у меня уже Typescript. Всё равно нестандартный ui проще писать на чём угодно, кроме браузера.

Shadow ★★★★★
()

Что не так сделал? Занялся фигней...

dem ★★
()
import java.util.*;

public class Main {
    private final Map<Integer, Table> hash = new HashMap<>();

    static class Table {
        int x2;
        int x3;
        String str;
        public Table(int i) {
            x2 = i*2;
            x3 = i*3;
            str = Integer.toString(x3).intern();
        }
    }

    public void test() throws Exception {
        for (int i = 0; i < 1000*1000; i++) {
            hash.put(i, new Table(i));
        }
        System.out.println("Generated");
        System.in.read();
        System.gc();
        System.in.read();
    }

    public static void main(String[] args) throws Exception {
       new Main().test();
    }
}
# java -Xlog:gc Main
[0.016s][info][gc] Using G1
[0.393s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 62M->60M(1024M) 25.372ms
[1.010s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 113M->116M(1024M) 21.523ms
Generated

[3.591s][info][gc] GC(2) Pause Full (System.gc()) 138M->123M(424M) 54.126ms

123M, так-то.

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

вроде скорость удаления слишком медленная

Это уже починили, причём давно.

no-such-file ★★★★★
()

https://ideone.com/2DR7qR

На го отожрало 22 мегабайта

package main
import "fmt"

func main(){
	var h [1000*1000]map[string]interface{}
	
	for i := 0; i < 1000*1000; i++ {
		m := make(map[string]interface{}, 3)
		
		h[i] = m
		
		m["x1"] = i * 2
		m["x2"] = i * 3
		m["str"] = fmt.Sprint(i*3)
	}
}
nikolnik ★★★
()
Ответ на: комментарий от nikolnik

осталось только увидеть реализацию от царя и можно идти вешаться

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

Он отжирает кучу ресурсов в процессе и ему может просто не хватить в определённый момент. Плюс разница в объёме кода.

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

А теперь попробуй записать в txt файл числа от 1, 100.000.000 и сравни сколько секунд это будет делать жава и сколько минут жаваскрип

Зачем?

crutch_master ★★★★★
() автор топика

Давай начнём с того, что эквивалентный джава код будет выглядеть так:

public class Test {

    static class Table {
        int x2;
        int x3;
        String str;
        public Table(int i) {
            x2 = i*2;
            x3 = i*3;
            str = Integer.toString(x3);
        }
    }
    private HashMap<Integer, Table> hash = new HashMap<>();

    public Test() {
    
    }

    private void log(String s) {
        System.out.println(s);
    }

    public void test() {
        for (int i = 0; i < 1000*1000; i++) {
            hash.put(i, new Table(i));
        }
        log("Generated");
        Scanner scan = new Scanner(System.in);
        scan.nextInt();
    }

    public static void main(String[] args) {
       Test m = new Test();
       m.test();
    }
}

В жс нет long-ов и v8 держит всё это дело в 32 битных переменных. Эквивалентный код на java 11 скушал как и на ноде - 180 МБ, но правда не опустилось до 127 МБ.

Пойдём дальше, если хочешь хранить большой словарь в памяти, то не используй HashMap из стандартной библиотеки:

private SmoothieMap<Integer, Table> hash = new SmoothieMap<>(1000*1000);

И вот мы достигли отметки 139 МБ.

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

Поставь 11, там символы компактней хранятся.

В 8-ке можно -XX:+UseCompressedStrings использовать, но это неточно, под рукой нет потестить.

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

В 8-ке можно -XX:+UseCompressedStrings использовать, но это неточно, под рукой нет потестить.

Для этого не нужно иметь под рукой.

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html (Ctrl+F UseCompressedStrings)

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7129417

korvin_ ★★★★★
()

Добавил ключики -XX:+UnlockExperimentalVMOptions -XX:+UseZGC и получил 50 МБ на java 11. Похоже джава идёт к успеху, осталось дождаться валхалу и еще парочку джепов и будет улёт.

foror ★★★★★
()
Последнее исправление: foror (всего исправлений: 1)

Вот же прикол, запустил твой код с лонгами и вернул обратно HashMap получил 47 МБ.

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

На го отожрало 22 мегабайта

Лень загружать GraalVM и компилить под native image, может у кого уже стоит, вот аналогичный код для джавы:

import java.util.Scanner;

public class Test2 {

    static class Table {
        private int x2;
        private int x3;
        private String str;
        public Table(int i) {
            x2 = i*2;
            x3 = i*3;
            str = Integer.toString(x3);
        }
    }
    private Table[] hash = new Table[1000*1000];

    public Test2() {
    
    }

    private void log(String s) {
        System.out.println(s);
    }

    public void test() {
        for (int i = 0; i < 1000*1000; i++) {
            hash[i] = new Table(i);
        }
    }

    public static void main(String[] args) {
       Test2 m = new Test2();
       m.test();
       
       m.log("Generated");
       Scanner scan = new Scanner(System.in);
       scan.nextInt();
    }
}
foror ★★★★★
()
Ответ на: комментарий от foror

Качнул с гитхаба RC12 и в итоге хуже, чем с ZGC с HashMap - 78 МБ

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

писать --- может быть. поддерживать --- однозначно нет.

У меня микросервисы и кода на строчек 200-400. Поддерживать нечего.

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

Что-то мне не охота плясать с UnlockExperimentalVMOptions в проде. Когда его там дотестят? Плюс 50 мб - это после гц? Пиковый жор такой же?

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

Давай начнём с того, что эквивалентный джава код будет выглядеть так:

Кстати, что там у примитивного типа с null? Null нужен.

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

Что-то мне не охота плясать с UnlockExperimentalVMOptions в проде

Поставь 11-ую, она норм для продакшена, плюс там latin строки память в два раза меньше занимают. А эта штука только под Linux/x64 сделана на текущий момент...

Когда его там дотестят?

Хз, может к следующей lts?

Плюс 50 мб - это после гц?

После гц 46.6 МБ

Пиковый жор такой же?

На старте 47 МБ на твоём коде

Кстати, что там у примитивного типа с null? Null нужен.

Полностью твой код запустил, даже static на внутренний класс не добавлял.

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