Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

LINQ, Collection of interfaces και performance

Îåêßíçóå áðü ôï ìÝëïò KelMan. Τελευταία δημοσίευση από το μέλος Παναγιώτης Καναβός στις 09-09-2009, 19:58. Υπάρχουν 2 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  09-09-2009, 19:18 53589

    LINQ, Collection of interfaces και performance

    Αυτές τις μέρες γράφω κώδικα που έχει να κάνει με την προγραμματιστική δημιουργία ενός πακέτου για τα Integration Services του SQL Server και μου έτυχε το εξής ενδιαφέρον πρόβλημα (έτσι κουβέντα να γίνεται):

    Ας ξεκινήσουμε με την πρώτη έκδοση του κώδικα την οποία δοκίμασα αρχικά:

            ' Select and map destination columns
            For Each virtualInputColumn As IDTSVirtualInputColumn100 In destinationVirtualInputColumns
                ' Select column, and retain new input column
                Dim inputColumn As IDTSInputColumn100 = sqlServerDestInstance.SetUsageType(destinationInput.ID, destinationVirtualInput, virtualInputColumn.LineageID, DTSUsageType.UT_READONLY)
                
                ' Find external column
                Dim externalColumn As IDTSExternalMetadataColumn100 = Nothing
    
                For Each di As IDTSExternalMetadataColumn100 In destinationInput.ExternalMetadataColumnCollection
                    If di.Name = inputColumn.Name Then
                        externalColumn = di
                        Exit For
                    End If
                Next
    
                ' Map input column to external column
                If externalColumn IsNot Nothing Then
                    Debug.WriteLine(externalColumn.Name)
                    sqlServerDestInstance.MapInputColumn(destinationInput.ID, inputColumn.ID, externalColumn.ID)
                Else
                    Debug.WriteLine("Not found: " & virtualInputColumn.Name)
                End If
            Next virtualInputColumn

    Μην τρομάξετε και σας χάσω, ο κώδικας είναι απλός. Έχουμε μια σειρά από input columns και θέλουμε να τα κάνουμε map σε output columns. Μέσα στα input columns είναι πολλά που δεν μας κάνουν οπότε η λογική πάει ως εξής. Έχουμε δύο collections, το destinationVirtualInputColumns και το ExternalMetadataColumnCollection. Παίρνουμε ένα-ένα τα input columns και ψάχνουμε αν υπάρχουν στο ExternalMetadataColumnCollection ώστε στη συνέχεια να κάνουμε MapInputColumn. Το ExternalMetadataColumnCollection είναι ένα χαζό-collection από interfaces IDTSExternalMetadataColumn100, μιλάμε για COM και δεν έχει εξυπνάδες τύπου "Contains". Έχει όμως μια εξυπνάδα που τη χρησιμοποίησα για να κάνω τη δεύτερη έκδοση του snippet:

            ' Select and map destination columns
            For Each virtualInputColumn As IDTSVirtualInputColumn100 In destinationVirtualInputColumns
                ' Select column, and retain new input column
                Dim inputColumn As IDTSInputColumn100 = sqlServerDestInstance.SetUsageType(destinationInput.ID, destinationVirtualInput, virtualInputColumn.LineageID, DTSUsageType.UT_READONLY)
                ' Find external column by name
                Dim externalColumn As IDTSExternalMetadataColumn100 = Nothing
                Try
                    externalColumn = destinationInput.ExternalMetadataColumnCollection(inputColumn.Name)
                Catch ex As System.Runtime.InteropServices.COMException
                    'Bummer! Column not found...
                End Try
    
                ' Map input column to external column
                If externalColumn IsNot Nothing Then
                    Debug.WriteLine(externalColumn.Name)
                    sqlServerDestInstance.MapInputColumn(destinationInput.ID, inputColumn.ID, externalColumn.ID)
                Else
                    Debug.WriteLine("Not found: " & virtualInputColumn.Name)
                End If
            Next virtualInputColumn

    Προσέξτε το Try/Catch. Μπορώ να πάρω reference χρησιμοποιώντας το Item default property ωστόσο αν δεν υπάρχει αυτό που ψάχνω έχω exception. Δεν μπορώ να πω ότι με ενθουσιάζει προγραμματιστικά αυτός ο τρόπος, οπότε είπα να δοκιμάσω και με LINQ, περισσότερο από περιέργεια καθώς ήξερα ότι από πλευράς performance δεν θα γούρλωνα τα μάτια:

            Dim externalColumns = destinationInput.ExternalMetadataColumnCollection.Cast(Of IDTSExternalMetadataColumn100)()
            ' Select and map destination columns
            For Each virtualInputColumn As IDTSVirtualInputColumn100 In destinationVirtualInputColumns
                ' Select column, and retain new input column
                Dim inputColumn As IDTSInputColumn100 = sqlServerDestInstance.SetUsageType(destinationInput.ID, destinationVirtualInput, virtualInputColumn.LineageID, DTSUsageType.UT_READONLY)
    
                ' Find external column by name
                Dim externalColumn = externalColumns _
                                     .SingleOrDefault(Function(c) c.Name = inputColumn.Name)
    
                ' Map input column to external column
                If externalColumn IsNot Nothing Then
                    Debug.WriteLine(externalColumn.Name)
                    sqlServerDestInstance.MapInputColumn(destinationInput.ID, inputColumn.ID, externalColumn.ID)
                Else
                    Debug.WriteLine("Not found: " & virtualInputColumn.Name)
                End If
            Next virtualInputColumn

    Πιο κομψός κώδικας σε σχέση με τις δύο προηγούμενες δοκιμές. Κράτησα για το τέλος το θέμα του performance. Με ένα stopwatch μέτρησα και βρήκα τα παρακάτω:

    1η έκδοση:  01 min και 05.87 sec
    2η έκδοση:  00 min και 03.48 sec
    3η έκδοση:  01 min και 37.79 sec

    Ουφ! Φτάσαμε στο τέλος. Το ερώτημα είναι... πώς λένε τον οδηγό; Χα, όχι. Ποιόν κώδικα κρατάς και γιατί;

     

     


    Vir prudens non contra ventum mingit
  •  09-09-2009, 19:45 53590 σε απάντηση της 53589

    Απ: LINQ, Collection of interfaces και performance

    Ωραία! Η απάντηση στο παραπάνω είναι... Κανέναν!

    JOIN to the rescue!

            Dim externalColumns = destinationInput.ExternalMetadataColumnCollection.Cast(Of IDTSExternalMetadataColumn100)()
            Dim inputColumns = destinationVirtualInputColumns.Cast(Of IDTSVirtualInputColumn100)()
    
            Dim query = From externalColumn In externalColumns _
                        Join virtualInputColumn In inputColumns On externalColumn.Name Equals virtualInputColumn.Name _
                        Select externalColumn, virtualInputColumn
    
            For Each element In query
                Dim inputColumn As IDTSInputColumn100 = sqlServerDestInstance.SetUsageType(destinationInput.ID, destinationVirtualInput, element.virtualInputColumn.LineageID, DTSUsageType.UT_READONLY)
                sqlServerDestInstance.MapInputColumn(destinationInput.ID, inputColumn.ID, element.externalColumn.ID)
            Next

    H αλήθεια είναι ότι δεν πήγε κατευθείαν εκεί το μυαλό μου - δεν έχω συνηθίσει να σκέφτομαι the linq way...

    Χρόνος; Ταρατατζούμ... 00 min & 01.23 sec. Niiiceee...


    Vir prudens non contra ventum mingit
  •  09-09-2009, 19:58 53591 σε απάντηση της 53590

    Απ: LINQ, Collection of interfaces και performance

    Τσκ, τσκ, τσκ! Δεν είπες από την αρχή ότι έψαχνες να βρεις ποιά αντικείμενα έχουν κοινά ονόματα! Βλέποντας τον αρχικό κώδικα δεν πήρα χαμπάρι ότι αυτό έψαχνες.

    Όσον αφορά τους μυστήριους χρόνους, πάω στοίχημα ότι φταίνε τα casts που δεν είναι casts. Οι κλάσεις που χρησιμοποιείς είναι COM και έτσι κάθε cast στην ουσία μετατρέπεται σε ένα QueryInterface, το οποίο μετά θέλει φυσικά και ένα Release. Πάω στοίχημα ότι ο κώδικας που δημιουργεί η πρώτη έκδοση με LINQ κάνει συνεχώς casts και Release με αποτέλεσμα την τραγική αυτή επίδοση. Το περίεργο είναι ότι η έκδοση με το Join είναι γρηγορότερη από την πρώτη εκδοχή καθώς λογικά πρέπει να δημιουργεί τον ίδιο αριθμό COM κλήσεων, αν και δεν έψαξα πολύ τον κώδικα.

    Γενικώς το Join είναι σκέτο λουκούμι όταν ψάχνεις να βρεις είτε κοινά αντικείμενα σε λίστες, είτε τη διαφορά. Άσε που ο κώδικας είναι απείρος καθαρότερος. Έχω χρησιμοποιήσει αυτό το κόλπο με το LINQ to Sharepoint. Εκεί βέβαια δεν υποστηρίζεται το Join, οπότε έκανα το εξής: Πήρα τα ονόματα των αντικειμένων με δύο LINQ queries και μετά κράτησα το Minus τους. Έτσι βρήκα τις διαφορές. Δεν αποκλείεται να κάνει κάτι τέτοιο και το LINQ to Objects για να αποφύγει να κάνει M*N αναζητήσεις.


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems