Dear Michael,

Thank you for your detailed response and the multiple clarifications.

I was considering some of these concerns (but did not want to add an even 
longer amount of tests) but there were some (like the limit expression) I 
was not aware of at all.

It has been very helpful in furthering my understanding of Neo.

Cheers,


On Monday, March 17, 2014 4:05:41 PM UTC, [email protected] wrote:

> *Author's note:* Even though this post seems to be partially delving into 
> the technical characteristics of Cypher it has been created as an initiator 
> for discussion on relative scalability and performance of native vs. cypher 
> so I believed it should be placed in the group forum as opposed to stack 
> overflow. If the coordinators disagree on this decision please move it to 
> SO. Thank you. 
> ** neo4j version, library versions, OS, jdk:*
>
> Neo4j Stable Release 2.0.1 [community edition], with relevant libraries.
> OS: windows 7 (64 bit), service pack 1, 8gb ram, i5 quad core desktop 
> (without hyper-threading).
> jdk: java 7 (jdk1.7.0_51).
> all applications are in java, using embedded database instance (java 
> runtime and neo4j vm/other arguments are default (empty) in each case).
>
> Fellow Neo4J enthusiasts,
> After performing various simple benchmarks it would appear that using 
> native java to query a neo graph outperforms doing the same using cypher. 
> This would normally be surprising in a relational context but i can see 
> how it can make sense in this graph context (as virtual memory is heavily 
> used etc.). 
> As such I would like to know if this is in fact the case for the majority 
> of queries that can be made in neo4j, or whether I am missing something 
> glaringly obvious in my tests.
>  
> *A summary of my test parameters is as follows:*
>
>    - a graph of 100k nodes (each having one property) is used as a 
>    baseline (relationships and more properties are added for later 
>    experiments).
>    - after graph insertion the jvm is restarted before querying in order 
>    to clear virtual memory.
>    - the same query is performed using cypher and neo4j in each case.
>    - the jvm is restarted for every subsequent query experiment to ensure 
>    virtual memory is wiped (vm is embedded into java heap in windows by 
>    default).
>    - during each experiment 10 repetitions of the query are performed, 
>    with each repetition having a small change in one of the query variables. 
>    This is to test execution time for the first instance (hence before any 
>    nodes are in virtual memory) as well as subsequent calls where most of the 
>    nodes queried are in RAM.
>    - execution time is recorded using System.nanotime() for accuracy.
>    - the cypher queries have been optimised as much as possible (with my 
>    current knowledge) using a single execution engine per experiment and the 
>    changing variable is injected as a parameter to the query.
>
> *Hypothesis:*
> For all experiments (details below) both for initial query execution as 
> well as subsequent executions, the native java api outperforms cypher.
>
> *The following queries were performed (code presented below, this is a 
> summary in text for conciseness):*
>
>    - get me all nodes which have property 'i' equal to {max}. NB: The 
>    variable {max} ranges from 0 to 100k in the nodes and the queries test all 
>    possible ranges to ensure that the java execution does not have a 
>    preferential treatment by finding results close to the top of the node 
> file 
>    (as is shown in the results).
>    - get me all nodes which have property 'i' equal to {max}, through a 
>    single indexed node. In my context (model-driven engineering research) it 
>    is common to have a single (or very few) starting points for a query, so 
> my 
>    second test simulated this behaviour by creating a single "source" node 
>    with relationship to the 100k nodes to be queried. as such, the query goes 
>    to the lucene index to find the node and then traverses the relationship 
> it 
>    has to the 100k nodes in order to be executed. This also avoids using the 
>    GlobalGraphOperations.at(database).getAllNodes() operation in java (which 
>    is useful as it would never be used in my context).
>    - get me all nodes which have property 'i' equal to {max} and 
>    relationship named (of relationship type with name) {name} to another 
> node. 
>    this is a simple extension to the first query which uses a one-hop 
>    traversal as well.
>    - get me all nodes which have property 'i' equal to {max} and 'i2' 
>    equal to {max2}.
>    - get me all nodes which have property 'i' equal to {max} and 'i2' 
>    equal to {max2} and relationship named {name} to another node. 
>
> *Results:*
>
> I will only present the detailed results of the first query as it would 
> get tediously long to present them all (they are all included as a snippet 
> link below), and as mentioned above, all of them seem to support the 
> statement that java 
>
> outperforms cypher.
>
> *Query 1 results:*
> Java (microseconds): 15415  result 'i': 1  testing equality on: 1
> Java (microseconds): 288  result 'i': 2  testing equality on: 2
> Java (microseconds): 333  result 'i': 3  testing equality on: 3
> Java (microseconds): 304  result 'i': 4  testing equality on: 4
> Java (microseconds): 303  result 'i': 5  testing equality on: 5
> Java (microseconds): 319  result 'i': 6  testing equality on: 6
> Java (microseconds): 331  result 'i': 7  testing equality on: 7
> Java (microseconds): 355  result 'i': 8  testing equality on: 8
> Java (microseconds): 368  result 'i': 9  testing equality on: 9
> Java (microseconds): 385  result 'i': 10  testing equality on: 10
>
> Java (microseconds): 175027  result 'i': 1000  testing equality on: 1000
> Java (microseconds): 146228  result 'i': 2000  testing equality on: 2000
> Java (microseconds): 126249  result 'i': 3000  testing equality on: 3000
> Java (microseconds): 98282  result 'i': 4000  testing equality on: 4000
> Java (microseconds): 69881  result 'i': 5000  testing equality on: 5000
> Java (microseconds): 38536  result 'i': 6000  testing equality on: 6000
> Java (microseconds): 24090  result 'i': 7000  testing equality on: 7000
> Java (microseconds): 25140  result 'i': 8000  testing equality on: 8000
> Java (microseconds): 25849  result 'i': 9000  testing equality on: 9000
> Java (microseconds): 26664  result 'i': 10000  testing equality on: 10000
>
> Java (microseconds): 1704711  result 'i': 99997  testing equality on: 99997
> Java (microseconds): 119149  result 'i': 99998  testing equality on: 99998
> Java (microseconds): 49827  result 'i': 99999  testing equality on: 99999
> Java (microseconds): 60392  result 'i': -1   testing equality on: 100000
> Java (microseconds): 42451  result 'i': -1   testing equality on: 100001
> Java (microseconds): 35205  result 'i': -1   testing equality on: 100002
> Java (microseconds): 36279  result 'i': -1   testing equality on: 100003
> Java (microseconds): 34999  result 'i': -1   testing equality on: 100004
> Java (microseconds): 35179  result 'i': -1   testing equality on: 100005
> Java (microseconds): 45571  result 'i': -1   testing equality on: 100006
>
> Cypher [prepared with 1 execution engine] (microseconds): 2688552  result 
> 'i': 100  testing equality on: 100
> Cypher [prepared with 1 execution engine] (microseconds): 134839  result 
> 'i': 200  testing equality on: 200
> Cypher [prepared with 1 execution engine] (microseconds): 116128  result 
> 'i': 300  testing equality on: 300
> Cypher [prepared with 1 execution engine] (microseconds): 96070   result 
> 'i': 400  testing equality on: 400
> Cypher [prepared with 1 execution engine] (microseconds): 111627  result 
> 'i': 500  testing equality on: 500
> Cypher [prepared with 1 execution engine] (microseconds): 116955  result 
> 'i': 600  testing equality on: 600
> Cypher [prepared with 1 execution engine] (microseconds): 98720   result 
> 'i': 700  testing equality on: 700
> Cypher [prepared with 1 execution engine] (microseconds): 96051   result 
> 'i': 800  testing equality on: 800
> Cypher [prepared with 1 execution engine] (microseconds): 106406  result 
> 'i': 900  testing equality on: 900
> Cypher [prepared with 1 execution engine] (microseconds): 97068   result 
> 'i': 1000  testing equality on: 1000
>
> Cypher [prepared with 1 execution engine] (microseconds): 2651371  result 
> 'i': 1000  testing equality on: 1000
> Cypher [prepared with 1 execution engine] (microseconds): 121623  result 
> 'i': 2000  testing equality on: 2000
> Cypher [prepared with 1 execution engine] (microseconds): 95211   result 
> 'i': 3000  testing equality on: 3000
> Cypher [prepared with 1 execution engine] (microseconds): 79345   result 
> 'i': 4000  testing equality on: 4000
> Cypher [prepared with 1 execution engine] (microseconds): 88915   result 
> 'i': 5000  testing equality on: 5000
> Cypher [prepared with 1 execution engine] (microseconds): 100527  result 
> 'i': 6000  testing equality on: 6000
> Cypher [prepared with 1 execution engine] (microseconds): 77890   result 
> 'i': 7000  testing equality on: 7000
> Cypher [prepared with 1 execution engine] (microseconds): 77430   result 
> 'i': 8000  testing equality on: 8000
> Cypher [prepared with 1 execution engine] (microseconds): 76451   result 
> 'i': 9000  testing equality on: 9000
> Cypher [prepared with 1 execution engine] (microseconds): 86732   result 
> 'i': 10000  testing equality on: 10000
>
> As we can clearly see, Java can "cheat" on low equality tests (as we break 
> after finding the node as we assume (and know in our context) 'i' is 
> unique) but more interestingly even when it fails (checking i > 100k) it is 
> still roughly 2 
>
> times as fast as cypher both for initial queries and subsequent ones (this 
> is a good test for non-unique properties too as it forces java to iterate 
> through all of the nodes present).
> This shows two things in my view:
> 1) Java can optimise for unique results. As far as I am aware cypher 
> cannot be told to stop when it finds a result we know is unique (such as an 
> ISBN of a book for example or any other unique property in a node).
> 2) For non-unique results (or for a failed query) it is still faster than 
> cypher.
>
> After getting these results my curiosity prompted me to expand the scope 
> by adding relationships and a second attribute to see if the same trend 
> continues, and it did.
>
> *Links to code snippets:*
> first query:
> https://gist.github.com/anonymous/9601553
>
> second query:
> https://gist.github.com/anonymous/9601556
>
> third query:
> https://gist.github.com/anonymous/9601571
>
> fourth:
> https://gist.github.com/anonymous/9601581
>
> fifth:
> https://gist.github.com/anonymous/9601590
>
> entire result set link:
> https://gist.github.com/anonymous/9601486
>
> *Discussion:*
>
> My main question is whether these results are to be expected (or even 
> obvious to some) which would mean I will just use native java in my 
> application.
> If my results are wrong/misleading I would appreciate knowing why but if 
> not, a discussion on how to improve cypher to attempt to close the gap may 
> be useful.
>
> Other notes and observations I had (non-conclusive as the tests performed 
> were not as thorough as the above):
> using 'count' in cypher seems to destroy execution time (whereas normally 
> in sql it improves it).
> adding depth to a cypher search (for example going from (a)-[]->(b) to 
> (a)-[]->(b)-[]->(c)) seems to scale a lot worst in cypher than java.
>
> Thank you for reading this wall,
>
> Costas
>
>  
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Neo4j" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to