Fast Collections library

A number of optimised collections libraries exist that do things like replace JDK’s HashSet<Integer> with a faster and lighter IntHashSet.

I forked a collections benchmark suite and updated the versions. The full suite of tests takes days to complete. Comparing just Trove (currently shipped by Fiji) and Eclipse Collections (formerly GSCollections, used by BoneJ) gives this result summary: EC_vs_Trove.ods.zip (42.8 KB)

Full results are too big to be posted here.

Highlights - EC is much faster (~1.5-3×) than Trove to add, remove, copy, populate and iterate int elements in a HashSet (here a UnifiedSet), but a bit slower (~10%) to check with contains(). These are the operations that matter the most for BoneJ.

Note that the tests are named gscollections, but the code tested is the up-to-date Eclipse Collections.

d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.addElement	memory-footprint.log	N/A	467505	100	avgt	60	154.061	±	2.045	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.addElement	memory-footprint.log	N/A	467505	1000	avgt	60	191.244	±	2.928	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.addElement	memory-footprint.log	N/A	467505	10000	avgt	60	209.048	±	3.252	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.addElement	memory-footprint.log	N/A	467505	100000	avgt	60	279.614	±	5.102	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.addElement	memory-footprint.log	N/A	467505	1000000	avgt	60	660.821	±	32.912	ns/op

d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.addElement	memory-footprint.log	N/A	467505	100	avgt	60	64.7	±	0.776	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.addElement	memory-footprint.log	N/A	467505	1000	avgt	60	66.057	±	0.939	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.addElement	memory-footprint.log	N/A	467505	10000	avgt	60	81.165	±	1.523	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.addElement	memory-footprint.log	N/A	467505	100000	avgt	60	134.344	±	2.008	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.addElement	memory-footprint.log	N/A	467505	1000000	avgt	60	454.729	±	37.142	ns/op


d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.containsElement	memory-footprint.log	N/A	467505	100	avgt	60	40.521	±	0.159	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.containsElement	memory-footprint.log	N/A	467505	1000	avgt	60	48.709	±	0.618	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.containsElement	memory-footprint.log	N/A	467505	10000	avgt	60	57.496	±	0.959	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.containsElement	memory-footprint.log	N/A	467505	100000	avgt	60	101.093	±	1.777	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.containsElement	memory-footprint.log	N/A	467505	1000000	avgt	60	330.134	±	26.805	ns/op


d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.containsElement	memory-footprint.log	N/A	467505	100	avgt	60	47.447	±	0.356	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.containsElement	memory-footprint.log	N/A	467505	1000	avgt	60	53.831	±	0.977	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.containsElement	memory-footprint.log	N/A	467505	10000	avgt	60	63.674	±	1.196	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.containsElement	memory-footprint.log	N/A	467505	100000	avgt	60	100.954	±	1.331	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.containsElement	memory-footprint.log	N/A	467505	1000000	avgt	60	358.008	±	23.172	ns/op



d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.iterate	memory-footprint.log	N/A	467505	100	avgt	60	1736.483	±	12.34	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.iterate	memory-footprint.log	N/A	467505	1000	avgt	60	23906.732	±	478.162	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.iterate	memory-footprint.log	N/A	467505	10000	avgt	60	270865.792	±	2288.5	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.iterate	memory-footprint.log	N/A	467505	100000	avgt	60	2352405.567	±	9359.089	ns/op
d.h.p.c.trove.sets.Trove_Integer_HashSet_Test.iterate	memory-footprint.log	N/A	467505	1000000	avgt	60	30163713.947	±	129011.963	ns/op

d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.iterate	memory-footprint.log	N/A	467505	100	avgt	60	983.56	±	5.659	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.iterate	memory-footprint.log	N/A	467505	1000	avgt	60	9389.887	±	157.296	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.iterate	memory-footprint.log	N/A	467505	10000	avgt	60	205697.963	±	3591.927	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.iterate	memory-footprint.log	N/A	467505	100000	avgt	60	2230485.749	±	22884.35	ns/op
d.h.p.c.gscollections.sets.GSCollections_Integer_UnifiedSet_Test.iterate	memory-footprint.log	N/A	467505	1000000	avgt	60	36188801.179	±	1954637.683	ns/op



6 Likes

@axtimwalde this is some benchmark data for unsorted long+int maps: EC is in general ~2× faster.

d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.addElement	memory-footprint.log	100	467505	100	avgt	60	62.098	±	0.862	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.addElement	memory-footprint.log	100	467505	1000	avgt	60	74.883	±	0.92	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.addElement	memory-footprint.log	100	467505	10000	avgt	60	91.969	±	1.255	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.addElement	memory-footprint.log	100	467505	100000	avgt	60	150.218	±	2.124	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.addElement	memory-footprint.log	100	467505	1000000	avgt	60	475.151	±	45.912	ns/op

d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.addElement	memory-footprint.log	100	467505	100	avgt	60	161	±	3.202	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.addElement	memory-footprint.log	100	467505	1000	avgt	60	187.787	±	2.544	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.addElement	memory-footprint.log	100	467505	10000	avgt	60	213.593	±	4.105	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.addElement	memory-footprint.log	100	467505	100000	avgt	60	312.714	±	4.853	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.addElement	memory-footprint.log	100	467505	1000000	avgt	60	785.621	±	38.642	ns/op


d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.removeElement	memory-footprint.log	100	467505	100	avgt	60	59.168	±	0.281	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.removeElement	memory-footprint.log	100	467505	1000	avgt	60	79.044	±	0.967	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.removeElement	memory-footprint.log	100	467505	10000	avgt	60	95.206	±	1.984	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.removeElement	memory-footprint.log	100	467505	100000	avgt	60	142.761	±	1.951	ns/op
d.h.p.c.gscollections.maps.GSCollections_LongInteger_UnifiedMaps_Test.removeElement	memory-footprint.log	100	467505	1000000	avgt	60	475.449	±	39.482	ns/op


d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.removeElement	memory-footprint.log	100	467505	100	avgt	60	131.979	±	2.906	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.removeElement	memory-footprint.log	100	467505	1000	avgt	60	185.425	±	2.869	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.removeElement	memory-footprint.log	100	467505	10000	avgt	60	210.833	±	2.898	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.removeElement	memory-footprint.log	100	467505	100000	avgt	60	282.192	±	3.958	ns/op
d.h.p.c.trove.maps.Trove_LongInteger_HashMap_Test.removeElement	memory-footprint.log	100	467505	1000000	avgt	60	867.847	±	47.487	ns/op

3 Likes

Relatedly, thanks to @mdoube, the SciJava bill of materials now includes Eclipse Collections (scijava/pom-scijava#134). This will be part of the forthcoming pom-scijava 29.0.0 release.

5 Likes