OCCT7 +TBB

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: OCCT7 +TBB

Post by triplus »

sgrogan wrote:Thanks triplus!
You're welcome.
I get multi-threading activity w/ 6.9.1 /wo TBB on Ubuntu and w/ 7.0.0 w/TBB on Win. Both using Draft Array w/ Fuse set.
Thanks for testing.

P.S. I did some work on multiCut today and it looks like i will be able to implement this method.
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: OCCT7 +TBB

Post by triplus »

OK here is my plan what i plan to do and some guidance for future developers. I will add remaining methods to Part WB and i won't mess with the existing ones. Thanks to @shoogen and his multiFuse method this shouldn't be that hard to achieve. After i will provide some tests. Therefore it should be easy to test things out by running a code snippet in console. Things like comparing results when OCC/OCE is build with TBB or without therefore should become easy. I am aiming at:

From GUI:
  • Draft Array Fuse
  • Set of Part Join (Connect...) and boolean tools (Boolean Fragments...) added by @DeepSOIC.
And from the Python console using:
  • Part multiFuse, multiCut, multiCommon and multiSection.
  • Part generalFuse
I feel that is a nice start. In this pull request:

https://github.com/FreeCAD/FreeCAD/pull/324

A list of places can be seen where we use methods that don't support any of this:

viewtopic.php?f=8&t=11237#p90502

Migrating from current methods to new ones will be needed in the future. Respective Part WB methods therefore can be used as a reference (or directly). I don't plan to do that myself for now as there could be caveats involved. Best to leave this to respective workbench developers/users to decide. And it really is a nice experience when enabling parallel mode and seeing the results. Therefore i decided to leave some fun to other developers and eager user to twist their arm a bit to look into it.
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: OCCT7 +TBB

Post by triplus »

Code: Select all

# @triplus - 2016
# Boolean Benchmark

import time
import Part
import FreeCAD
from FreeCAD import Base

cols = 10
rows = 10

# ----------------------------------------

x = 0
y = 0
z = 0
h = 0

results = []
shape = ["Box", "Cylinder", "Sphere"]

for i in shape:

    shapes = []

    if i == "Cylinder":
        x = 5
        y = 5
    elif i == "Sphere":
        x = 5
        y = 5
        z = 5
    else:
        pass

    c = cols

    while c:

        r = rows

        while r:

            if i == "Box":
                shapes.append(Part.makeBox(10, 10, 10, Base.Vector(x, y, z)))
            elif i == "Cylinder":
                shapes.append(Part.makeCylinder(5, 10, Base.Vector(x, y, z)))
            elif i == "Sphere":
                shapes.append(Part.makeSphere(5, Base.Vector(x, y, z)))
            else:
                pass

            r -= 1
            x += 20

        if i == "Box":
            x = 0
        else:
            x = 5

        y += 20
        c -= 1

    compound = Part.makeCompound(shapes)

    box = Part.makeBox(18 * cols, 18 * rows, 8, Base.Vector(5, 5, 1))

    if i == "Box":
        print "----------------------------------------"
        print "Size: " + str(cols) + " x " + str(rows)
        print ""
        print "Above 100%: multi method is faster."
        print "Below 100%: multi method is slower."
        print "----------------------------------------"
        print "Test Box"
    elif i == "Cylinder":
        print "Test Cylinder"
    elif i == "Sphere":
        print "Test Sphere"
    else:
        pass

    print "----------------------------------------"

    t = time.time()
    result = box.cut(compound)
    timeCut = time.time() - t

    results.append(result)

    if hasattr(box, 'multiCut'):

        t = time.time()
        result = box.multiCut([compound])
        timeMultiCut = time.time() - t

        results.append(result)

        print str(timeCut) + "s - cut"
        print str(timeMultiCut) + "s - multiCut"
        print str(int(timeCut / timeMultiCut * 100)) + "%"
    else:
        print str(timeCut) + "s - cut"
        print "multiCut method not avaliable."

    print ""

    t = time.time()
    result = box.common(compound)
    timeCommon = time.time() - t

    results.append(result)

    if hasattr(box, 'multiCommon'):

        t = time.time()
        result = box.multiCommon([compound])
        timeMultiCommon = time.time() - t

        results.append(result)

        print str(timeCommon) + "s - common"
        print str(timeMultiCommon) + "s - multiCommon"
        print str(int(timeCommon / timeMultiCommon * 100)) + "%"
    else:
        print str(timeCommon) + "s - common"
        print "multiCommon method not avaliable."

    print ""

    t = time.time()
    result = box.fuse(compound)
    timeFuse = time.time() - t

    results.append(result)

    if hasattr(box, 'multiFuse'):

        t = time.time()
        result = box.multiFuse([compound])
        timeMultiFuse = time.time() - t

        results.append(result)

        print str(timeFuse) + "s - fuse"
        print str(timeMultiFuse) + "s - multiFuse"
        print str(int(timeFuse / timeMultiFuse * 100)) + "%"
    else:
        print str(timeFuse) + "s - fuse"
        print "multiFuse method not avaliable."

    print ""

    t = time.time()
    result = box.section(compound)
    timeSection = time.time() - t

    results.append(result)

    if hasattr(box, 'multiSection'):

        t = time.time()
        result = box.multiSection([compound])
        timeMultiSection = time.time() - t

        results.append(result)

        print str(timeSection) + "s - section"
        print str(timeMultiSection) + "s - multiSection"
        print str(int(timeSection / timeMultiSection * 100)) + "%"
    else:
        print str(timeSection) + "s - section"
        print "multiSection method not avaliable."

    print "----------------------------------------"

for i in results:

    placement = FreeCAD.Placement()
    placement.move(FreeCAD.Vector(0, 0, h))
    i.Placement = placement
    Part.show(i)

    h += 60
BooleanBenchmark.png
BooleanBenchmark.png (174.1 KiB) Viewed 4036 times
Pull request: Add Part BOA multiCut, multiCommon and multiSection methods
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: OCCT7 +TBB

Post by triplus »

I adapted the benchmark to align it with the actual changes introduced in FreeCAD. I made it more simple and split it in two pieces. Each piece basically does only one thing. I used fuse as a reference (but cut, section and common can be used instead by changing 1 line in code).

Fuse (like it always was):

Code: Select all

# @triplus - 2016, 2017
# Boolean Benchmark

import time
import Part
from FreeCAD import Base

cols = 20
rows = 20

# ----------------------------------------

x = 5
y = 5
z = 5
h = 0
c = cols
r = rows
shapes = []

while c:
    r = rows
    while r:
        shapes.append(Part.makeSphere(5, Base.Vector(x, y, z)))
        x += 20
        r -= 1

    x = 5
    y += 20
    c -= 1

compound = Part.makeCompound(shapes)

box = Part.makeBox(18 * cols, 18 * rows, 8, Base.Vector(5, 5, 1))

t = time.time()
result = box.fuse(compound)  # fuse, cut, common and section
timeBOA = time.time() - t
print(str(timeBOA) + "s - " + str(cols) + "x" + str(rows))

Part.show(result)
Fuse with multiple arguments (single/multiple argument(s) can be provided and additional tolerance value is supported):

Code: Select all

# @triplus - 2016, 2017
# Boolean Benchmark

import time
import Part
from FreeCAD import Base

cols = 20
rows = 20

# ----------------------------------------

x = 5
y = 5
z = 5
h = 0
c = cols
r = rows
shapes = []

while c:
    r = rows
    while r:
        shapes.append(Part.makeSphere(5, Base.Vector(x, y, z)))
        x += 20
        r -= 1

    x = 5
    y += 20
    c -= 1

box = Part.makeBox(18 * cols, 18 * rows, 8, Base.Vector(5, 5, 1))

try:
    t = time.time()
    result = box.fuse(shapes)  # fuse, cut, common and section
    timeBOA = time.time() - t
    print(str(timeBOA) + "s - " + str(cols) + "x" + str(rows))
    Part.show(result)
except:
    print("Multiple arguments: N/A")
Main purpose is to test if multithreading works (when multiple arguments are used this usually results in multithreading). It can be used to compare efficiency between the method used (OSD_Parallel (available by default), TBB or with OCE additional option OpenMP). Or to compare the results between different versions of OCC/OCE.

OCE 0.18:
Options.png
Options.png (12.96 KiB) Viewed 3759 times
P.S. As mentioned in the pull request discussion for common/section. When multiple arguments are provided s1 AND (s2 OR s3) is the result.
Last edited by triplus on Sat Jan 21, 2017 2:48 am, edited 1 time in total.
User avatar
sgrogan
Veteran
Posts: 6499
Joined: Wed Oct 22, 2014 5:02 pm

Re: OCCT7 +TBB

Post by sgrogan »

Great test, Thanks!
"fight the good fight"
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: OCCT7 +TBB

Post by easyw-fc »

triplus wrote:I adapted the benchmark to align it with the actual changes introduced in FreeCAD.
....
Fuse (like it always was): ....
Hi @triplus
I'm using some fuse routines in some of my macro...

with this fuse code:

Code: Select all

import time
import FreeCAD,FreeCADGui,Part

def say(msg):
    FreeCAD.Console.PrintMessage(msg)
    FreeCAD.Console.PrintMessage('\n')

say("fusing... ")
__objs__=[]
doc=FreeCAD.ActiveDocument
for obj in doc.Objects:
    FreeCADGui.Selection.addSelection(obj)
    __objs__.append(obj)

t = time.time()
doc.addObject("Part::MultiFuse","Fusion")
doc.Fusion.Shapes = __objs__
doc.recompute()
timeP = time.time() - t
say("fusing time = "+str(timeP) + "s")
your multithreading fuse code will be triggered?
thx
Maurice
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: OCCT7 +TBB

Post by triplus »

Hi Maurice.

No AFAIK your example won't do it and i don't know if there is any control available on FreeCAD/Part object level over this. You need to do something like this instead:

Code: Select all

import Part
import time

s = Gui.Selection.getSelection()

base = s.pop(0).Shape

shapes = []

for i in s:
    shapes.append(i.Shape)

t = time.time()
shape = base.fuse(shapes)
print(time.time() - t)

Part.show(shape)
Test A:

Create new document and run the benchmark 3 times:

https://forum.freecadweb.org/viewtopic. ... 30#p154815

After run your snippet.

Test B:

Create new document and run the benchmark 3 times:

https://forum.freecadweb.org/viewtopic. ... 30#p154815

After select the results in tree view (3 objects) and run the snippet i provided.

P.S. Compare the results from Test A and B to see if there is any difference.
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: OCCT7 +TBB

Post by easyw-fc »

triplus wrote:P.S. Compare the results from Test A and B to see if there is any difference.
The test seems very useful!
I tried to fuse a lot of models using

Code: Select all

doc.addObject("Part::MultiFuse","Fusion")
doc.Fusion.Shapes = __objs__

compared to the multi-fuse method

Code: Select all

shape = base.fuse(shapes)
and the difference is 84sec vs 212sec... so it may worth to use your method... :D
What I miss atm is that the shape doesn't have the colors of the items to be fused...

Is there a way to fuse using multi-fuse method but conserve the colors?
(please consider that I'm going to fuse single objects with already multi-colors as in the file attached)
test-colors.FCStd
(25.48 KiB) Downloaded 87 times
thx again
Maurice
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: OCCT7 +TBB

Post by triplus »

easyw-fc wrote:and the difference is 84sec vs 212sec... so it may worth to use your method... :D
Yeah i know how you feel. It was a multi-year effort to get to here and when i first experienced BOA multithreading and had data to demonstrate it's working my heart skipped a bit. Surely a story one can tell to grandchildren. I am really happy i could contribute small part to make this happen.

There was a time in the past we thought this phase comes down to the famous and elusive BOA mutlithreading:

Code: Select all

Part.show(shape)
I am glad that turned out not to be it!
What I miss atm is that the shape doesn't have the colors of the items to be fused...
Support is low level ATM. But it's there. I will leave it to the next generation of FreeCAD BOA pioneers to progress this further. It's great achievement when you make something like that work and the next generation shouldn’t be deprived of that.

When you will open Part WB and will select some geometry and click on one of the BOA tools. Once there will be a property and you will be able to change it from Standard to Multiple arguments and in addition will be able to provide a tolerance value. Then your snippet above will likely work by slightly modifying it. And you should get color support like you have it now. Further if in the future tools like Pad or Pocket or Groove ... will work when applied on multiple solids or when used on compounds in the PartDesign workbench. The tools and the end users would likely benefit if the same properties would be exposed for the resulting features. But this are only some pointers from my side and as for the rest i will let the next generation of FreeCAD BOA pioneers to figure that out!
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: OCCT7 +TBB

Post by easyw-fc »

triplus wrote: When you will open Part WB and will select some geometry and click on one of the BOA tools. Once there will be a property and you will be able to change it from Standard to Multiple arguments and in addition will be able to provide a tolerance value. Then your snippet above will likely work by slightly modifying it. And you should get color support like you have it now.
Ok, I will wait for it ...
Anyway for the moment thanks for the great improvement in multi-threading... I agree with you that is very important for FC :D

I already found a great speed up comparing fusing operation in FC 0.16 64b OCC 0.68 and in FC 0.17 64b OCC 0.70 ... so anything further will be an other plus :)

thx again
Maurice
Post Reply