Silverlight Compression (which is better binary WCF or CslaLight example?)

Silverlight Compression (which is better binary WCF or CslaLight example?)

Old forum URL: forums.lhotka.net/forums/t/7849.aspx


decius posted on Thursday, October 22, 2009

With SL3 we get binary encoding of the WCF for silverlight. 

http://www.silverlight.net/learn/videos/silverlight-videos/hdi-silverlight-3-binary-encoding/

In the past I've used the WCF compression example provided in the CslaLight Samples.  Now that we have binary encoding, does this make that model obsolete??

Has anyone got experience doing benchmarking on comparing the two?

decius replied on Thursday, October 22, 2009

Looks like this is already being discussed on another thread. My appologies for the duped post. I'll post any questions on this thread instead:

http://forums.lhotka.net/forums/thread/34880.aspx

RockfordLhotka replied on Thursday, October 22, 2009

Using binary XML in CSLA .NET is a multi-level proposition.

CSLA .NET does some serialization internally, and CSLA .NET 3.8 now defaults to using binary XML.

The MobileFormatter does its own serialization as well, and so in 3.8 that now defaults to using binary XML too - so you get that automatically.

Those streams of data you compress (in the compression example) are the serialized data going to/from MobileFormatter - so in 3.8 you'll be compressing the binary XML.

The other level where this matters is in the WCF data transfer itself. You control this based on how you configure your WCF endpoint and client. The thing is, the data portal service contract transfers byte arrays, not object graphs. So the WCF serialization is really only working with already-serialized and maybe already-compressed data.

However, there's a lot of value in using binary XML at the WCF level, because with text XML those byte arrays (binary data) must be converted into text to be valid XML. That almost doubles the size of the byte stream! Using binary XML should avoid that binary-to-text conversion and therefore should avoid the doubling of the byte stream size.

Yes, XML sometimes really sucks.

So in CSLA 3.8 you already get binary XML. I suggest you continue to use compression, and that you also configure WCF to use binary XML. That gives you the smallest data at all threee levels available.

decius replied on Friday, October 23, 2009

I see that on the server side the UseBinaryXml can be configured with an AppSetting key CslaUseBinaryXml. But how might I configure the UseBinaryXml property of the MobileFormatter in Silverlight?  I was hoping to do some quick benchmarking. I see that the MobileFormatter class is sealed, and I can't access that static property.

RockfordLhotka replied on Friday, October 23, 2009

In the code behind App.xaml, right where you’d configure the data portal to run locally, you can set a property on MobileFormatter (I think) to do the same thing. The 3.8 change log has info on this.

 

If you have trouble finding it let me know and I’ll help – I appreciate you taking the time to do some comparative testing!

 

decius replied on Friday, October 23, 2009

Yes, but unfortunately the static UseBinaryXml property on the MobileFormatter is inaccessible because the class is sealed.  For doing my benchmarking I'll just create a csla build where that class isn't sealed.  Unless you can't think of any other way to change that value, but I don't seem to see one.

 

RockfordLhotka replied on Friday, October 23, 2009

It is a public static though. You should be able to just do this in app.xaml.cs:

 

Csla.Serialization.Mobile.MobileFormatter.UseBinaryXml = true;

 

No need to alter CSLA at all.

decius replied on Friday, October 23, 2009

sorry, you're right!  The property wasn't showing up to me before because I had forgotten to update the csla reference from 3.7 to 3.8 at that time.  I thought that was kind of odd...I started assuming it was because the SL runtime handled sealed accesibility differently or something lol.

I'll be sure to post any results I come up with.

decius replied on Monday, October 26, 2009

I made a few steps further to getting a benchmark app setup today.  But I ran into a roadblock that I was needing help with.When switching UseBinaryXml to false, I get the following error when trying to deserialize.

There was an error deserializing the object of type System.Collections.Generic.List`1[[Csla.Serialization.Mobile.SerializationInfo, Csla, Version=3.8.0.0, Culture=neutral, PublicKeyToken=93be5fdc093e4c30]]. The input source is not correctly formatted.

System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type System.Collections.Generic.List`1[[Csla.Serialization.Mobile.SerializationInfo, Csla, Version=3.8.0.0, Culture=neutral, PublicKeyToken=93be5fdc093e4c30]]. The input source is not correctly formatted. ---> System.Xml.XmlException: The input source is not correctly formatted.\r\n at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)\r\n at System.Xml.XmlBufferReader.ReadValue(XmlBinaryNodeType nodeType, ValueHandle value)\r\n at System.Xml.XmlBinaryReader.ReadNode()\r\n at System.Xml.XmlBinaryReader.Read()\r\n at System.Xml.XmlBaseReader.IsStartElement()\r\n at System.Xml.XmlBaseReader.IsStartElement(XmlDictionaryString localName, XmlDictionaryString namespaceUri)\r\n at System.Runtime.Serialization.XmlReaderDelegator.IsStartElement(XmlDictionaryString localname, XmlDictionaryString ns)\r\n at System.Runtime.Serializa

tion.XmlObjectSerializer.IsRootElement(XmlReaderDelegator reader, DataContract contract, XmlDictionaryString name, XmlDictionaryString ns)\r\n at System.Runtime.Serialization.DataContractSerializer.InternalIsStartObject(XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)\r\n at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)\r\n --- End of inner exception stack trace ---\r\n at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)\r\n at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader)\r\n at Csla.Serialization.Mobile.MobileFormatter.Deserialize(XmlReader reader) in C:\\Projects\\Local\\csla3.8.0beta\\cslacs\\Csla\\Serialization\\Mobile\\MobileFormatter.cs:line 258\r\n at Csla.Serialization.Mobile.Mobil

eFormatter.Deserialize(Stream serializationStream) in C:\\Projects\\Local\\csla3.8.0beta\\cslacs\\Csla\\Serialization\\Mobile\\MobileFormatter.cs:line 229\r\n at Csla.Serialization.Mobile.MobileFormatter.Deserialize(Byte[] data) in C:\\Projects\\Local\\csla3.8.0beta\\cslacs\\Csla\\Serialization\\Mobile\\MobileFormatter.cs:line 378\r\n at Csla.Server.Hosts.Silverlight.WcfPortal.GetCriteria(Byte[] criteriaData) in C:\\Projects\\Local\\csla3.8.0beta\\cslacs\\Csla\\Server\\Hosts\\Silverlight\\WcfPortal.cs:line 302\r\n at Csla.Server.Hosts.Silverlight.WcfPortal.Fetch(CriteriaRequest request) in C:\\Projects\\Local\\csla3.8.0beta\\cslacs\\Csla\\Server\\Hosts\\Silverlight\\WcfPortal.cs:line 126"

The error occurs inside the MobileFormatter where ReadObject is called on the DataContractSerializer.  It seems that even thought I've turned off UseBinaryXml, the reader that is trying to be used is still of type System.Xml.XmlBinaryReader, which I suppose explains why an error gets thrown....

Is this a bug with regards to this configurable?

RockfordLhotka replied on Monday, October 26, 2009

This sounds like a bug – did you find specifically where the code is wrong?

 

RockfordLhotka replied on Monday, October 26, 2009

The bug isn't an obvious one. ReadObject() is called in Deserialize(), and that gets its reader from GetXmlReader(), which is the central method that returns either a normal or binary reader.

Are you sure you have your server and client side configurations both set properly?

decius replied on Tuesday, October 27, 2009

Sorry for the alarm -- you're absolutely right in asking that.  I had forgotten about the need to configure the server web.config.  Adding the appkey on the server solves the problem.

With this in mind, I suppose it's not easily possible to create an application with some objects using BinaryEncoding and some not?

RockfordLhotka replied on Tuesday, October 27, 2009

The data portal uses a central configuration…

 

decius replied on Wednesday, October 28, 2009

RockfordLhotka:

The data portal uses a central configuration…

 

Yes I understand.  The only reason I was asking was because I was trying to create a project to benchmark/test both configurations configurations quickly/easily.  Naturally, the problem with that is changing it on the client doesn't change it on the server, and thus resulting in a deserialization error when the response comes back.

I suppose I could have just extended the WcfPortal class on the host and overrided the ConvertResponse method and make the configuration change for relevant to that portal before the base.ConvertResponse call, but I ended up just changing the configuration on the server manually as needed, which was a lot simpler.

I finally got around to recording some results comparing xmlbinary encoding and compression.  I'll try to post them soon.

 

 

 

decius replied on Thursday, October 29, 2009

Rocky, I'd love to hear your thoughts and input on this.  Here's my findings.

http://www.markballengerdesign.com/tmp/BinaryXmlComparison.zip
---------------------------------------------------------------------------------------
A series of different sized test objects were passed through the dataportal and analyzed using Fiddler. The test object acted as the "control" for the experiment, while the dataportal configuration was the "variable". The four dataportal configurations tested were as follows:
  • None - no compression, no binary encoding
  • Both - compression and binary encoding
  • CompressedOnly - only compression is used on the dataportal, no binary encoding
  • BinaryOnly - only binary encoding is used on the dataportal (no compression)
Two series of tests were performed. The first tests series (Sheet2) used a Csla.ReadOnlyListBase with a child
that contained only 1 property of type Byte.  The second series of tests
(Sheet 3) used a Csla.ReadOnlyListBase object aimed at reflecting real-life situations and contains a variety of properties. 

 

 

The criteria used to construct a test is nothing more than an integer property called "PacketAmount"
which defines how many child objects to append to the list before sending the response back

 

 

*** An important thing to note is that the "Overall elapsed" time is NOT necessarily intended to reflect a typical user's elapsed time. This is because for this study, It was the elapsed time hitting a server within the client's network. You will notice, that when comparing Both vs. BinaryOnly, this experiment's performance bottleneck is the processing of the compression and NOT the action of transfering the response, thus making compression seem less worthy. This might not be the case for users hitting a server that is not as close to them, however it is outside of the scope of this experiment and deserves further study. Below show more detailed information regarding this stipulation.

 

The tracert to the host server from the client machine
  1    <1 ms    <1 ms    <1 ms  xxx.xx.xx.254
  2     1 ms     1 ms     1 ms  xxx.xx.xx.205
  3     1 ms    <1 ms    <1 ms  thedestination.com [xxx.xx.xx.8]
Information about the host server used:
Intel® Xeon® CPU
E7340 @ 2.40GHz
2.40 Ghz, 2.00 GB of RAM
Microsoft Windows Server 2003
Enterprise Edition
Service Pack 2

RockfordLhotka replied on Thursday, October 29, 2009

That’s nice work Mark!

 

It seems like just using binary XML is probably the best overall win. It doesn’t always offer the smallest byte size, but it offers what looks like substantially better performance than compression with larger data sizes. Which isn’t entirely surprising since compression is relatively CPU intensive, and binary XML should actually reduce the overall CPU requirements.

 

decius replied on Thursday, October 29, 2009

Happy to serve the Csla community!

The only thing I wasn't sure about was if that finding would still hold true if the client was on a slower connection.  I tried hooking Fiddler up while using some IE and Firefox throttle addons, but it didn't seem like Fiddler recognized the throttle, because it wasn't effecting the elasped time Fiddler was reporting...

From your experience, do you think that a slower (or average) connection might make compression more worthy?

RockfordLhotka replied on Thursday, October 29, 2009

I wonder if you shouldn’t run the tests without the larger volume sizes. Notice how the graphs are essentially all flat (seemingly near 0) until they skyrocket? While the extreme size performance is interesting, it probably doesn’t reflect normal usage.

 

And I suspect all that “near 0” data isn’t really so uniform as it appears. In other words, the right end of the graph has altered the scale such that maybe we can’t see valuable differences in the more common data size ranges?

 

decius replied on Thursday, October 29, 2009

Yes, I agree that makes it hard to see.  You could always just change Source data on those existing charts to not include the higher volumes which makes it a little easier to compare the smaller sizes... (hehe, Even though throwing together charts in excel is super fast and easy, it's relatively a poor charting interface IMHO).

If you think it would be useful to do another series I don't mind to.  Is there any specific intervals you would like to have done?

So you don't think that the connection speed is much of a factor then?

RockfordLhotka replied on Thursday, October 29, 2009

Good point, you don’t need to re-run the tests, just change the range on the chart – doh! J

Jack replied on Thursday, October 29, 2009

Is it a fairly automated test? 

Would it be possible to isolate the timings into chunks? 

Post server data retrieval -> serialize/compress

Start of Send -> Client received (Internet)

Client Uncompress/deserialize

That would really give a nice experiment and isolate which bits and pieces are in play and where.  There have been comments on the forums that the Silverlight side is a potential bottleneck /w deserializing.

I would really like to have something in place that I could swap out the free compression algorithms for something commerical and see if I can really measure the difference.

It is hard to test anything locally as anything going through the internet either bounces straight back at my modem or at the next routing step.  Other than 1 Mississippis over the phone while talking to people its hard to get a real sense.

I would be happy to get some of my users at various locations to try doing a test if we wanted to try and get a better sense of bandwidth implications. 

I have two ISP connections ranging from 5/1 Mbs to 100/5 dl/ul so if you have a simple deployable app that I can have them just click a button on the client I'm happy to provide the results.

I'm in western Canada and have clients at Yale/UCLA/Atlanta which is as close to the 4 corners of the US as I can get.  I know my east coast clients have a worse experience just due to the data transfer times but I'm actually wondering how much the processing on their end makes a difference or not.  There are a wide ranges of client PC's.

Thanks

jack

decius replied on Thursday, October 29, 2009

RockfordLhotka:

Good point, you don’t need to re-run the tests, just change the range on the chart – doh! J

Even still, when analyzing the smaller volumes, another series that refines the curve at these low volumes would probably be useful, it gets pretty hard to get clarity without a smaller interval there because of how haphazard the data gets there.

 

Jack:

Is it a fairly automated test? 

Unfotunately, not really. I just Reviewed the response object manually through fiddler.  However, with talk of probing more tests, I might decide to figure out how to write some rules for fiddler that can log this for me... If you have any tips on doing that let me know

Jack:

Would it be possible to isolate the timings into chunks? 

Yeah, but mainly for simplicity I just wanted to see the big picture (BytesReceived and the OveralElapsedTime) so that I compare the DataPortal configurations. Below shows and example of the statistics Fiddler harvests, which would get you what you were looking for. I do have a test project, I'll try to post.  But honestly the test project ain't all that fancy, the real automation for testing it would lie in those Fiddler rules I mentioned...

Request Count:  1
Bytes Sent:  196
Bytes Received: 683,617

ACTUAL PERFORMANCE
--------------
ClientConnected: 14:51:05:4953
ClientDoneRequest: 14:51:05:5109
Gateway Determination: 0ms
DNS Lookup:   141ms
TCP/IP Connect:  78ms
ServerGotRequest: 14:51:05:7296
ServerBeginResponse: 14:51:05:7921
ServerDoneResponse: 14:51:08:5728
ClientBeginResponse: 14:51:08:5728
ClientDoneResponse: 14:51:08:5728

 Overall Elapsed: 00:00:03.0775340

 

 

Jack:

There have been comments on the forums that the Silverlight side is a potential bottleneck /w deserializing.

Yes, I believe I've brought this up a time or two as well.  Unfortunately the cause for this is because of the restricted Silverlight CLR for reflection, which due to this left serialization in SL up to the MobileFormatter rather than the binary formatter used for windows versions.  The only way to improve that is to improve the MobileFormatter or write a new one

 

Jack:

I would be happy to get some of my users at various locations to try doing a test if we wanted to try and get a better sense of bandwidth implications. 

Awesome. Like I said earlier, I'll try to wrap up what I've been using for you and post it here soon. Maybe tomorrow if I get time... 

mbblum replied on Thursday, October 29, 2009

The best overall for us actually appears to be compression. We have clients in rural districts with very slow netwok connections, some as low as dialup. Then we have a very large metro user in which managment thinks a T1 is plenty of bandwidth. (They can't believe that could be the cause of slow performance.)

Based on these results, the time gains from less network traffic should easily offset that extra CPU time. Compression looks like the clear winner for the situation here.

Thanks for posting the results.

mbb

decius replied on Friday, October 30, 2009


Based on these results, the time gains from less network traffic should easily offset that extra CPU time. Compression looks like the clear winner for the situation here.


Youre saying that you experience better results using compression only without binary encoding? No binary encoding at all?  I don't know that I could agree with that statement given the emprirical data in the tests, even with the network traffic included.  If anything, I would think it would be a combination of the two. Do you have any hard evidence to back that up?  (you do notice there were four configurations tested, not just two right?)

I found it's possible to throttle fiddler.  I'll post some tests with various connection times as well; as soon as I figure out how to configure fiddler that way and get some free time.


RockfordLhotka replied on Friday, October 30, 2009

I really think you should find that having WCF using binary xml should cut the bytes on the wire by nearly half. That didn’t seem to be the case, which is surprising, but here’s my (maybe faulty) reasoning.

 

The MobileFormatter serializes the data into XML or binary (in 3.8 it is binary). And if you use compression you absolutely end up with binary. In every case, what is sent over the wire is a byte[].

 

WCF will get that byte array, and must convert it (by default) into XML – so it ends up in a CDATA block or something. But it is binary, and XML doesn’t allow binary, so they have to base64 encode it, which should nearly double its size.

 

I would expect that binary XML wouldn’t need to base64 encode the byte array, thus saving all that base64 size overhead.

 

But from your data, maybe that is not correct…

 

decius replied on Friday, October 30, 2009

Rocky:

I really think you should find that having WCF using binary xml should cut the bytes on the wire by nearly half. That didn’t seem to be the case

Really? Because comparing BinaryOnly to the "None" configuration the bytes are close to halved, as you expected.  It's only when comparing CompressedOnly to Both that you don't quite see an improvement in the bytes received, but honestly that doesn't surprise me because it's already been binary encoded so the compression has less to gain... ne?

Regardless, for my own selfish reasons, I'm mostly curious about the bottom line (the overall elapsed time for the average user). Which is why I'm running another test right now using some throtling features in Fiddler I just found out about (yay =P).

The next time I post I'll zip up the little test app UI I'm using, with instructions on how to setup Fiddler to monitor it and capture the data in batches too, in case anyone else would like to use this stuff....

decius replied on Friday, October 30, 2009

 I've tried to configure the "simulate modem speeds" rule in Fiddler to match the average user connection, but surprisingly it doesn't appear possible (long story short) because of the way the app is designed it uses an int for a configurable where I need a double and I can't supply .39 for the configurable.... I guess the feature was intended to simulate dial up speeds and didn't consider the need for faster connections....

According to my calculations and research, the average downstream in the USA is 3.9mb/s and the average upstream is 314kb/s. The fastest I can make Fiddler go while throttling is .5mb/s down and .5mb/s up (tested using Speedtest.net)

So I decided to use that setting anyway to see an "extremely slow" user's performance.  And guess what? The Both dataportal configuration appears drastically more worthy, as suspected. We could imagine that the payoff for average speeds is much less than this test shows (because it's 1/8th the speed of the average user's down stream) However given in the results, Both appears to be in many cases 100 x faster than binary only and 1.5 x as fast as compression only! 

Again, I'll be posting the test projects along with these results in case anyone wants to investigate this further. 

 

decius replied on Friday, October 30, 2009

Okay here's the newer stuff... 
http://markballengerdesign.com/tmp/BinaryXmlComparison2.zip

When I get time I'll upload this project to CodePlex so that others can contribute to the testing project if they want
http://cslalightdpbenchmark.codeplex.com/

Jack replied on Monday, November 02, 2009

Has anybody had any deployment issues when switching to the binary WCF?  I had no problems making the switch locally but deploying to my webserver is not working at all.  I haven't changed anything else that I can see...

jack

decius replied on Monday, November 02, 2009

Yeah, you have to change the Csla setting on the server web.config manually to re-enable it to use binary encoding.  I discussed this with Rocky many posts ago on this thread. 

It would be possible to create an app that allows the user to change this from the UI, but it was going to end up in some extra work, and for me, it just made more sense to make this change manually when testing...

Hope this helps.

Jack replied on Tuesday, October 27, 2009

Is there an example in any of the samples of using binary XML in WCF ?

Thanks

jack

Jack replied on Tuesday, October 27, 2009

Well it isn't hard.  Here is an example for SL for anyone interested:

 

1) Add a new customBinding to both the server/client

2) Change the endpoint to use the new binding name and configuration.

 

3) The maxRecieved/buffer etc sizes are in different spots than in the basic HTTP bindings and are different on the Client than on the server.   But it only took 5 mins which was less time than I spent looking for an example.

 

In ServiceReferences.ClientConfig:

 

<configuration>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicHttpBinding_IWcfPortal" maxBufferSize="80000000"

            maxReceivedMessageSize="80000000" receiveTimeout="00:10:00" sendTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00">

        </binding>

      </basicHttpBinding>

      <customBinding>

        <binding name="BinaryBinding_IWcfPortal">

          <binaryMessageEncoding/>

          <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"/>

        </binding>

      </customBinding>

    </bindings>

    <client>

     

      <!--<endpoint address="http://localhost:2555/WcfPortal.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IWcfPortal"

                contract="Csla.WcfPortal.IWcfPortal" name="BasicHttpBinding_IWcfPortal" />-->

      <endpoint address="http://localhost:2555/WcfPortal.svc" binding="customBinding" bindingConfiguration="BinaryBinding_IWcfPortal"

                contract="Csla.WcfPortal.IWcfPortal" name="BinaryBinding_IWcfPortal" />

 

    </client>

  </system.serviceModel>

</configuration>

 

In web.config in WCF project:

 

<bindings>

                  <basicHttpBinding>

                        <binding name="BasicHttpBinding_IWcfPortal" maxBufferSize="80000000" maxReceivedMessageSize="80000000" receiveTimeout="00:10:00" sendTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00">

                              <readerQuotas maxBytesPerRead="80000000" maxArrayLength="80000000" maxStringContentLength="80000000"/>

                        </binding>

                  </basicHttpBinding>

      <customBinding>

        <binding name="BinaryBinding_IWcfPortal" receiveTimeout="00:10:00" sendTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00" >

          <binaryMessageEncoding maxReadPoolSize="2147483647" maxSessionSize="2147483647" maxWritePoolSize="2147483647">

            <readerQuotas maxDepth="32" maxStringContentLength="5242880" maxArrayLength="200000" maxBytesPerRead="4096" maxNameTableCharCount="16384" />

          </binaryMessageEncoding>

          <httpTransport maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>

        </binding>

      </customBinding>

            </bindings>

            <services>

                  <!--<service behaviorConfiguration="WcfPortalBehavior" name="Csla.Server.Hosts.Silverlight.WcfPortal">-->

        <service behaviorConfiguration="WcfPortalBehavior" name="AlexanderGracie.Library.Compression.CompressedHost">

          <endpoint address="" binding="customBinding" contract="Csla.Server.Hosts.Silverlight.IWcfPortal" bindingConfiguration="BinaryBinding_IWcfPortal">

            <!--<endpoint address="" binding="basicHttpBinding" contract="Csla.Server.Hosts.Silverlight.IWcfPortal" bindingConfiguration="BasicHttpBinding_IWcfPortal">-->

          <identity>

            <dns value="localhost"/>

          </identity>

      </endpoint>

        </service>

    </services>

      </system.serviceModel>

 

 

From: Jack [mailto:cslanet@lhotka.net]
Sent: October-27-09 1:47 PM
To: jaddington@alexandergracie.com
Subject: Re: [CSLA .NET] Silverlight Compression (which is better binary WCF or CslaLight example?)

 

Is there an example in any of the samples of using binary XML in WCF ?

Thanks

jack


Copyright (c) Marimer LLC