I have pending the review of this email that a user has sent to try to give a speed up on the embedded server support i have done a quick review ( haven't time for more until now ) i will try to do a quick review tomorrow.

I have forwarded it to list to hear comments on the changes the use proposes :) as i have doubs on some of them and seconds opinions are really welcome !!!!!!!!!!!!!!!!!!!!!!!!!!!!!


Thanks in advance



-------------------------------------------- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ---------------------------------------------


As I have mailed to the mailinglist:

I have improved the speed of the DataReader about 3x I can get it to compete with the GDS server connection if but I will have to discuss this with you. I know my PInvoke and .Net optimalizations, however I need somebody to review the use of the API.

These are the performances I get when using my modified version to read 8600 records with 60 columns
using (IDataReader reader = cmd.ExecuteReader()) {
   while (reader.Read()) {
        for (int idx = 0; idx < reader.FieldCount; idx++) {
               reader.GetValue(idx);
       }
   }

Embedded:
   Before: 9688 ms
   After: 3047 ms

Server:
   Unmodified: 1141 ms

I think I can get the embedded version to the same performance level of the Server version.

The modifications were done for 1.7, but can be easily reproduced for the 2.0 branch.

I attached a Speedup.txt explaining the steps I have taken.
Also I attached a patch for the 1.7 version (patched against 18 oct. 2006)

I hope to hear from you soon, because I need some more speedup in my application. I have a few questions at the end of Speedup.txt.
Also I would like to hear what you think of my modifications.

Regards,
Jelle Hissink

--
Carlos Guzmán Álvarez
Vigo-Spain

http://carlosga.wordpress.com

Embedded database query took almost 10 seconds while the server connected 
version took
about 1.1 seconds. The ant profiler pointed in the direction of the 
XsqldaMarshaler 
(more then 90 % of the time was spent in the marshaller class
 % of time of IDataReader.Read()
 MarshalNativeToManaged  - 48 % 
 MarshalManagedToNative  - 33 %
 CleanUpNativeData       - 10 %
 next was Descriptor.getItem() with almost 4 % and DBValue.ctor with around 2 %)

Test situation:
interating over a select query that returns about 8600 rows and containing 
about 60 columns.

Basicly doing a:
                                int startTicks = Environment.TickCount;
                                
                                using (IDbCommand cmd = conn.CreateCommand()) {
                                        cmd.CommandText = 
                                                "SELECT 
HelpdeskRegistration.\"Key\", HelpdeskRegistration.TemplateName, 
HelpdeskRegistration.MonitorSLA, HelpdeskRegistration.TimeSpendString, 
HelpdeskRegistration.BTimeSpendString, HelpdeskRegistration.Summary, 
HelpdeskRegistration.Description, HelpdeskRegistration.ExternalReference, 
HelpdeskRegistration.Solution, HelpdeskRegistration.Notes, 
HelpdeskRegistration.Publish, HelpdeskRegistration.Categorie_K, 
HelpdeskRegistration.Categorie_T, HelpdeskRegistration.RegistrationType_K, 
HelpdeskRegistration.RegistrationType_T, HelpdeskRegistration.Impact_K, 
HelpdeskRegistration.Impact_T, HelpdeskRegistration.Priority_K, 
HelpdeskRegistration.Priority_T, HelpdeskRegistration.Status_K, 
HelpdeskRegistration.Status_T, HelpdeskRegistration.Assignee_K, 
HelpdeskRegistration.Assignee_T, HelpdeskRegistration.Requestor_K, 
HelpdeskRegistration.Requestor_T, HelpdeskRegistration.AffectedEndUser_K, 
HelpdeskRegistration.AffectedEndUser_T, HelpdeskRegistration.RegistrationNr, 
HelpdeskRegistration.OpenDate, HelpdeskRegistration.CloseDate, 
HelpdeskRegistration.NeedByDate, HelpdeskRegistration.FreeBool, 
HelpdeskRegistration.FreeInt, HelpdeskRegistration.FreeString, 
HelpdeskRegistration.FreeString1, HelpdeskRegistration.FreeDate, 
HelpdeskRegistration.FreeNumber, HelpdeskRegistration.Exported, 
HelpdeskRegistration.Export, HelpdeskRegistration.RegisteredBy_K, 
HelpdeskRegistration.RegisteredBy_T, HelpdeskRegistration.lfFreeBool_K, 
HelpdeskRegistration.lfFreeBool_T, HelpdeskRegistration.lfFreeString_K, " +
                                                
"HelpdeskRegistration.lfFreeString_T, HelpdeskRegistration.lfFreeString1_K, 
HelpdeskRegistration.lfFreeString1_T, HelpdeskRegistration.lfFreeInt_K, 
HelpdeskRegistration.lfFreeInt_T, HelpdeskRegistration.lfFreeNumber_K, 
HelpdeskRegistration.lfFreeNumber_T, HelpdeskRegistration.lfFreeDate_K, 
HelpdeskRegistration.lfFreeDate_T, HelpdeskRegistration.Asset1_K, 
HelpdeskRegistration.Asset1_T, HelpdeskRegistration.Asset2_K, 
HelpdeskRegistration.Asset2_T, HelpdeskRegistration.Asset3_K, 
HelpdeskRegistration.Asset3_T, HelpdeskRegistration.OrderNr" + 
Environment.NewLine + 
                                                "FROM HelpdeskRegistration" + 
Environment.NewLine + 
                                                "    WHERE 
HelpdeskRegistration.TypeNameId=37;";

                                        using (IDataReader reader = 
cmd.ExecuteReader()) {
                                                while (reader.Read()) {
                                                  // not reading data, just 
iterating for this test...
                                                }
                                        }       
                                }
                                
                                Console.WriteLine("{0} ms", 
Environment.TickCount - startTicks);

Changes I made:
1) Charset.GetEncoding() cached
  It is called frequently and costs relatively much. Caching provides some easy 
to get speedup.
2) Try to optimize memory allocation/release 
  Memory allocation is done in a lot of small blocks in 
XsqldaMarshaler.MarshalManagedToNative allocates a 
  large number of small memory blocks. This can be optimized if the data is 
placed after the structure.
  Cleanup can also be simplified
    - Marshal.DestroyStructure is never needed, it only releases things like 
COM-BStrings, 
      as all of the data is contained within the structure it is a redundant 
call...
    - When we only need one Marshal.FreeHGlobal to release all the memory we no 
longer need to
      call Marshal.PtrToStructure
    - So the only call we need to make one call to Marshal.FreeHGlobal to 
release all the memory.
3) more then 50 % of the remaining 6656 ms seems to origionate from 
XsqldaMarshaler.GetString and  XsqldaMarshaler.GetStringBuffer
  Im going to reference times in profiler seconds (ps) as it runs slower in 
profiler then in real life...
  First we analyse XsqldaMarshaler.GetString():
  a) Profiling shows:
    - 3.62 ps is spent in Charset.GetString(byte[])
    - 2.92 ps is spent on value.Replace('\0', ' ').Trim();
    Replacing value.Replace('\0', ' ').Trim(); with value.TrimEnd('\0', ' ', 
'\t', '\n', '\r'); shifts the balance
    The TrimEnd() should not change the operations (as it seems it is only used 
to remove padding at the end of the string).
    - 3.81 ps is spent in Charset.GetString(byte[])
    - 1.27 ps is spent on value.TrimEnd('\0', ' ', '\t', '\n', '\r');
  b) Pre-trimming the byte array in XsqldaMarshaler.GetString() 
    (getting the length of the array and then decreasing it while it is ending 
in '\0' or whitespace)
    This should ease the task of Charset.GetString() and also should reduce the 
TrimEnd to do nothing, thus I removed it.
    Off course these optimizations only can be applied when 
charset.BytesPerCharacter <= 1
    Results:
    - 3.39 ps for Charset.GetString(byte[], int offset, int count); (less 
characters so somewhat faster)
    - 0.85 ps TrimEnd() removed but got an extra loop removing trailing '\0' 
and ' ' from the byte array
  c) I feel we cannot improve much upon XsqldaMarshaler.GetStringBuffer() 
except I feel a hashtable would allow some caching at higher speed.
    So let's try that... 
    - 5.71 ps for charset.GetBytes(string, idx, count, buffer, startoffset)
    Testing with a static hashtable to confirm...
    - 5.19 ps now it is for the total routine
    This doesn't seem worth the fact that you will have to pass around a 
Hashtable for caching, so dumping this change.
4) A large change seems to be to not call 
XsqldaMarshaler.MarshalManagedToNative and XsqldaMarshaler.CleanUpNativeData
    on every call to Fetch(). So caching the IntPtr and releasing it in 
Release() (overriden that) and calling CleanUpNativeData on finish (status == 
100)
5) Some minor optimalizations (caching Marshal.SizeOf calls)

Results (version 1.7):

percentage  - time    - details
100.0 %     - 9390 ms - origional unmodified code
 83.9 %     - 7875 ms - (1) modification for Charset class (caching result of 
Charset.GetEncoding())
 70.1 %     - 6656 ms - (2) allocating one continuous block of memory for 
MarshalManagedToNative
 58.6 %     - 5500 ms - (3a) Changed trimming of strings in 
XsqldaMarshaler.GetString()
 49.6 %     - 4656 ms - (3b) Pre-trimming the byte array in 
XsqldaMarshaler.GetString()
 32.8 %     - 3078 ms - (4) Caching IntPtr sqlda within Fetch()
 31.1 %     - 2922 ms - (5) caching Marshal.SizeOf calls
 
The percentage can be dropped to about 10 % as the remaining time is largely 
used for 
XsqldaMarshaler.GetStringBuffer() however I'm unsure wether the Fetch() really 
uses this.
Maybe we could come up with a version XsqldaMarshaler.MarshalNativeToManaged() 
that reuses the last Descriptor.

Attachment: source-1.7.18-10-2006.patch
Description: Binary data

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Firebird-net-provider mailing list
Firebird-net-provider@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/firebird-net-provider

Reply via email to