xBimTeam / XbimEssentials

A .NET library to work with data in the IFC format. This is the core component of the Xbim Toolkit
https://xbimteam.github.io/
Other
485 stars 172 forks source link

Is it OK to modify IfcStore in Parallel.ForEach loop? #518

Closed shichongdong closed 5 months ago

shichongdong commented 12 months ago

I want to use XbimGeometryEngine to create solids for many entities, so I will use Parallel.ForEach to improve performance.

In earch Parallel.ForEach loop, it will use IXbimGeometryEngine.CreateXXX to create IXbimSolid, so I need modify IfcStore to add Ifc solids.

I have done the code and make some testings. It works with some random exceptions, so my questions are:

  1. Is it OK to do like that?
  2. If it's OK, how can I fix the random exceptions?

Assemblies and versions affected:

"Xbim.Essentials" Version="6.0.423-develop" "Xbim.Geometry" Version="5.1.666-develop"

// GeometryFactory is wrapper around a new XbimGeometryEngine and a new IfcStore object
using (var geometryFactory = new GeometryFactory())
using (var txn = geometryFactory.Model.BeginTransaction())
{
                    // entities is a collection of objects
                    Parallel.ForEach(entities, new ParallelOptions(), entity =>
                    {
                              // create solid with XbimGeometryEngine and IfcStore
                    });
                    tx.commit();
}
andyward commented 12 months ago

Sure, it's basically what we do here: https://github.com/xBimTeam/XbimGeometry/blob/c4df872dc60f3f440b10fbb8e624055b34ae68eb/Xbim.ModelGeometry.Scene/Xbim3DModelContext.cs#L813

The underlying OpenCascade (OCC) geometry engine is generally supposed to be thread safe, but that's not to say there aren't edge cases / bugs.

It may be worth using our latest v6 Geometry in the netcore branch which has the latest OCC7.6

If you're seeing intermittent errors maybe try to isolate the inputs triggering the exception. It's usually invalid geometry that triggers issues (which can in turn manifest as threading issues)

shichongdong commented 12 months ago

@andyward , thanks for your reply. OK, I will check the code. BTW, I can't find v6 Geometry in NuGet, so where can I find the pre-build packages or I need build it myself?

andyward commented 12 months ago

Sorry we only have a private nuget repo currently for v6. It's actually simple to build it yourself (and much quicker than the v5 branches in develop / master). Will see about getting it into Myget soon.

6ther commented 5 months ago

` using (var tran = model.BeginTransaction("test")) { Parallel.ForEach(entities, new ParallelOptions(), entity => { //new instances var newInstance = model.Instances.New(); }); tx.commit(); }

` 1712653302224 it throw exception like the picture.

andyward commented 5 months ago

Looks to me that MultiValueDictionary is not thread safe. If you wanted to submit a PR replacing the inner Dictionary with a ConcurrentDictionary that should help.

Is it Revit that is imposing the Parallel foreach on your code, or is it your decision? While Parallel will help with the Geometry compute, I don't know how much gain you'll get with basic I/O.

martin1cerny commented 5 months ago

Hi @6ther,

while it is safe to read data in parallel, modifying the model is not designed to work in parallel. The transaction is supposed to be linear, which cannot be guaranteed in parallel.

Creating/modifying data is usually very fast, so the common approach is to get the data ready (in parallel if that requires some heavy processing) and than create the IFC in one go.

What is your scenario for creating data in parallel? I can see you are trying to export some data from Revit and that is not very parallel friendly even for reading as I think that Addins run in the UI thread context.

6ther commented 5 months ago

Hi @6ther, 你好,

while it is safe to read data in parallel, modifying the model is not designed to work in parallel. The transaction is supposed to be linear, which cannot be guaranteed in parallel.虽然并行读取数据是安全的,但修改模型并不是为了并行工作而设计的。事务应该是线性的,这不能保证并行。

Creating/modifying data is usually very fast, so the common approach is to get the data ready (in parallel if that requires some heavy processing) and than create the IFC in one go.创建/修改数据通常非常快,因此常见的方法是准备好数据(如果需要进行一些繁重的处理,则并行进行),然后一次性创建IFC。

What is your scenario for creating data in parallel? I can see you are trying to export some data from Revit and that is not very parallel friendly even for reading as I think that Addins run in the UI thread context.并行创建数据的场景是什么?我可以看到你正试图从Revit导出一些数据,这是不是非常并行友好,甚至阅读,因为我认为插件运行在UI线程上下文中。

Yes, i try to export some data from Revit and i think that calling RevitAPI is not parallel in my code to get data ready, but want to write the prepared data to IFC in parallel. Is thak ok?

martin1cerny commented 5 months ago

No, as I said earlier, none of our IModel implementations support parallel creation/editing.

6ther commented 5 months ago

No, as I said earlier, none of our IModel implementations support parallel creation/editing.不,正如我之前所说的,我们的 IModel 实现都不支持并行创建/编辑。

thanks a lot. I understand.