I agree with Joris. Implement the current setup and solve a performance
collapse if and when it causes issues.
--
Information System on Graph Classes and their Inclusions (ISGCI)
http://www.graphclasses.org
On 10 April 2016 9:07:25 pm Joris Kinable <de...@gm...> wrote:
> I've done some testing to measure the impact of frequent object creation.
> *Algorithm setup:*
> 1. Implementation where VertexPair objects are frequently created and
> immediately discarded afterwards (see
> https://github.com/jgrapht/jgrapht/pull/188).
> 2. Implementation where I use a ModifiableVertexPair as proposed by Ernst.
> In this implementation I created a single ModifiableVertexPair indexPair.
> Each time an edge is queried in the Map<ModifiableVertexPair<V>,
> ArrayUnenforcedSet<E>> touchingVerticesToEdgeMap,
> indexPair.updatePair(source, target) gets invoked first. This entirely
> avoids the creation of new VertexPairs. To ensure that read operations are
> thread-save, I made the getEdge(u,v) and getAllEdges(u,v) synchronized.
> Here's the implementation:
> https://github.com/jkinable/jgrapht/blob/SpecificsOptimization_experimental/jgrapht-core/src/main/java/org/jgrapht/graph/specifics/FastLookupDirectedSpecifics_experimental.java
> . If you want to try it yourself or see my implementation, download this
> branch:
> https://github.com/jkinable/jgrapht/tree/SpecificsOptimization_experimental
>
> *Experiment setup:*
> java version "1.8.0_77"
> Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
>
> Each experiment is repeated 5 times, with 5 warmup iterations each.
>
> *Experiments:*
> 1. ConstructGraph: Build 100 graphs, each consisting of 1000 vertices and
> 100000 edges
> 2. UseGraph: Build 100 graphs, run 3 different algorithms on each graph,
> randomly destroy part of the graph (triggering containsEdge(u,v) and
> removeEdge(u,v)).
>
> Results:
>
> Benchmark
> Mode Cnt Score Error Units
> ConstructGraph(objectCreation) avgt 5 11313.907 ±
> 216.430 ms/op
> UseGraph(objectCreation) avgt 5 33234.987 ± 1009.527
> ms/op
> ConstructGraph(IndexPair) avgt 5 11230.629 ± 146.290 ms/op
> UseGraph(IndexPair) avgt 5 32295.133 ± 1208.719 ms/op
>
> Here objectCreation uses a DirectedSpecifics implementation where
> short-lived VertexPairs are created. IndexPair uses a DirectedSpecifics
> implementation where objects are reused.
> I repeated these experiment several times, never was there a clear winner.
>
> Conclusion:
> I was unable to create a scenario where reusing the objects was
> significantly more efficient than recreating them. Quite an interesting
> result if you ask me. Consequently, I would propose to continue with my
> pull request: https://github.com/jgrapht/jgrapht/pull/188
>
>
> br,
>
> Joris Kinable
>
>
> On Sun, Apr 10, 2016 at 7:24 AM, H.N. de Ridder <hnr...@gr...>
> wrote:
>
>> On Sat, Apr 09, 2016 at 08:25:15PM -0700, John Sichi wrote:
>>
>> Yes, that's true. Thread-unsafety is a k.o.-criterium. I tested with 64bit
>> icedtea 2.6.5. I'm short on time right now, but I'll see in a week or so
>> whether I can repeat the tests with java8 and also if I can pinpoint when
>> memory gets tight and what happens then.
>>
>> > Hmmm...the problem with the reusable pair object is that it makes even
>> > reads thread-unsafe (unless it's implemented via a thread-local
>> variable).
>> > I'm not 100% sure, but I think the last time we analyzed it, the graph
>> data
>> > structures themselves were all OK for concurrent reads (though not of
>> > course for concurrent reads+writes).
>> >
>> > It might be good to repeat the benchmarking with the latest JVM.
>> >
>> > On Sat, Apr 9, 2016 at 11:14 AM, H.N. de Ridder <
>> hnr...@gr...>
>> > wrote:
>> >
>> > > On Fri, Apr 08, 2016 at 02:36:37AM -0700, John Sichi wrote:
>> > > > Regarding short-lived VertexPairs: long ago I learned to stop
>> worrying
>> > > and
>> > > > love the Eden approach of the garbage collector. Think about how
>> many
>> > > > iterators are created and forgotten during a typical algorithm
>> > > execution...
>> > >
>> > > But iterators typically are used more than once, here the new pair is
>> used
>> > > only for a single hash lookup.
>> > >
>> > > Several years ago I ran into the same performance issue with JgraphT
>> and
>> > > as a
>> > > quick hack created a wrapper class like this (and then forget about it
>> > > until
>> > > this discussion):
>> > >
>> > > public class CacheGraph<V,E> extends ListenableDirectedGraph<V,E>
>> > > implements GraphListener<V,E> {
>> > > private DirectedGraph<V,E> graph; /** The graph we're caching */
>> > > private HashMap<V,V> nodeCache;
>> > > private HashMap<Pair,E> edgeCache;
>> > > private Pair reusablePair; /** For looking up edges */
>> > >
>> > > The rest of the class is about as you'd expect. Note that Pair,
>> different
>> > > from
>> > > org.jgrapht.util.VertexPair, is modifiable. I used this class to time
>> the
>> > > impact of allocating a new Pair() when performing an edge lookup and
>> in my
>> > > application it adds about 9% to the running time. And this is when
>> there
>> > > is no
>> > > shortage of memory. When there is, the shit can really hit the fan. So
>> I
>> > > suggest using a reusable pair object and not reallocating one for every
>> > > test.
>> > >
>> > > Regards,
>> > > Ernst
>> > >
>> > > >
>> > > > On Wed, Apr 6, 2016 at 4:18 PM, Joris Kinable <de...@gm...>
>> wrote:
>> > > >
>> > > > > John,
>> > > > >
>> > > > > If I understand you correct, you would propose to do the following:
>> > > > > To the Specifics class, you would add:
>> > > > > Map<VertexPair<V,V>,Set<E>> vertexPairToEdgeMap=....;
>> > > > >
>> > > > > Then in DirectedSpecifics.addEdgeToTouchingVertices(E e) you would
>> > > *add*:
>> > > > > VertexPair<V,V> pair=new VertexPair<>(source, target);
>> > > > > if(!vertexPairToEdgeMap.containsKey(pair))
>> > > vertexPairToEdgeMap.put(pair,
>> > > > > new LinkedHashSet<>());
>> > > > > vertexPairToEdgeMap.get(pair).put(e);
>> > > > >
>> > > > > And then the current DirectedSpecifics.getEdge(V source, V target)
>> > > would
>> > > > > be *replaced* by:
>> > > > > VertexPair<V,V> pair=new VertexPair<>(source, target);
>> > > > > if(!vertexPairToEdgeMap.containsKey(pair) ||
>> > > > > vertexPairToEdgeMap.get(pair).isEmpty()) return null;
>> > > > > else return vertexPairToEdgeMap.get(pair).iterator().next();
>> > > > >
>> > > > > This would be possible, but then the
>> SimpleIdentityDirectedGraphTest
>> > > would
>> > > > > fail. (In this test, the Vertex objects are changed *after* they
>> are
>> > > > > created and added to graph, so an IdentityHashMap is required, see
>> the
>> > > test
>> > > > > for details). I'm not sure how to resolve this. An application can
>> > > indeed
>> > > > > override createDirectedSpecifics, but both Specifics and
>> > > > > DirectedSpecifics are resp. private and protected, so you cannot
>> > > provide a
>> > > > > custom DirectedSpecifics implementation which avoid this issue.
>> > > > >
>> > > > > Also, would the frequent construction of short-lived VertexPair
>> objects
>> > > > > not be an issue, i.e. a new VertexPair would be created each time
>> > > getEdge(V
>> > > > > source, V target) is invoked.
>> > > > >
>> > > > > Joris
>> > > > >
>> > > > > On Wed, Apr 6, 2016 at 5:25 PM, John Sichi <js...@gm...>
>> wrote:
>> > > > >
>> > > > >> Per the last option mentioned by Joris, I don't think any
>> > > equals/hashCode
>> > > > >> override is required for any object.
>> > > > >>
>> > > > >> My preference is a hybrid of his second and third options.
>> > > > >>
>> > > > >> We would add an additional graph-level map inside of Specifics
>> (not
>> > > > >> vertex-level inside of EdgeContainer), where the key is
>> VertexPair,
>> > > and the
>> > > > >> value is Edge (or Set<Edge> for multigraphs). When adding an
>> edge,
>> > > we add
>> > > > >> a corresponding entry. When removing an edge, we remove the
>> entry (or
>> > > > >> remove the edge from the set for multigraphs, deleting the entry
>> once
>> > > the
>> > > > >> set is empty). This allows us to optimize both containsEdge and
>> > > getEdge
>> > > > >> (which is slightly different depending on directed vs undirected).
>> > > > >>
>> > > > >> The default implementation would be this new indexing. If an
>> > > application
>> > > > >> wants to revert to the old approach to save memory, this would be
>> > > possible
>> > > > >> by overriding the createDirectedSpecifics and
>> > > createUndirectedSpecifics
>> > > > >> methods.
>> > > > >>
>> > > > >>
>> > > > >> On Wed, Apr 6, 2016 at 2:01 PM, Rushang Karia <
>> > > rus...@ho...>
>> > > > >> wrote:
>> > > > >>
>> > > > >>>
>> > > > >>> Wouldn't this be solved by checking for references as well in
>> > > addition
>> > > > >>> to the equals()?
>> > > > >>>
>> > > > >>> ----- Reply message -----
>> > > > >>> From: "Aidan Delaney" <ai...@on...>
>> > > > >>> To: "jgr...@li..." <
>> > > > >>> jgr...@li...>
>> > > > >>> Subject: [jgrapht-users] some potential speed improvements for
>> > > > >>> containsEdge(u, v) and getEdge(u, v)
>> > > > >>> Date: Wed, Apr 6, 2016 01:33
>> > > > >>>
>> > > > >>>
>> > > > >>> Dear all,
>> > > > >>> As far as I recall (and I'm happy to be wrong) overriding hash
>> code
>> > > for
>> > > > >>> Edge leads to Bad Things (tm). JGraphT is a general graph
>> > > > >>> implementation. As such, it doesn't assume that you're graph is
>> not
>> > > a
>> > > > >>> multigraph.
>> > > > >>> In the JGraphT implementation, if we add edge e between
>> > > vertex
>> > > > >>> v1 and v2 we -- unsurprisingly -- have an edge between v1 and v2.
>> > > If I
>> > > > >>> create another edge, e', which is between v1 and v2 and
>> identical to
>> > > e
>> > > > >>> in all other respects, then my graph has two edges, e and e',
>> between
>> > > > >>> v1 and v2. By overriding hashCode, e and e' would be equal
>> edges and
>> > > > >>> you'd only have one edge between v1 and v2.
>> > > > >>> I hope the above helps. I'll read it again after my
>> first
>> > > > >>> coffee to see if it's actually intelligible.
>> > > > >>>
>> > > > >>> --
>> > > > >>> Dr Aidan Delaney
>> > > > >>> Principal Lecturer
>> > > > >>> Computing, Engineering & Maths
>> > > > >>> University of Brighton
>> > > > >>>
>> > > > >>> @aidandelaney
>> > > > >>>
>> > >
>> > > --
>> > > Information System on Graph Classes and their Inclusions (ISGCI)
>> > > http://www.graphclasses.org
>> > >
>> > >
>> > >
>> ------------------------------------------------------------------------------
>> > > Find and fix application performance issues faster with Applications
>> > > Manager
>> > > Applications Manager provides deep performance insights into multiple
>> > > tiers of
>> > > your business applications. It resolves application problems quickly
>> and
>> > > reduces your MTTR. Get your free trial!
>> http://pubads.g.doubleclick.net/
>> > > gampad/clk?id=1444514301&iu=/ca-pub-7940484522588532
>> > > _______________________________________________
>> > > jgrapht-users mailing list
>> > > jgr...@li...
>> > > https://lists.sourceforge.net/lists/listinfo/jgrapht-users
>> > >
>>
>> --
>> Information System on Graph Classes and their Inclusions (ISGCI)
>> http://www.graphclasses.org
>>
>>
>> ------------------------------------------------------------------------------
>> Find and fix application performance issues faster with Applications
>> Manager
>> Applications Manager provides deep performance insights into multiple
>> tiers of
>> your business applications. It resolves application problems quickly and
>> reduces your MTTR. Get your free trial! http://pubads.g.doubleclick.net/
>> gampad/clk?id=1444514301&iu=/ca-pub-7940484522588532
>> _______________________________________________
>> jgrapht-users mailing list
>> jgr...@li...
>> https://lists.sourceforge.net/lists/listinfo/jgrapht-users
>>
>
|