Based on Olli's report, there are performance issues when using octree with larger meshes. Based on his silicone2.xxx pickle and a short testing/profiling script (attached), the following profiling data were gathered (sorted by cumulative time, the first line is total test time; unpickling the field takes about 5s). The changes were merged in https://sourceforge.net/p/mupif/code/ci/5e6bcd3beaf44471f1e86ce3fb4fb8f5ffcdf6db/ .
@Olli, please test if you are getting a reasonable speedup, and whether some further tunin/rewriting would help you.
To use minieigen.AlignedBox3, do "apt-get install python-minieigen" (Debian/Ubuntu), "pip install minieigen" (Linux). For Windows, just skip it, unless you are fluent with distutils. Docs at http://eudoxos.github.io/minieigen/ , it is a wrapper around Eigen (http://eigen.tuxfamily.org).
The lookup itself seems to be quite fast (something like 10k/sec for small random bboxes), the initial localizer construction is quite slow (34s in the last version).
Cheers, Václav
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 145.422 145.422 <string>:1(<module>)
1 0.018 0.018 145.422 145.422 octest.py:4(testOctree)
1 0.589 0.589 138.229 138.229 Mesh.py:308(giveCellLocalizer)
69994 0.043 0.000 122.429 0.002 Octree.py:240(insert)
3798050/69994 9.670 0.000 122.386 0.002 Octree.py:107(insert)
3814658 2.628 0.000 61.938 0.000 Octree.py:101(containsBBox)
1737460 21.848 0.000 51.911 0.000 Cell.py:109(getBBox)
16228554 5.384 0.000 31.870 0.000 newrange.py:140(__iter__)
3814659 11.890 0.000 30.634 0.000 Octree.py:87(giveMyBBox)
3853331 10.458 0.000 28.938 0.000 BBox.py:67(intersects)
16228554 17.207 0.000 26.486 0.000 newrange.py:149(__init__)
16228554 21.680 0.000 25.157 0.000 newrange.py:37(__init__)
54479486 11.447 0.000 16.439 0.000 newrange.py:155(next)
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 132.569 132.569 <string>:1(<module>)
1 0.022 0.022 132.569 132.569 octest.py:4(testOctree)
1 1.284 1.284 125.698 125.698 Mesh.py:308(giveCellLocalizer)
69994 0.042 0.000 107.593 0.002 Octree.py:240(insert)
3798050/69994 10.477 0.000 107.552 0.002 Octree.py:107(insert)
1741983 25.050 0.000 55.990 0.000 Cell.py:109(getBBox)
3815090 3.084 0.000 42.533 0.000 Octree.py:101(containsBBox)
3815091 17.479 0.000 36.392 0.000 Octree.py:87(giveMyBBox)
12389602 4.201 0.000 25.086 0.000 newrange.py:140(__iter__)
12389602 13.498 0.000 20.884 0.000 newrange.py:149(__init__)
12389602 17.298 0.000 20.428 0.000 newrange.py:37(__init__)
46279839 10.000 0.000 14.618 0.000 newrange.py:155(next)
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 78.486 78.486 <string>:1(<module>)
1 0.021 0.021 78.486 78.486 octest.py:4(testOctree)
1 0.904 0.904 73.277 73.277 Mesh.py:308(giveCellLocalizer)
69994 0.032 0.000 68.863 0.001 Octree.py:240(insert)
3798050/69994 10.274 0.000 68.831 0.001 Octree.py:107(insert)
3814786 2.934 0.000 42.534 0.000 Octree.py:101(containsBBox)
3814787 17.946 0.000 36.620 0.000 Octree.py:87(giveMyBBox)
7373065 2.418 0.000 14.832 0.000 newrange.py:140(__iter__)
7373065 7.919 0.000 12.414 0.000 newrange.py:149(__init__)
7373065 10.309 0.000 12.252 0.000 newrange.py:37(__init__)
26213957 5.809 0.000 8.403 0.000 newrange.py:155(next)
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 39.334 39.334 <string>:1(<module>)
1 0.021 0.021 39.334 39.334 octest.py:4(testOctree)
1 0.890 0.890 34.235 34.235 Mesh.py:308(giveCellLocalizer)
69994 0.029 0.000 29.840 0.000 Octree.py:243(insert)
3798050/69994 8.732 0.000 29.810 0.000 Octree.py:110(insert)
3560426 1.216 0.000 7.061 0.000 newrange.py:140(__iter__)
3560426 3.834 0.000 5.845 0.000 newrange.py:149(__init__)
3813994 1.691 0.000 5.670 0.000 Octree.py:104(containsBBox)
Diff:
Reporting some further improvements (results were not tested, but they seem to be correct). The following results do NOT include unpickling, so add 5s to make it commensurable with the above profiles:
remove nested loops
https://sourceforge.net/p/mupif/code/ci/43743999c32f1cfcd157a279c6de8ee6ce9e7878/
better cell bbox loop
https://sourceforge.net/p/mupif/code/ci/5cd90e3b5a0a5423c982940e9d7c2859cf2169bc/
use c++ mupif.fastOctant.Octant
https://sourceforge.net/p/mupif/code/ci/ba933cdf82ba0004f44f1470a7c73f02af4def40/
In here, the hotspot (2.792s/3.619s) is calls to getBBox from within c++ for cells (it needs to go through python).
As explained in README.md, fastOctant must be manually enabled in setup.py (useCxx=True) and will only work on Linux. Needs Eigen installed in /usr/include/eigen3 (that's what package managers do), boost_python and a compiler understanding -std=c++11.
I did not test the result for correctness, but it is returning something quite sensible (a few hits for random boxes).
Improved profiling script attached.
Last edit: Václav Šmilauer 2015-11-13
I can see some nice improvement in speed in my code also:
Original was about 1 hour:
On the improved version(https://sourceforge.net/p/mupif/code/ci/009b1e460211bb85ec7562f47bf473a6cbd59d36/)
about 7 minutes.
Olli, thanks. Are the new (faster) results correct, based your quick look at them? As said, I did not test the implementation (it is mostly 1:1 from python), but I am happy if it works.
Did you encounter any problems installing minieigen or compiling the fastOctree module? It is something we would like to iron out before other people start using it.
I did not check the results yet. I had no problem in installing.
Using the most recent FastOctant-C++ module I see so degredation in performance to 9 minutes.
Also FastOctant does not support pickling. Could this cause problems with Pyro4?
Pickling should definitely be supported, I will add it just now. The question coming to my mind is what happens if a machine without fastOctact unpickles fastOctact.Octant sent over the wire.
PS for my own track: http://stackoverflow.com/questions/33753224/pickling-unpickling-alternative-api-compatible-class-implementations
Alright, I commited pickling support for fastOctant.
I think though that supporting the same pickle/unpickle interface for both Octant and fastOctant will be pain, and that mesh should really NOT transfer its own locator over the wire (it can be reconstructed locally, probably faster than transferred). For that,
Mesh.__getstate__would have to be defined, returning__dict__withoutvertexLocalizerandcellLocalizer.Another option: make everybody using the platform have proper compiler & boost::python? Including those on Windows.
I think making everyone use compiler on windows is just painful. Even with mingw and msys2 there will be compatibility issues. I think your suggestion not to pickle the localizers is the best way to go.
(Field pickling fixed by Vit just now. Please open a separate ticket for new issues next time.)
For compiler under Windows: I've been using (rarely, fortunately) mingw-w64-compiled modules (also with boost::python) in MSVC (official) python builds, and there were no issues. But it was painful to set up, agreed.
A belated note to the discussion, localizers were made to be not pickled with Mesh long time ago. Are there some outstanding issues with this ticket? The one I can think of is supporting non-3d domains (like 2d or such) which will be represented as 3d with fastOctant. Is that a problem?