Author Topic: Slow iteration on ObjectIds  (Read 11753 times)

0 Members and 1 Guest are viewing this topic.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Slow iteration on ObjectIds
« Reply #15 on: August 25, 2011, 09:26:34 AM »
Code: [Select]
//Database's ObjectIds iteration
[CommandMethod("FastIteration", CommandFlags.Modal)]
public void FastIteration() {
    Document dwg = acad.DocumentManager.MdiActiveDocument;
    Database TargetDb = dwg.Database;
    Editor ed = dwg.Editor;
    DateTime start = DateTime.Now;

    long amount = 0;//Database's ObjectIds common counter

    using (Transaction t = TargetDb.TransactionManager.StartTransaction()) {
        for (long i = TargetDb.BlockTableId.Handle.Value; i < TargetDb.Handseed.Value; i++) {
            ObjectId id = ObjectId.Null;

            //next line - Fatal error!!!
            int x = getAcDbObjectId17(TargetDb.UnmanagedObject, ref id, false, new Handle(i), 0);
            if (x != 0 && id.IsValid)
                ++amount;
        }
    }
    TimeSpan len = DateTime.Now - start;
    ed.WriteMessage(string.Format("Amount: {0}\nFast iteration time: {1} min. {2} sec.\n",
        amount, len.Minutes, len.Seconds));
}

//P/Invoke method for AutoCAD 2009 x86 (Alexander Rivilis's code)
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall,
   EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId17(IntPtr db, ref ObjectId id, bool createnew, Handle h, Int32 reserved);

I get fatall error :(

About my topic:

I have some methods, which operation I want to accelerate. Here one of them:

Code: [Select]
public Dictionary<Type, List<ObjectId>> GetByTypes(Func<Transaction, ObjectId, bool> requirement) {
    Dictionary<Type, List<ObjectId>> dict = new Dictionary<Type, List<ObjectId>>();
    using (Transaction t = TargetDb.TransactionManager.StartTransaction()) {
        for (long i = TargetDb.BlockTableId.Handle.Value; i < TargetDb.Handseed.Value; i++) {
            ObjectId id = ObjectId.Null;
            try {
                id = TargetDb.GetObjectId(false, new Handle(i), 0);
            }
            catch { continue; }

            if (id != ObjectId.Null &&  id.IsValid && !id.IsErased && requirement(t, id)) {
                Type type = t.GetObject(id, OpenMode.ForRead).GetType();
                if (!dict.Keys.Contains(type))
                    dict.Add(type, new List<ObjectId>());
                dict[type].Add(id);
            }
        }
    }
    return dict;
}

It method return ObjectIds, grouped on Type, which is key of dictionary record.

Somebody can help with P/Invoke?

 :-(


Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Slow iteration on ObjectIds
« Reply #16 on: August 25, 2011, 10:18:05 AM »
Problem is solved!

Code: [Select]
//WARNING: P/Invoke method FOR AUTOCAD 2009 x86 ONLY!!! (Alexander Rivilis's code)
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall,
   EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId17(IntPtr db, ref ObjectId id, bool createnew, ref Handle h, Int32 reserved);
// Define Command "AsdkCmd1"
[CommandMethod("AsdkCmd1")]
static public void test() // This method can have any name
{
    // Put your command code here
    Database db = HostApplicationServices.WorkingDatabase;
    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    DateTime start = DateTime.Now;

    long amount = 0;//common counter

    using (Transaction t = db.TransactionManager.StartTransaction()) {
        ObjectId id = new ObjectId();
        for (long i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++) {
            Handle h = new Handle(i);
            if (getAcDbObjectId17(db.UnmanagedObject, ref id, false, ref h, 0) == 0) {
                ++amount;
                id = db.GetObjectId(false, new Handle(i), 0);
                DBObject dbo= t.GetObject(id, OpenMode.ForRead);
            }
        }
    }
    TimeSpan len = DateTime.Now - start;
    ed.WriteMessage(string.Format("Amount: {0}\nFast iteration time: {1} min. {2} sec.\n",
        amount, len.Minutes, len.Seconds));

}
for other AutoCAD versions:
Code: [Select]
//Ниже приведён набор методов, с помощью которых можно получить значение идентификатора, минуя при этом генерацию
//исключения, если запрашиваемый идентификатор отсутствует в базе данных чертежа (это существенно ускоряет
//производительность)
//Низкий поклон Александру Ривилису, написавшему эти методы!!!!!
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall,
   EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId17x32(IntPtr db, ref ObjectId id, bool createnew, ref Handle h, Int32 reserved);

[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall,
  EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId17x64(IntPtr db, ref ObjectId id, bool createnew, ref Handle h, Int32 reserved);

[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall,
   EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId18x32(IntPtr db, ref ObjectId id, bool createnew, ref Handle h, Int32 reserved);

[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall,
  EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId18x64(IntPtr db, ref ObjectId id, bool createnew, ref Handle h, Int32 reserved);

public static int getAcDbObjectId(IntPtr db, ref ObjectId id, bool createnew, ref Handle h, Int32 reserved) {
    switch (Application.Version.Major) {
        case 17: {
                if (IntPtr.Size == 4) return getAcDbObjectId17x32(db, ref id, createnew, ref h, reserved);
                else return getAcDbObjectId17x64(db, ref id, createnew, ref h, reserved);
            }
        case 18: {
                if (IntPtr.Size == 4) return getAcDbObjectId18x32(db, ref id, createnew, ref h, reserved);
                else return getAcDbObjectId18x64(db, ref id, createnew, ref h, reserved);
            }
    }
    return -1;
}

Thank you all, and Alexander Rivilis!!!
« Last Edit: August 25, 2011, 11:07:21 AM by Andrey »

kaefer

  • Guest
Re: Slow iteration on ObjectIds
« Reply #17 on: August 25, 2011, 01:20:19 PM »
Code: [Select]
           if (getAcDbObjectId17(db.UnmanagedObject, ref id, false, ref h, 0) == 0) {
                ++amount;
                id = db.GetObjectId(false, new Handle(i), 0);  // <- Could you dump this line?

Firstly, congratulations!

What caught my attention now was the fact that you're doing the same thing twice, via p/invoke and for good measure the .NET way too. If I'm not completely mistaken; the second argument is actually out, and the fourth is ref. Not sure if this distinction carries over into C++ land.
Quote
Acad::ErrorStatus getAcDbObjectId(
    AcDbObjectId& retId,
    bool createIfNotFound,
    const AcDbHandle& objHandle,
    Adesk::UInt32 xRefId = 0
);


Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Slow iteration on ObjectIds
« Reply #18 on: August 25, 2011, 01:26:57 PM »
What caught my attention now was the fact that you're doing the same thing twice
Yes it my mistake. :) Thank you!

I have published the code and tests here (the text in Russian, but the code it is possible to read and understand).
« Last Edit: August 25, 2011, 01:32:50 PM by Andrey »

Jeff H

  • Needs a day job
  • Posts: 6144
Re: Slow iteration on ObjectIds
« Reply #19 on: August 25, 2011, 01:45:07 PM »
One thing that might help instead of storing types you could use strings and not have to open each object.
 
For example this just prints all the
ObjectId's in modelSpace ObjectID.ObjectClass.Name & ObjectID.ObjectClass.DxfName
 
For a line will print
Quote
AcDbLine LINE

Code: [Select]

              [CommandMethod("PrintAllModelSpaceTypes")]
        public void PrintAllModelSpaceTypes()
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            using (Transaction trx = db.TransactionManager.StartTransaction())
            {
               
                    BlockTable bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                    BlockTableRecord modelBtr = (BlockTableRecord)trx.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
                    foreach (ObjectId objId in modelBtr)
                    {               
                            ed.WriteMessage("\n{0} {1}", objId.ObjectClass.Name, objId.ObjectClass.DxfName);                       
                    }
                    trx.Commit();
                             
            }
        }
« Last Edit: August 25, 2011, 01:50:25 PM by Jeff H »

kaefer

  • Guest
Re: Slow iteration on ObjectIds
« Reply #20 on: August 26, 2011, 02:16:58 AM »
One thing that might help instead of storing types you could use strings and not have to open each object.

It does help indeed. For the benefit of those being on a platform slightly more up-to-date then there's another thing that helps: Database.TryGetObjectId. Fast it is too.

Code: [Select]
        [CommandMethod("Test")]
        static public void test()
        {
            Database db = HostApplicationServices.WorkingDatabase;
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
            long amount = 0;
            Dictionary<string, int> d = new Dictionary<string, int>();
            ObjectId id = ObjectId.Null;
            for (long i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++)
            {
                Handle h = new Handle(i);
                if (db.TryGetObjectId(h, out id) && !id.IsNull && id.IsValid && !id.IsErased)
                    {
                        string t = id.ObjectClass.DxfName;
                        amount++;       
                        if(d.ContainsKey(t))
                            d[t]++;
                        else
                            d.Add(t, 1);
                    }
            }
            sw.Stop();
            foreach(KeyValuePair<string,int> kvp in d)
                ed.WriteMessage("\n{0}: {1} ", kvp.Key, kvp.Value);
            ed.WriteMessage("\nTotal {0} objects in drawing, time elapsed: {1} ", amount, sw.Elapsed);
        }

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Slow iteration on ObjectIds
« Reply #21 on: August 26, 2011, 02:41:09 AM »
db.TryGetObjectId(h, out id)
You are magician!!!! Many thanks!!!!! It is the fastest variant, fastest all previous!!! (3 seconds).

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Slow iteration on ObjectIds
« Reply #22 on: August 26, 2011, 04:03:51 AM »
One thing that might help instead of storing types you could use strings and not have to open each object.
Thank you! It really very much has accelerated operation.