[C#] Object conversie performance

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Beste Tweakers,

Ik zit met een vreemd probleem. Ik deserialiseer een object collectie met 3000 objecten in minder dan een halve seconde. In die tijd is dus met file IO een XML file opgepakt en weer omgezet naar een object met een List van objecten.

Nu moet ik deze lijst op een gegeven moment omzetten naar een lijst met net iets andere objecten. Pseudo voorbeeld:

code:
1
2
3
4
5
6
public static AndereObjecten VertaalLijst(List<BronObjecten> bron)
{
  List<AndereObjecten> resultaat = new List<AndereObjecten>();
  bron.ForEach(obj=>resultaat.Add(new AndereObjecten(){waardeA=bron.WaardeC,waardeB=bron.WaardeQ});
  return resultaat;
}


En wat schetsts mijn verbazing..., dit duurt gewoon bijna 3 seconde! De typen over te zetten properties zijn gelijk, dus als waardeA = string dan in bron.WaardeC dat ook.

C# kan dus wel in een halve seconde via file IO een gedeserialiseerd bestand weer omzetten naar een object met 3000 objecten maar kan niet - in memory - een bron lijst met 3000 objecten omzetten naar een iets andere bron lijst met 3000 objecten.

Dat lijkt mij sterk... dus is de kans meestal groot aanwezig dat ik iets fout doe.

Hebben jullie enig idee wat de fout kan zijn of wat ik hier beter zou kunnen doen? Een zoekwoord tip zou ook mooi zijn, als ik op "Object conversion" zoek dan krijg ik nogal niet relevante links en ook andere zoek trefwoorden lopen nogal op andere resultaten uit.

Acties:
  • 0 Henk 'm!

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Ik neem aan dat je een typo hebt gemaakt en dat waardeA=bron.WaardeC,waardeB=bron.WaardeQ eigenlijk waardeA=obj.WaardeC,waardeB=obj.WaardeQ moet zijn.

Hoe lees je de lijst in vanuit file?

Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:53

Haan

dotnetter

Je zou in ieder geval kunnen beginnen met de List<AndereObjecten> direct de juiste grootte te geven, dus:
C#:
1
List<AndereObjecten> resultaat = new List<AndereObjecten>(bron.Count);

Nu moet de list steeds vergroot worden, wat natuurlijk tijd kost.

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • Face_-_LeSS
  • Registratie: September 2004
  • Niet online
Hoe meet je de tijd? Zo?

C#:
1
2
3
DateTime start = DateTime.Now
var lijst = VertaalLijst(bron);
Debug.WriteLine(DateTime.Now - start);

Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:53

Haan

dotnetter

Face_-_LeSS schreef op maandag 16 augustus 2010 @ 15:01:
Hoe meet je de tijd? Zo?

C#:
1
2
3
DateTime start = DateTime.Now
var lijst = VertaalLijst(bron);
Debug.WriteLine(DateTime.Now - start);
Ik mag hopen dat het iets is als dit:
C#:
1
2
3
4
var sw = Stopwatch.StartNew();
// process here
sw.Stop();
var elapsed = sw.Elapsed.TotalMilliseconds;

Daarbij schijnt je ook altijd in release mode en niet vanuit visual studio (dus de exe los draaien) te moeten benchen

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 20:55
Verdeel sowieso je logica over meer stappen, dan kan je daarover gaan timen (dus het toevoegen aan de lijst, constructor aanroepen en properties setten los).

3.000 objecten in 3 seconden is overigens allesbehalve snel.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bedankt voor de snelle reacties. Het probleem viel op toen een step over in debug mode erg lang duurde. Daarna inderdaad met de stopwatch methode gezien dat dit 2 tot wel 3 seconde in beslag nam wat gewoon veel te lang is.

Ik heb inderdaad in plaats van een list ook al een array met benoemd maximum gebruikt maar dat gaf niet echt een groot verschil (paar miliseconde). Ik ben nu de .ConvertAll methode aan het proberen maar heb zo'n voorgevoel dat dit ook weinig uit gaat maken.

De waarde had ik even als voorbeeld, het gaat er om dat ik vanuit een bron een bepaalde propertynaam overzet naar een doel object waar de property anders heet maar wel van hetzelfde type is.

Het lijkt nu haast sneller door de collectie te serializeren naar een bestand, daar een replace te doen van de property tags naar de nieuwe tags en die terug te deserialiseren.

Het deserialiseren doe ik als:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
        public static object DeSerializeObject(string Xml, System.Type ObjType)
        {
            object obj;
            XmlSerializer ser = new XmlSerializer(ObjType);
            using (StringReader stringReader = new StringReader(Xml))
            {
                using (XmlTextReader xmlReader = new XmlTextReader(stringReader))
                {
                    obj = ser.Deserialize(xmlReader);
                }
            }
            return obj;
        }


Waar de ingaande xml een geserialiseerde string is die met een streamreader.readtoend eerder is uitgelezen. Dat gaat echt enorm snel en kan nog sneller door in plaats van xml serialisatie / deserialisatie de binaire serialize / deserialize te gebruiken.

Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:53

Haan

dotnetter

Beetje offtopic, maar wist je dat jouw deserialize methode ook anders (imo mooier) kan?
C#:
1
2
3
4
5
XDocument sourceXml = XDocument.Parse(content);
XmlSerializer serializer = new XmlSerializer(typeof(myType));
XmlReader reader = sourceXml.CreateReader();

var results = (myType)serializer.Deserialize(reader);

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
@Haan, tnx, altijd leuk om dit soort dingen te leren.

Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

Misschien zoiets? (Als de volgorde van de objecten uitmaakt moet je AsParallel() weghalen uiteraard.

En de return is een IEnumerable<AndereObjecten>.

C#:
1
2
3
4
5
6
7
public static AndereObjecten VertaalLijst(IList<BronObjecten> bron)
{
  var result = from x in bron.AsParallel()
                   select new AndereObjecten { waardeA = x.WaardeC, waardeB = x.WaardeQ };
  
  return result;
}

[ Voor 0% gewijzigd door HMS op 16-08-2010 15:29 . Reden: Foutje in code. ]


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Waarom geen explicit/implicit cast definiëren en ConvertAll(obj=>(AndereObjecten)obj) gebruiken? Verder, gebeurd er misschien iets geks in de properties? 3000 objecten converteren lijkt me een aantal milliseconden moeten duren, niet seconden (of ze moeten wel erg groot zijn). Meerdere draadjes gebruiken lijkt me redelijk overdreven in de meeste gevallen.. :p

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • BM
  • Registratie: September 2001
  • Laatst online: 20:37

BM

Moderator Spielerij
Wel handig om er dan bij te vermelden dat daar .Net 4.0 voor vereist is :)
Iig zolang je AsParallel gebruikt.

Xbox
Even the dark has a silver lining | I'm all you can imagine times infinity, times three


Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

BM schreef op maandag 16 augustus 2010 @ 15:47:
Wel handig om er dan bij te vermelden dat daar .Net 4.0 voor vereist is :)
Iig zolang je AsParallel gebruikt.
Owh sorry, ik wist niet dat PLINQ queries .NET 4 specific waren. Maar goed, weet ik dat ook weer ;)

@ pedorus, maar het kan wel ;-).

edit:

Wat ook nog kan, is dat er een soort lazy loading plaats vind van de XML? (Gebruik nooit (de)serialization, maar zou wel de lange access time kunnen verklaren.)

[ Voor 24% gewijzigd door HMS op 16-08-2010 15:53 ]


Acties:
  • 0 Henk 'm!

  • BugBoy
  • Registratie: November 2002
  • Laatst online: 13-09 09:01
Er is echt iets anders aan de hand als het serializen / deserializen sneller gaat dan een rechtstreekse omzetting van de objecten. Veel sneller dan onderstaande code zul je het niet gaan krijgen (zonder de code parallel uit te voeren):

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
public static IList<AndereObjecten> VertaalLijst(IList<BronObjecten> bron)
{
  var resultaat = new AndereObjecten[bron.Count];
  for (int index=0; index<bron.Count; ++index)
  {
    resultaat[index] = new AndereObjecten()
        {
          waardeA = bron[index].WaardeC,
          waardeB = bron[index].WaardeQ
        };
  }
  return resultaat;
}


Offtopic: Ik zou altijd met interfaces werken, omdat je zo meer vrijheid houdt om de interne implementatie aan te passen en je methode wordt breder bruikbaar (mijn implementatie accepteert ook een array i.p.v. een lijst). Als je persé een echte lijst wil teruggeven, dan moet de lijst initialiseren met de gewenste lengte.

The miracle isn't that I finished. The miracle is that I had the courage to start.


Acties:
  • 0 Henk 'm!

  • Korben
  • Registratie: Januari 2001
  • Laatst online: 13-07 01:53

Korben

() => {};

BugBoy schreef op maandag 16 augustus 2010 @ 23:55:
Offtopic: Ik zou altijd met interfaces werken, omdat je zo meer vrijheid houdt om de interne implementatie aan te passen en je methode wordt breder bruikbaar (mijn implementatie accepteert ook een array i.p.v. een lijst). Als je persé een echte lijst wil teruggeven, dan moet de lijst initialiseren met de gewenste lengte.
Met je principe ben ik het zeker eens, programmeer tegen interfaces. Echter, als jouw functie een IList<T> accepteert kun je er wel een array aan meegeven, maar vervolgens functioneert die array niet als een volwaardige IList<T> (je kunt hem bijvoorbeeld niet bewerken). Ik zou eerder kijken naar IEnumerable<T> als argument en als returntype. Hetzelfde gaat daar namelijk voor op; in de meeste gevallen hoef je alleen maar over die resultaten heen te enumereren, dus is het helemaal niet interessant of het wordt opgeslagen in een lijst of een array of een LinkedList<T>, enzovoorts.

.oisyn: Échte programmeurs haten PHP met een passie. Ben jij soms geen echte programmeur?


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Mooi om dit allemaal te lezen er er van te leren. Ik kreeg de "AsParallel" optie al niet aan de slag (heb ook geen 4.0) maar zonder AsParallel doet hij het ook. Ik moet nog testen of het sneller is.

De ConvertAll ben ik ook nog mee bezig. Lazy loading is helaas geen optie, het is juist belangrijk dat ik alle gegevens in één keer heb. Het wordt namelijk gebruikt om veelvoudinge connecties terug naar de database te voorkomen.

Het voorbeeld van BugBoy leek mij ook haast niet sneller te kunnen. Ik zal nog eens goed alle property waarden controleren en eens kijken of het met 100 objecten ook zo traag is.

Ik gebruik verderop in de code de List specifieke eigenschappen, er gaat ook zeker weten altijd een List in, maar je hebt gelijk, het is netter om een IList of IEnumerable te gebruiken. Ik zal het aanpassen :-)

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Misschien eens een idee om met een profiler te kijken waar 'ie nu zo lang mee bezig is? Een seconde per 1000 objecten ongeveer is bizar lang.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Ik zou inderdaad eens gaan timen wat er zo lang duurt. Dingen als AsParallel zijn leuk, maar het lijk mij dat er een structureel ander probleem is. Word er in de Assignment van een property bijvoorbeeld wat gelogd, of op de een of andere manier IO uitgevoerd? Het creëren van 3000 objecten en wat properties assignen moet gewoon niet zo lang duren!

[ Voor 40% gewijzigd door Woy op 17-08-2010 14:02 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 21-08 23:06

HMS

Of om even 'evil' yield te gebruiken:

C#:
1
2
3
4
5
6
7
8
9
10
11
public static IEnumerable<AndereObjecten> VertaalLijst(IList<BronObjecten> bron) 
{ 
for (int index=0; index<bron.Count; ++index) 
  { 
    yield return new AndereObjecten() 
        { 
          waardeA = bron[index].WaardeC, 
          waardeB = bron[index].WaardeQ 
        }; 
  }
}


Maar dit gaat hoogst waarschijnlijk je probleem toch niet oplossen, omdat het probleem ergens ligt. Zoals Woy al aankaart.

Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 08-09 11:33
Probeer eens geen linq syntax te gebruiken maar gewoon een doodnormale forloop. LinQ lijkt altijd heel erg mooi en compact maar onderwater kan er echt heel veel gedaan moeten (worden) voor die regel code. Zo weet ik een voorbeeld waarbij je kon zoeken met linq, waar je verwachte dat dit binarysearch was of zoiets was dit eigenlijk een forloop over de hele collectie.

Edit: yield is niet ev1l, vooral voor parsers is het meesterlijk.

[ Voor 8% gewijzigd door roy-t op 17-08-2010 20:05 ]

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

Verwijderd

is AndereObjecten wel een DTO? zo niet probeer eens een test te maken met dezelfde LINQ code en een dummy POCO class. (Als 'andereobjecten' een domain/business class is lijkt het me beter deze te mappen naar een DTO en deze te serializeren... )

Acties:
  • 0 Henk 'm!

  • __fred__
  • Registratie: November 2001
  • Laatst online: 00:44
Draai je code eens zonder debugger met een serieuze stopwatch in je code. Dan zul je zien dat het allemaal wel mee valt. Je debugger hangt nu aan de code, die heeft (veel) tijd nodig om zijn eigen administratie bij te houden. De .NET runtime code wordt niet gedebugged en voert dus een stuk sneller uit.
Pagina: 1