Source Code mit Nutzung con Com-Objejten deutlich langsmer im Thread

  • VB.NET
  • .NET (FX) 4.0

Es gibt 96 Antworten in diesem Thema. Der letzte Beitrag () ist von Tukuan.

    jvbsl schrieb:

    Natürlich müssen die ganzen COM-Objekte noch richtig released werden

    jvbsl schrieb:

    aber das zugrunde liegende COM-Objekt nie released wird, vlt. solltest du hier einfach mit einer zweiten Referenz

    Beides sind bömische Dörfer für mich. Was meinst du damit? Könntest du es in Codezeilen ausdrücken.

    ErfinderDesRades schrieb:

    DChanel-Dingens nicht eine Clear-Methode

    Nö. Leider kein Clear, Exit, close, dispose, deallocade, free oder was es sonst so geben könnte. Muss da wohl mal bei der Hotline anrufen - oder so.

    Hab mal von dem oberen Projekt die Zeiten gemessen. 1. Durchlauf GUI. Zweiter Thread mit Prio Normal. So sind beide Berechnungen fast gleich lang... ?(
    Dreh noch mal am Rad <X

    ### Start from GUI
    0 - Start
    5290 - FFTs calc. - 0
    20890 - FFTs calc. - 1
    48731 - FFTs calc. - 2
    83408 - FFTs calc. - 3
    187544 - FFTs calc. - 4
    195853 - FFTs calc. - 5
    Channel read; 0;15;3,16666666666667;6,17791766428355
    Zeros calced; 2444;9439;4438,66666666667;5066,37125235278
    FFT calced; 5097;104135;32609,5;46833,0471729099
    195856 - finished
    ########################################################
    ### Start from Tread - Normal
    0 - Start
    37734 - FFTs calc. - 0
    66468 - FFTs calc. - 1
    94406 - FFTs calc. - 2
    122511 - FFTs calc. - 3
    151248 - FFTs calc. - 4
    180384 - FFTs calc. - 5
    Channel read; 7;16;11,8333333333333;12,2406426846524
    Zeros calced; 4240;14011;6435,66666666667;7285,57337025257
    FFT calced; 27938;37704;30058,3333333333;30254,8879962671
    180424 - finished
    ##########################################################

    Tukuan schrieb:

    Dreh noch mal am Rad

    Warum? Ich dachte das war das Ziel, dass es im GUI Thread gleich schnell ist wie in nem extra Thread? Oder ist es jetzt im GUI-Thread langsamer geworden?

    Vlt. gibt es ja ein Release?
    Bzw. hierfür gibts ja ne Methode(wurde glaub ich bereits erwähnt):
    msdn.microsoft.com/de-de/libra…ecomobject(v=vs.110).aspx
    Eine zweite Referenz deshalb, weil du im Prinzip das hier hast:

    Quellcode

    1. a = ComObject1
    2. a = ComObject2
    3. a.Release()
    4. // aber wie releast du dann ComObject1?
    5. ->
    6. a = ComObject1
    7. b = ComObject2
    8. a.Release()
    9. b.Release()
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    Tukuan schrieb:

    Nö. Leider kein Clear, Exit, close, dispose, deallocade, free oder was es sonst so geben könnte.
    Remove?
    such mal das DChannel.AppendDataDouble() im ObjectBrowser auf, und poste Screenshot, was da sonst noch an Methoden verfügbar ist.

    Was ist mit meine anneren beiden Anregungen?
    1. böse Funktionen vermeiden
    2. klären, was das ChannelRead bei dir fürn Datentyp ist, und erklären, warum du das nach Array konvertierst.
      (das bringt der Performance nix, aber bereinigt den Code und schult dein Verständnis von Datentypen)

    jvbsl schrieb:

    Warum? Ich dachte das war das Ziel, dass es im GUI Thread gleich schnell ist wie in nem extra Thread?

    Ja, schon... Aber... Ach ich weiß auch nicht... Unbefriedigend... HAb ja im Prinzip nichts geändert... Im "im Prinzip" steckt dann wohl die Lösung...
    Das mit dem Release geht für den Datentyp nicht (Siehe unten).

    ErfinderDesRades schrieb:

    Remove?

    Gib's auch nicht... Verdammt, wie kann ich den Bilder hochladen...
    Was ist mit meine anneren beiden Anregungen?

    1. Werde ich machen.
    2. Ist ein Double(). Es wird nach Array konvertiert weil es die Funktion AppendDataDouble verlangt. Sonst:
    "Option Strict On" lässt das Einschränken von Typ "System.Array" zu Typ "1-dimensionales Array von Double" beim Zurückkopieren des ByRef-Parameterwerts "Data" in das entsprechende Argument nicht zu.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Tukuan“ ()

    1. https://www.vb-paradise.de/index.php/Thread/103476-Video-Anleitung-zur-Forum-Bedienung/?postID=887846#post887846
    2. gut
    3. ach, das ist byRef, und erwartet wird Typ Array? Ist ja ein krankes Design.
      Und mussich gleich ausprobieren, ob die Werte bei der CType-Geschichte ühaupt korrekt zurückgeschrieben werden.

    edit: habs ausprobiert, und fund, das failt.
    Also entweder hat deine Funktion dadurch ein Fehlverhalten, oder die Famos-Progger haben da ByRef verwendet, wo es überhaupt nicht hingehört.
    Die Fehlfunktion ist folgende: Bei ByRef-Parametern sollen die übergebenen Variablen durch die aufgerufene Methode ausgetauscht werden.
    Bei dir durch den CType() drumrum übergibst du aber gar nicht die deklarierte Variable, sondern das Ergebnis der Konvertierung. Die Methode tauscht also das Konvertierungs-Ergebnis aus - nicht aber deine Variable.

    Try this:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    2. Dim arr = {5, 4, 3, 2, 1}
    3. ReplaceArrayWrong(CType(arr, Array)) 'erzwingt Konvertierung, und verfehlt das Replacing
    4. ReplaceArrayRight(arr) 'sauber geproggt funktioniert das Replacing
    5. End Sub
    6. Private Sub ReplaceArrayWrong(ByRef arr As Array)
    7. 'Parameter deklariert als Array im allgemeinen
    8. arr = {1, 2, 3, 4, 5, 6}
    9. End Sub
    10. Private Sub ReplaceArrayRight(ByRef arr As Integer())
    11. 'Parameter typsicher deklariert als Integer-Array
    12. arr = {1, 2, 3, 4, 5, 6}
    13. End Sub
    Und gehe im Einzelschritt den Button_Click durch, und kontrollier im Lokalfenster, wie sich arr verändert oder eben nicht.

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Ist ja ein krankes Design.

    JA!!! 110 prozentige zustimmung...
    Hab ich schon an einigen anderen Stellen bei deren Schnittstelle festgestellt. Bei anderen Objekten/Funktionen werden Sachen überschrieben, obwohl sie mit Byval übergeben wurden (ist wohl blöd beschrieben von mir...).
    ABER sie haben einiges in ihrer Com-Schnittstelle vermurkst...
    man kann auch mit dem kranken ByRef-Design erreichen, dass die Variablen ausgetauscht werden.
    Dazu müsstest du aber überhaupt erstmal überprüfen, ob die Methode dieses Array verändert.
    Falls ja, hat dein Proggi vmtl. kein Fehlverhalten, falls nein, muss man dem Krank-Design entsprechend den Code ändern, und dann nochmal gucken, ob er was verändert.
    Weil kann auch sein, dass garnix verändert werden soll.

    Naja - genau betrachtet siehts mir eh aus wie VollQuatsch: In der Schleife weist du die Millisekunden deiner Stopwatch an die ChannelRead-Elemente zu - das kann doch nicht dein Ernst sein?

    ErfinderDesRades schrieb:

    In der Schleife weist du die Millisekunden deiner Stopwatch an die ChannelRead-Elemente zu - das kann doch nicht dein Ernst sein?

    Das war doch nur um herauszubekommen was wie lange dauert. Für den SourceCode, der nachher verwendet werden soll, kommt das natürlich alles raus...

    thefiloe schrieb:

    Bin deshalb nach langem hin und her dazu übergegangen die ganzen COM Aufrufe "manuell" zu machen, da ich da wenigstens weiß was ich mache.
    Ist auch besser. Insbesondere bei Ereignissen produziert der RCW-Generator gerne mal Schrott. Welche Komponente kann man denn z.B. nicht verwenden? Mir ist noch keine untergekommen...

    Tukuan schrieb:

    Wenn ich es bisher noch nicht komplett falsch verstanden habe, ist das ein zugriff auf die imc Com Schnittstelle ImcCoreLib. Oder?
    Ja. Möglicherweise ist die Typbeschreibung nicht ganz exakt, sodass der generierte RCW nicht optimal ist. Kann vorkommen - dagegen hilft aber nur die manuelle Deklaration aller verwendeten Schnittstellen. Wie das geht? Naja - du musst die IDLs der Schnittstellen raussuchen und manuell in .NET übersetzen.

    ErfinderDesRades schrieb:

    Also entweder hat deine Funktion dadurch ein Fehlverhalten, oder die Famos-Progger haben da ByRef verwendet, wo es überhaupt nicht hingehört.
    Die Autoren haben gar kein ByRef verwendet, weil das mit C++-Referenzen bzw. -Zeigern nichts zutun hat. Das kann höchstens aus dem RCW stammen, der beim Setzen des Verweises automatisch generiert wurde. Um den Sinn des ByRef zu überprüfen, müsste man die generierte Deklaration mit der originalen IDL-Deklaration abgleichen und abwägen, ob die Übersetzung passt.

    Generell gilt aber: Erst mal den Profiler fragen, erst dann machen weitere Untersuchungen Sinn. Wir raten hier ins Blaue hinein, und zwar alle zusammen. Alle Änderungsversuche führen nur zu weiterer Frustration, sofern nicht jemand zufällig das Problem trifft.
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „hal2000“ ()

    hal2000 schrieb:

    Generell gilt aber: Erst mal den Profiler fragen, erst dann machen weitere Untersuchungen Sinn. Wir raten hier ins Blaue hinein, und zwar alle zusammen. Alle Änderungsversuche führen nur zu weiterer Frustration, sofern nicht jemand zufällig das Problem trifft.

    Da magst du recht haben. Ich werde es mal mit Profiling versuchen. Leider kann das aber ne ganze Zeit dauern, da ich nun erst mal nicht zu programmieren komme.
    Erst einmal vielen Dank.
    Ich melde mich dann wieder...

    hal2000 schrieb:

    Welche Komponente kann man denn z.B. nicht verwenden?

    Hatte gerade "kürzlich" das Problem mit XAudio2. z.B. mit dem IXAudio2VoiceCallback interface. Außerdem verstehe ich bis heute nicht was .NET eig. bei den ganzen COM Aufrufen macht. Ich rufe bei COM alles manuell über die Adressen der VTable des COM Objektes auf. Hatte da nie das Problem mit komischen Fehlern bezüglich STA/MTA Marshaling. Bei reinem .NET COM-Interface Import ist nicht dran zu denken. Da hast du keine Chance, haut immer die nichts sagende Fehlermeldung E_NOINTERFACE (sobald du etwas z.B. in einem STA Thread erstellst und in einem MTA Thread verwendest). Das Problem hast du jedoch bei nativer Verwendung der COM-Objekte nicht (ebenso nicht wenn du die Funktionspointer manuell aufrufst). Mal davon abgesehen, dass es merkbar schneller ist.

    Zum Problem des TE: Was berechnet, denn nun die Bibliothek eigentlich? Es kann auch gut sein, dass die Berechnung einfach diese Zeit braucht und es da nicht viel zum optimieren gibt (außer halt auf mehrere Threads auszulagern und vll. eine Art Queue zu verwenden).


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

    thefiloe schrieb:

    Außerdem verstehe ich bis heute nicht was .NET eig. bei den ganzen COM Aufrufen macht.
    Schreib dir mal eine kleine COM-Library in C++ (einfach Interface + Coklasse mit Class Factory). Verwende kein ATL/MFC. Deklariere das Interface in .NET per <ComImport()> und benutze die ClassFactory, um dein Objekt zu erstellen. Letzteres ist sehr interessant, weil du dort schön die QueryInterface-Aufrufe, die aus .NET kommen, in deinem C++-Projekt nachverfolgen kannst. (Unverwaltetes Debuggen aktivieren).

    Sehr hilfreich dabei: progtutorials.tripod.com/COM.htm

    thefiloe schrieb:

    Ich rufe bei COM alles manuell über die Adressen der VTable des COM Objektes auf.
    Dann kannst du aber keine frühe Bindung verwenden und hast keine Typprüfung vom Compiler - klingt fehleranfällig. Du kannst in der .NET-Deklaration die DispID als Attribut explizit angeben.

    thefiloe schrieb:

    sobald du etwas z.B. in einem STA Thread erstellst und in einem MTA Thread verwendest
    Das sollte man auch nicht mit Komponenten machen, die nicht dafür vorgesehen sind (wegen Threadsicherheit).

    thefiloe schrieb:

    IXAudio2VoiceCallback
    Callbacks im COM-Interop sind hässlich - aber sie funktionieren. Ich hatte das Problem mal mit IWMPRemoteMediaServices beim Remoting des Windows Media-Player aus .NET. Falls es dir hilft: An irgendeiner Stelle muss ein Parameter ByRef sein, auch wenn das der Interfacedeklaration scheinbar widerspricht. Das hatte damals was mit Boxing zutun - die Runtime hatte mir eine Interfacereferenz nochmal in Object geboxt, weshalb die Callback-Quelle das Interface nicht aufrufen konnte. Oder so ähnlich.
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „hal2000“ ()

    Ich weiß wie die QueryInterface Aufrufe funktionieren. Diese kann man auch in .NET sehr gut nachverfolgen. Ich weiß auch viel besser als mir lieb ist wie man mit .NET Com Importe vornimmt. Nichts desto trotz hat das .NET einfach vermurkst. Dinge die mit C++ funktionieren, funktionieren mit normalen .NET "Methoden" nicht und sind dazu noch wesentlich langsamer.
    Frühe Bindung -> hast du recht, brauch ich aber auch nicht. Typprüfung. Nunja man muss halt wissen was man auf den Stack lädt und was man zurück bekommt. Vielmehr ist da nicht dahinter. Die Header Dateien sind da zuverlässig.
    Was die Komponenten angeht die nicht dafür vorgesehen sind... nunja. Da magst du teils recht haben. Doch wäre es schön wenn man das dem Benutzer überlässt ob er es schlussendlich machen kann. Tatsache ist, dass es in C++ mit den gleichen Komponenten funktioniert. Tatsache ist auch, dass die Komponenten die ich verwende noch NIE irgendwelche Probleme gemacht haben. Was noch mehr verwunder ist, dass manche Komponenten es nahezu vorschreiben, dass sie in mehreren Threads verwendet werden müssen, .NET dies jedoch nicht zulässt.
    Callbacks sind hässlich? Naja. Was willste machen. Du brauchst sie also musst sie zum laufen bringen.
    Das mit dem ByRef ist kompletter quatsch. Anscheinend hast du dich noch nicht so viel mit COM und .NET befasst wie ich, aber das problem bei IXAudio2VoiceCallback ist:
    Du musst eine Klasse haben welche die Methoden des Callback-Interfaces implementiert. Ist klar. Du musst dann die Klasse instanzieren und diese in Form des Interfaces marshalen. Auch kein Problem. Damit .NET das jedoch richtig marshalt, musst du das InterfaceType-Attribut angeben. Und jetzt kommt der Knackpunkt. Du kannst nicht einfach IUnknown angeben, da es schlichtweg nicht IUnknown ist. Die restlichen beiden Optionen erst recht nicht. Was also tun. Ohne das InterfaceType-Attribut geht es nicht. Dieses kannst du jedoch nicht wirklich da es keinen passenden Interface-Typ gibt. Gibt man jetzt IUnknown an, so wird es als IUnknown gemarshalt. Dies bedeutet, dass .NET in der VTable zusätzlich zu den Methoden des IXAudio2VoiceCallback interfaces noch die 3 IUnknown Methoden hinzunimmt (QueryInterface, AddRef, Release). Diese werden am Anfang hinzugefügt (bei abgeleiteten COM Objekten so üblich) -> Index 0, 1, 2 in der VTable. Versucht jetzt XAudio2 die erste Methode (1. Methode in der VTable) des IXAudio2VoiceCallback interfaces aufzurufen so bekommst du kurz und klanglos ein funktioniert nicht mehr und in der Ereignisanzeige steht nur ein Fehlercode der für AccessViolation steht. Weiß man das was ich jetzt gerade erklärt habe, so ist es nicht schwer zu erraten weshalb. XAudio2 haut irgendwas aufn Stack und ruft dann QueryInterface auf (da eben die von .NET hinzugefügte QueryInterface Methode jetzt auf Index 0 in der VTable liegt).
    Einzige Lösung für das Problem: InterfaceType-Attribut hinzufügen mit IUnknown. Anschließend den Pointer zur gemarhalten Instanz der IXAudio2VoiceCallback Implementation nehmen, über diesen die VTable raussuchen und dort Index 0,1,2 wieder überschreiben sodass der Murks von .NET wieder weg ist. Das ganze jedoch nicht für jede neue Instanz vom IXAudio2VoiceCallback machen sondern nur ein mal!
    Nur weißte... auf so nen Scheiß musste erstmal kommen. Und das ist nur eine Leidensgeschichte bezüglich COM und .NET und hat mit ByRef rein gar nichts zu tun.
    Zusammenfassend: Arbeite seit Jahren mit COM und .NET. Und bin mittlerweile einfach soweit, dass ich am liebsten alles selbst mache. Dort weiß ich was tue, weiß wie ich Dinge marshalle und aufrufe. Funktioniert nach langem Testen besser, performanter und zuverlässiger. Falls du interessiert bist wie das mit dem oben genannten Beispiel funktioniert: cscore.codeplex.com/SourceCont…Core/XAudio2/XAudio2_8.cs (siehe CreateSourceVoiceNative methode).


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Sehr interessantes Problem. Dass das nichts mit ByRef zutun hat, ist mir auch klar. Sorry dass ich nicht den kompletten Sourcecode meiner vergangenen Projekte im Kopf habe - ich erinnere mich nur dunkel, dass ich ein anderes Callback-Problem eben dadurch lösen konnte. Aber vielleicht habe ich mich ja auch "zu wenig" damit beschäftigt...
    Gruß
    hal2000

    hal2000 schrieb:

    Schreib dir mal eine kleine COM-Library in C++ (einfach Interface + Coklasse mit Class Factory). Verwende kein ATL/MFC. Deklariere das Interface in .NET per <ComImport()> und benutze die ClassFactory, um dein Objekt zu erstellen. Letzteres ist sehr interessant, weil du dort schön die QueryInterface-Aufrufe, die aus .NET kommen, in deinem C++-Projekt nachverfolgen kannst. (Unverwaltetes Debuggen aktivieren).

    Sehr hilfreich dabei: progtutorials.tripod.com/COM.htm

    Danke... Pu... Das klingt wirklich kompliziert. Völlig neue Welten für mich.
    Werde mir das Tutorial mal anschauen, wenn ich mal wieder zum programmieren komme...