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

Ответ на: комментарий от 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)
Ответ на: комментарий от pon4ik

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

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

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

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

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

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

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

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

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

pon4ik ★★★★★ ()

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

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

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

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

Rastafarra ★★★ ()
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 ()

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

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)
Ответ на: комментарий от 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)
Ответ на: комментарий от 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 ★★★ ()
Ответ на: комментарий от crutch_master

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

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

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

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

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

После гц 46.6 МБ

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

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

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

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

foror ★★★ ()