Newsgroup: it.lavoro.informatica
Titolo: java vs c++ - heap seq

In questa puntata di java vs c++ si vuole mettere a confronto i tempi di 
allocazione sullo heap.
Per ora non in modo random (oggetto di un prossimo benchmark) ma solo 
allocazioni sequenziale della stessa dimensione.
Non e' realta fuori dalla comune programmazione, puo realmente succedere 
di avere casi di questo genere.
Pensiamo ad esempio ad un parsing di un testo tokenizzato scritto in un 
determinato linguaggio di programmazione.
I nodi creati sono in genere della stessa dimensione,hanno bassa 
deallocazione se non nulla e vengono tutti distrutti in un particolare 
momento.
I due programmi non fanno altro che creare una semplice lista il cui 
nodo viene creato sullo heap ed ogni nodo punta al nodo precedente.
In java sono previste due modalita (tranne l'ultimo caso),la prima senza 
preallocazione dello heap,la seconda con preallocazione computata.
Per c++ sono previste due modalita,la prima l'uso del classico heap 
manager fornito dalla lib std C (::new e ::delete usano internamente 
malloc e free in g++),purtroppo sembra non sia possibile preallocare 
l'heap od almeno non ho trovato nulla in proposito.
La seconda modalita merita un piccolo appunto.
Sia in jdk che in g++ e' possibile sostituire l'heap manager di default.
Ma in c++,al contrario di java dove tale tecnica e' preclusa od almeno a 
me risulta cosi (se qualcuno e' a conoscenza di tale tecnica in java puo 
farmelo sapere), e' possibile sostituire l'heap manager di default per 
una classe od un albero di classi con uno specializzato.
Nel nostro caso poiche i nodi hanno tutti la stessa dimensione e vengono 
tutti distrutti in un determinato punto,ecco che ho sostituito l'heap 
manager di default del nodo con uno piu adatto allo scopo.
Questa e' la seconda modalita.
L'heap manager per questo scopo l'ho chiamato estd::allocators::fixed 
(in c++ l'heap manager viene chiamato genericamente allocator anche 
perche non e' detto che venga usato l'heap) e se viene impostato con una 
initial_capacity a 0 usa l'heap manager di default in modo da non dover 
duplicare inutilmente codice o scrivere due programmi.
Ha la limitazione di non essere thread safe e non implementare T[] 
mentre l'heap manager di java come l'heap manager della lib std C lo sono.
Se qualcuno ne ha necessita di usarlo e/o adattarlo lo faccia pure,e' 
stato scritto da me per cui niente violazioni di copyright.
Per rendere un po piu interessante il benchmark ho pure fatto 
visualizzare la memoria occupata al termine della allocazione di tutti i 
nodi.
Per eseguire il benchmark si consiglia di disabilitare la swap area 
(sudo swapoff -a) ed avere almeno 10G di ram liberi (l'ultimo test e' 
pesante come ram occupata)
La mia macchina ha 16G di ram per cui questi problemi non sussistono.
 
-- heap.sh --
#!/bin/sh
JAVA_HOME=/opt/jdk1.8.0_192
PATH="$JAVA_HOME/bin:$PATH"
LD_LIBRARY_PATH="$JAVA_HOME/lib:$LD_LIBRARY_PATH"
rm -f a.out *.class *.o
javac -version
javac  Heap.java || exit 1
g++-8 --version
g++-8 -std=c++17 -O2  heap.cpp || exit 1
 
#env MALLOC* it's used for heap manager policies
set | grep MALLOC > /dev/null && { echo "unset MALLOC* env before">&2; 
exit 1;}
 
for n in 20000000 50000000 100000000 200000000 300000000
do
	echo "$n allocations"
	case $n in
		300000000) # java require a preallocation
			echo "java preallocation: -Xms10g"
			java -Xms10g Heap  $n 1 || exit 1
			;;
		*)
			java Heap $n 0 | tee log$$.txt
			mem=$(cat log$$.txt | perl  -ne '/memory:\s+(\d+)/ && print ($1*2)')
			rm -f log$$.txt
			if [ "$mem" ]
			then
				echo "java preallocation: -Xms${mem}m"
				java -Xms${mem}m Heap  $n 1 || exit 1
			else
				echo "java cannot retrieve memory used" >&2
				exit 1
			fi
	esac
	./a.out $n 0 || exit 1 #with std allocator
	./a.out $n 1 || exit 1 #with custom allocator
done
exit 0
--Heap.java --
class Heap {
 
	static class node {
		public node(node n) {
			m_pred=n;
		}				
		private node m_pred;
	};
 
 
	public static void main(String[] args) {
		long n=Long.parseLong(args[0]);
		final boolean heap_prealloc=Integer.parseInt(args[1]) != 0;
		try {
			node last=null;
			final long t1=System.currentTimeMillis();
			while(n-- != 0)
				last=new node(last);		
			final long t2=System.currentTimeMillis();
			System.out.println("java " + (heap_prealloc ? "with prealloc" : "") + 
" ctime: " + (t2 - t1) + "ms memory: " + ((double) 
Runtime.getRuntime().totalMemory() / (double) (1024 * 1024)) + "M");
		}
		catch(Exception e) {
			System.err.println(e);
			System.exit(1);
		}
	}
};
 
-- fixed.h --
#ifndef ESTD_ALLOCATORS_FIXED_H
#define ESTD_ALLOCATORS_FIXED_H
 
#include <cassert>
#include <vector>
 
/*
	an allocator for T type with less deallocation
	if initial_capacity is zero use the std allocator
	warning - not thread safe and not implemented for T[]
*/
 
 
namespace estd {
	namespace allocators {
 
 
		template <typename T>
		class fixed final {
			public:
				using value_type=T;
				using size_type=std::size_t;
				using difference_type=std::ptrdiff_t;
				using propagate_on_container_move_assignment=std::true_type;
				using is_always_equal=std::true_type;
				explicit fixed(size_type initial_capacity = {}, size_type 
extend_capacity = {})noexcept;
				fixed(const fixed&)=delete;
				fixed& operator=(const fixed&)=delete;
				fixed(fixed&& a)noexcept { to_move(a); }
				fixed& operator=(fixed&& a)noexcept { to_move(a); return *this; }
				void initial_capacity(size_type)noexcept;
				void extend_capacity(size_type v) noexcept { m_extend_capacity = v; }
				size_type initial_capacity()const noexcept { return 
m_initial_capacity; }
				size_type extend_capacity()const noexcept { return m_extend_capacity; }
				bool empty()const noexcept { return !m_pool.size();}
				~fixed() { clear(); }
				[[nodiscard]] value_type* allocate(size_type n);
				void deallocate(value_type* p, std::size_t n)noexcept;
				void clear()noexcept;
			private:
				using pool_t = std::vector<T*>;
				void extend(size_type);
				void to_move(fixed&&)noexcept;
				size_type m_initial_capacity;
				size_type m_extend_capacity;
				value_type* m_last;
				pool_t m_pool;
		};
 
 
		template <typename T>
		inline fixed<T>::fixed(size_type initial, size_type extend)noexcept
			: m_initial_capacity(initial)
			, m_extend_capacity(extend)
			, m_last()
			, m_pool()
		{
		}
 
		template <typename T>
		inline void fixed<T>::clear()noexcept {
			for (auto& p : m_pool)
				::free(p);
			m_pool.clear();
			m_last = nullptr;
		}
 
		template <typename T>
		inline void fixed<T>::initial_capacity(size_type 
initial_capacity)noexcept {
			assert(empty());
			m_initial_capacity = initial_capacity;
		}
 
		template <typename T>
		typename fixed<T>::value_type* fixed<T>::allocate(size_type n) {
			if (!m_initial_capacity)
				return static_cast<value_type*>(::operator new(n));
			assert(n <= sizeof(value_type));
			if (empty()) {
				extend(m_initial_capacity);
				assert(m_last);
				return m_last;
			}
			assert(m_last);
			const auto sz = m_pool.size() == 1 ? m_initial_capacity : 
m_extend_capacity;
			assert(sz);
			if (m_last == m_pool.back() + (sz - 1)) {
				if (!m_extend_capacity)
					throw std::bad_alloc();
				extend(m_extend_capacity);
				assert(m_last);
				return m_last;
			}
			return ++m_last;
		}
 
		template <typename T>
		inline void fixed<T>::deallocate(value_type* p, std::size_t n)noexcept {
			if (!p)
				return;
			assert(n == 1);
			if (!m_initial_capacity)
				::operator delete(p, n);
		}
 
		template <typename T>
		inline void fixed<T>::to_move(fixed&& a)noexcept {
			m_initial_capacity = a.m_initial_capacity;
			m_extend_capacity = a.m_extend_capacity;
			m_last = a.m_last;
			m_pool = std::move(a.m_pool);
			assert(a.empty());
		}
 
 
		template <typename T>
		void fixed<T>::extend(size_type sz) {
			assert(sz);
			m_pool.push_back(nullptr);
			auto p = static_cast<value_type*>(::malloc(sz * sizeof(value_type)));
			if (!p) {
				m_pool.erase(--(m_pool.rbegin().base()));
				throw std::bad_alloc();
			}
			m_pool.back() = m_last = p;
		}
 
	} //namespace
}
 
 
#endif
 
-- heap.cpp --
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include "fixed.h"
 
 
template <typename T>
inline T convert(const char* s) {
	T n;
	std::istringstream os(s);
	os >> n;
	return n;
}
 
 
using ck_t=std::chrono::high_resolution_clock::time_point;
 
static inline ck_t now()  {
	return std::chrono::high_resolution_clock::now();
}
 
static inline long diff(const ck_t& t1,const ck_t& t2) {
	return std::chrono::duration_cast<std::chrono::milliseconds>(t2 - 
t1).count();
}
 
static unsigned long getvm() {
	std::ifstream stat_stream("/proc/self/stat",std::ios_base::in);
	std::string pid, comm, state, ppid, pgrp, session, tty_nr
    		,tpgid, flags, minflt, cminflt, majflt, cmajflt
    		,utime, stime, cutime, cstime, priority, nice
    		,O, itrealvalue, starttime;
	unsigned long vsize;
	long rss;
	stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr
                >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
                >> utime >> stime >> cutime >> cstime >> priority >> nice
                >> O >> itrealvalue >> starttime >> vsize >> rss;
	return vsize;
}
 
 
class node final {
	public:
		using size_type = std::size_t;
		explicit node(node* pred)noexcept : m_pred(pred) {}
		node(const node&)=delete;
		node(node&&)=delete;
		node& operator=(const node&)=delete;
		node& operator=(node&&)=delete;
		void* operator new(size_type);
		void operator delete(void*);
	private:
		node* m_pred;
};
 
static estd::allocators::fixed<node> g_allocator;
 
 
inline void* node::operator new(size_type sz) {
	return g_allocator.allocate(sz);
}
 
inline void node::operator delete(void* p) {
	g_allocator.deallocate(static_cast<node*>(p),1);
}
 
int main(int argc,char** argv) {
	++argv;
	auto n=convert<long>(*argv++);
	const auto use_callocator=convert<int>(*argv) ? true : false;
	if (use_callocator)
		g_allocator.initial_capacity(n);
	try {
		node* last=nullptr;
		const auto t1 =  now();
		while(n--)
			last=new node{last};
		const auto t2 = now();
		std::cout << "c++ with " << (use_callocator ? "custom allocator" : 
"std allocator") << " ctime: "  << diff(t1,t2) << "ms memory: " << 
(getvm() / double(1024 * 1024)) << "M" << std::endl;
	}
	catch(const std::bad_alloc&) {
		std::cerr << "not enough memory " << std::endl;
		return 1;
	}
}
 
 
-- esecuzione
javac 1.8.0_192
g++-8 (Ubuntu 8.2.0-1ubuntu2~18.04) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
20000000 allocations
java  ctime: 4947ms memory: 709.0M
java preallocation: -Xms1418m
java with prealloc ctime: 128ms memory: 1359.0M
c++ with std allocator ctime: 535ms memory: 624.051M
c++ with custom allocator ctime: 76ms memory: 166.27M
50000000 allocations
java  ctime: 11678ms memory: 1287.5M
java preallocation: -Xms2574m
java with prealloc ctime: 2405ms memory: 2467.0M
c++ with std allocator ctime: 1309ms memory: 1539.54M
c++ with custom allocator ctime: 184ms memory: 395.152M
100000000 allocations
java  ctime: 25223ms memory: 2546.5M
java preallocation: -Xms5092m
java with prealloc ctime: 4782ms memory: 4880.0M
c++ with std allocator ctime: 2619ms memory: 3065.41M
c++ with custom allocator ctime: 368ms memory: 776.621M
200000000 allocations
java  ctime: 66187ms memory: 3552.0M
java preallocation: -Xms7104m
java with prealloc ctime: 6762ms memory: 6808.0M
c++ with std allocator ctime: 5240ms memory: 6117.26M
c++ with custom allocator ctime: 734ms memory: 1539.56M
300000000 allocations
java preallocation: -Xms10g
java with prealloc ctime: 9788ms memory: 9813.5M
c++ with std allocator ctime: 7870ms memory: 9168.99M
c++ with custom allocator ctime: 1107ms memory: 2302.5M

 In questa puntata di java vs c++ 
 
ogni volta ingenuamente mi sorprendo del nulla cosmico di queste 
cialtronate fotoniche.


Ogni volta mi sorprendo dall'IO imperante.
Cosmico e non ingenuo ed eterea cialtronaggine.
-- 
ac (x=y-1)
Aborro il Killfile
(La violenza e' l'ultimo rifugio degli incapaci -Salvor Hardin-)

 eterea cialtronaggine.
 
cara, il fusaro de'noaltri.


Se e' quello che si chiama Fusaro (f maiuscola).
Preferisco essere un Fusaro che un Sallusti.
Il primo e' anche piu' bello piu' cosmico quasi etereo
e molto meno cialtrone.
Grazie per l'accostamento, non sono degno!
P.S.
e' /noantri/


Il 03/03/19 12:26, rootkit ha scritto:

Ti manca la fantasia
 

 Fusaro che un Sallusti.
 
l'importante è accontentarsi.

 In questa puntata di java vs c++ 
 
O meglio: "in questa puntata di cone fare la figura dello scemo ancora una volta".
 
[snip sul solito oceano di cazzate]


O via! Pop Corn e Coca Cola.
 
-- 
The three most dangerous things in the world are a programmer with a 
soldering iron, a hardware type with a program patch and a user with an 
idea.

 O via! Pop Corn e Coca Cola.
 
Da notare il mare di stupidaggini con le quali perde tempo. Se solo impiegasse un 10% di quel tempo a studiare. Degli esperimenti di nessun valore. Peraltro, non contento delle figuracce passate. 

 O via! Pop Corn e Coca Cola.
 
naah, non credo. il quarto o quinto sequel di "vieni avanti cretino" non 
sarebbe quel cult assoluto del trash che è stato il primo. 
 

 e molto meno cialtrone.
 
Tu sei il re dei cialtroni. Cara padre sarebbe orgoglione.

 allocazione sullo heap.
 
Bel lavoro! Devi essere molto competente in entrambi i linguaggi per 
poter progettare questi numerosi e vari test !
 
 

 Bel lavoro! Devi essere molto competente in entrambi i linguaggi 
 
ROTFL :)
 
"enoquick, e` lei?"
 
Non solo e` competente (!) in entrambi i linguaggi, ma ha anche una personalita` spumeggiante.


Secondo me era solo una presa per il culo. Impossibile che fosseuna 
seria affermazione. Soprattutto se rivolta ad un soggetto dotato di 
personalita' spumeggiante.
:-)


L'aggettivo "spumeggiante" lo perseguiterà per un pezzo.


Il 05/03/19 17:29, Micky ha scritto:

In questi gg sono un po occupato per cui saro breve:
 
 
if (sarcasm())
	send_mke2fs();
else
	send_thank();
 
 
Per gli altri 3 che non hanno niente da fare che fare spiritosaggini un bel:
 
send_mke2fs()
 
 
 

 :-)
 
mah! sarebbe una autoironia decisamente sorprendente da parte sua. più 
volte ha dimostrato una dialettica brillante come un ciottolo di fiume e 
una ironia da far cascare le gonadi, oltre ad una suscettibilità non 
indifferente.


Brillante?....spumeggiante!


 Brillante?....spumeggiante!
 
s'è pure offeso quando gli ho mostrato la celebre scena di the mask, lo 
spumeggiante :-)


Tu, come direbbe qualcuno, in una gara di stupidita' arriveresti secondo.


 Tu, come direbbe qualcuno, in una gara di stupidita' arriveresti secondo.
 
Eh, ma sempre coglione rimani.

 send_mke2fs()
 
Una battuta che finora hai compreso solo tu.
 

 Una battuta che finora hai compreso solo tu.
 
ma è spumeggiante!


E tu sempre secondo arrivi.



La competenza e' una strana cosa.
Se sei /competente/ non ragioni con incompetenti
Ma questo e' peccato di grande arroganza.
Se sei completamente incompetente
/tenti/ di ragionare ma i competenti sono talmente /arroganti/
che sono il sinonimo di incompetenza.
Ci sono poi varie sfumature di grigio.
Ma questo, non tanto Socrate, ma la filosofia orientale ne da
ampia letteratura.
 
Lei chi e'?
 
La cosa piu' problematica e' quando incompetenti pensano di essere 
competenti.
Mi progetti un qualsiasi test e, poi, forse ne riparliamo.
E' suo ddiritto, ovviamente, delucidare su quali siano i test e
se, dove,come, quando, perche', siano inutili.
O come, quando, perche', dove un matematico e' un matematico
(questa cosa mi sarebbe piaciuta lo avesse detto  Ramanujan)
-- 
ac (x=y-1)
Aborro il Killfile
(La violenza e' l'ultimo rifugio degli incapaci -Salvor Hardin-)

Il 06/03/2019 14:51, Dr.UgoGagliardelli ha scritto:

Enoquick ha una personalita' spumeggiante rispetto a /tanti travet/ .
Che l'autore (Micky, un nick una storia ed una garanzia!) volesse essere
/sarcastico/ e' nelle cose.
Ma anche lui vorrebbe essere competente ;-)
In quanto a /sarcasmo/ e' un /parvenu/
 
Nelle poche cose in cui credo di essere competente cerco di capire e,
se mi e' possibile, insegnare.
Fare i deficienti e' facile.
IO, lei e un molteplici altri ne siamo grande e inarrivabile esempio


 
 
 
Come ho gia spiegato su it.comp.lang.c++ non ti diro il significato
Devi arrivarci da solo non dormendo la notte

Il 06/03/19 15:00, rootkit ha scritto:

Ma che spumeggiante.
Champagnissima (non e' un syntax error,e' un nuovo termine)
 
 

 IO
 
Stop. Tu non "fai" il deficiente. Tu SEI deficiente. E` diverso.

 Come ho gia spiegato 
 
Tu non "spieghi" niente. Tu spari solo stupidaggini.

 
 
Illazioni come al solito senza mai spiegarne i motivi
 

 Illazioni come al solito senza mai spiegarne i motivi
 
Nessuna illazione. E` chiaro a tutti, ormai, che tu non sei in grado di spiegare un bel niente. 
 

Il 07/03/19 11:02, 4ndre4 ha scritto:

Faccio come fai tu
Un bel mke2fs
 

 Un bel mke2fs
 
Questa e' la risposta tipo di quando non sai come replicare?
Ho in sospetto di si': inviare a null cosi' non si vede piu'.
Lontano dagli occhi, lontano dal culo. Non e' cosi'?
 

Il 07/03/19 12:08, Dr.UgoGagliardelli ha scritto:

ma che replico a fare con un tipo che non fa altro che svicolare
 
D'ora innanzi mi metto solo a discutere di faccende tecniche
Tutto il resto della merda come attacchi personali,insinuazioni e roba 
varia verra ignorata
 
 

 D'ora innanzi mi metto solo a discutere di faccende tecniche
 
Non hai la competenza per farlo. Si e` visto.