[Mono-dev] ToString() performace in Mono

Andreas Nahr ClassDevelopment at A-SoftTech.com
Sun Nov 25 05:54:31 EST 2007


There seem to be some low-hanging fruit there. Actually I did some
optimizations some years ago, but some were rejected because of possible
"code bloat".

Here are some things in the current implementation that could likely be
improved:
1) ToString calls FormatGeneral, which is the most complex Formatting. Is
that really needed for a parameterless call with the Default FormatProvider?
2) 		internal static string FormatGeneral (NumberStore ns)
		{
			return FormatGeneral (ns, -1,
NumberFormatInfo.CurrentInfo, true, false);
		}
Calls into a more specific FormatGeneral version with a lot of fixed
parameters. It would be possible to drop quite a few of them with a specific
implementation (or not call FormatGeneral at all (see 1))
3) There seem to be some bad magic numbers (assumptions) in FormatGeneral:
StringBuilder cb = new StringBuilder (ns.IntegerDigits + precision + 16);
This means we overallocate for every number (seems like it might be some
kind of max). The problem is that once StringBuilder.ToString() is called it
will throw away and recreate the string if a string is less than half
capacity. With the +16 this is basically guaranteed for any but the most
complex formattings. It would be better to choose a realistic value and then
let StringBuilder expand if neccessary (like ns.IntegerDigits + precision +
1)
4) FormatNumber has the same problem as in 3:
StringBuilder sb = new StringBuilder(ns.IntegerDigits * 3 + precision);
In fact the *3 is even more bogus...
5) another huge CPU-sink is the following:  1062,500
System.Globalization.NumberFormatInfo::get_CurrentInfo()
One of the problems seems to be the following code in Thread:
		public CultureInfo CurrentCulture {
			get {
				if (in_currentculture)
					/* Bail out */
					return CultureInfo.InvariantCulture;

				CultureInfo culture =
GetCachedCurrentCulture ();
				if (culture != null)
					return culture;
This means we need a call to GetCachedCurrentCulture (an internalcall) once
for retrieving anything up the chain. Perhaps it would be possible to find
another mechanism to do it like adding a bool isChanged that gets modified
by the runtime so we don't need to do that InternalCall every time
				if (isChanged)
					CultureInfo culture =
GetCachedCurrentCulture ();
				if (culture != null)
					return culture;

Attached is a profile on Windows/Mono 1.2.5:
val is 599999 and time 9047
Total time spent compiling 453 methods (sec): 0,07813
Slowest method to compile (sec): 0,01563:
System.Threading.Thread::GetCachedCurrentCulture()
Time(ms) Count   P/call(ms) Method name
########################
 9109,375       1  9109,375
compareCompare.Class1::runtime_invoke_void_string[](object,intptr,intptr,int
ptr)
  Callers (with count) that contribute at least for 1%:
########################
 9109,375       1  9109,375   compareCompare.Class1::Main(string[])
  Callers (with count) that contribute at least for 1%:
           1  100 %
compareCompare.Class1::runtime_invoke_void_string[](object,intptr,intptr,int
ptr)
########################
 8906,250  600006    0,015   System.Int32::ToString()
  Callers (with count) that contribute at least for 1%:
      600000  99 % compareCompare.Class1::Main(string[])
########################
 7531,250  600006    0,013
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore)
  Callers (with count) that contribute at least for 1%:
      600006  100 % System.Int32::ToString()
########################
 5750,000  600006    0,010
System.NumberFormatter::FormatGeneral(NumberFormd
by the runtime int,Number
FormatInfo,bool,bool)
  Callers (with count) that contribute at least for 1%:
      600006  100 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore)
########################
 1609,375  600004    0,003
.NumberStore::AppendIntegerString(int,StringBuilder)
  Callers (with count) that contribute at least for 1%:
      600003  99 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
 1109,375  600006    0,002   System.Text.StringBuilder::ToString()
  Callers (with count) that contribute at least for 1%:
      600003  99 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
 1062,500  600015    0,002
System.Globalization.NumberFormatInfo::get_CurrentInfo()
  Callers (with count) that contribute at least for 1%:
      600006  99 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore)
########################
 890,625  600176    0,001   System.String::Substring(int,int)
  Callers (with count) that contribute at least for 1%:
      600003  99 % System.Text.StringBuilder::ToString()
########################
 703,125  600004    0,001   System.Text.StringBuilder::.ctor(int)
  Callers (with count) that contribute at least for 1%:
      600003  99 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
 687,500  600007    0,001   .NumberStore::.ctor(int)
  Callers (with count) that contribute at least for 1%:
      600006  99 % System.Int32::ToString()
########################
 671,875 1200384    0,001   System.String::memcpy(byte*,byte*,int)
  Callers (with count) that contribute at least for 1%:
      600176  49 % System.String::Substring(int,int)
      600006  49 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore)
########################
 484,375  600004    0,001
System.Text.StringBuilder::.ctor(string,int,int,int)
  Callers (with count) that contribute at least for 1%:
      600004  100 % System.Text.StringBuilder::.ctor(int)
########################
 468,750 3481%:
      600015  100 % System.Globalization.CultureInfo::get_NumberFormat()
########################
 234,375 1200023    0,000   System.String::memset(byte*,int,int)
  Callers (with count) that contribu#####
 437,500  600015    0,001
System.Globalization.CultureInfo::get_NumberFormat()
  Callers (with count) that contribute at least for 1%:
      600015  100 % System.Globalization.NumberFormatInfo::get_CurrentInfo()
########################
 390,625 1200710    0,000   System.String::InternalAllocateStr(int)
  Callers (with count) that contribute at least for 1%:
      600176  49 % System.String::Substring(int,int)
      600004  49 % System.Text.StringBuilder::.ctor(string,int,int,int)
########################
 328,125 3000018    0,000   .NumberStore::get_IntegerDigits()
  Callers (with count) that contribute at least for 1%:
     2400012  79 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
      600004  20 % .NumberStore::AppendIntegerString(int,StringBuilder)
########################
 312,500  600018    0,001   System.Threading.Thread::get_CurrentCulture()
  Callers (with count) that contribute at least for 1%:
      600015  99 % System.Globalization.NumberFormatInfo::get_CurrentInfo()
########################
 296,875  600015    0,000   System.Globalization.CultureInfo::CheckNeutral()
  Callers (with count) that contribute at least for 1%:
      600015  100 % System.Globalization.CultureInfo::get_NumberFormat()
########################
 234,375 1200023    0,000   System.String::memset(byte*,int,int)
  Callers (with count) that contribute at least for 1%:
      600007  49 % .NumberStore::.ctor(int)
      600006  49 % System.Int32::ToString()
########################
 234,375  600255    0,000
System.Object::__icall_wrapper_mono_object_new_specific(intptr)
  Callers (with count) that contribute at least for 1%:
      600003  99 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
 203,125 1200311    0,000   System.String::memcpy4(byte*,byte*,int)
  Callers (with count) that contribute at least for 1%:
     1200311  100 % System.String::memcpy(byte*,byte*,int)
########################
 187,500  600219    0,000
System.Object::__icall_wrapper_mono_array_new_specific(intptr,int)
  Callers (with count) that contribute at least for 1%:
      600007  99 % .NumberStore::.ctor(int)
########################
 125,000  600003    0,000   .NumberStore::get_DecimalDigits()
  Callers (with count) that contribute at least for 1%:
      600003  100 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
  93,750      31    3,024
System.String::runtime_invoke_void(object,intptr,intptr,intptr)
  Callers (with count) that contribute at least for 1%:
           3  10 % I18N.Common.Manager::.ctor()
           2   6 % System.Int32::ToString()
           2   6 %
System.MonoType::InvokeMember(string,BindingFlags,Binder,object,object[],Par
ameterModifier[],CultureInfo,string[])
           2   6 %
Mono.Globalization.Unicode.SimpleCollator::.ctor(CultureInfo)
           1   3 % System.Globalization.NumberFormatInfo::get_CurrentInfo()
           1   3 % System.Threading.Thread::get_CurrentCulture()
           1   3 %
System.Globalization.CultureInfo::ConstructInvariant(bool)
           1   3 % compareCompare.Class1::Main(string[])
           1   3 % System.Console::.cctor()
           1   3 %
System.Reflection.MonoMethodInfo::get_parameter_info(intptr)
           1   3 %
System.Reflection.MonoMethod::Invoke(object,BindingFlags,Binder,object[],Cul
tureInfo)
           1   3 % System.Collections.Hashtable::Add(object,object)
           1   3 % System.Globalization.TextInfo::ToLower(char)
           1   3 % System.Collections.Hashtable::KeyEquals(object,object)
           1   3 % System.Globalization.CultureInfo::get_CompareInfo()
           1   3 % System.Globalization.CompareInfo::.cctor()
           1   3 % System.Globalization.CompareInfo::.ctor(CultureInfo)
           1   3 %
Mono.Globalization.Unicode.MSCompatUnicodeTable::FillCJK(string,CodePointInd
exer&,byte*&,byte*&,CodePointIndexer&,byte*&)
           1   3 %
Mono.Globalization.Unicode.MSCompatUnicodeTable::BuildTailoringTables(Cultur
eInfo,TailoringInfo,Contraction[]&,Level2Map[]&)
           1   3 % System.Console::OpenStandardError(int)
           1   3 %
System.IO.FileStream::.ctor(intptr,FileAccess,bool,int,bool,bool)
           1   3 %
System.IO.UnexceptionalStreamWriter::.ctor(Stream,Encoding)
           1   3 % System.IO.StreamWriter::.ctor(Stream,Encoding,int)
           1   3 %
System.IO.UnexceptionalStreamReader::.ctor(Stream,Encoding)
########################
  78,125  600006    0,000   .NumberStore::get_ZeroOnly()
  Callers (with count) that contribute at least for 1%:
      600006  100 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
  78,125  600018    0,000
System.Threading.Thread::GetCachedCurrentCulture()
  Callers (with count) that contribute at least for 1%:
      600018  100 % System.Threading.Thread::get_CurrentCulture()
########################
  62,500       1   62,500   System.Console::.cctor()
  Callers (with count) that contribute at least for 1%:
           1  100 %
System.String::runtime_invoke_void(object,intptr,intptr,intptr)
########################
  46,875  600003    0,000   .NumberStore::RoundDecimal(int,bool,bool)
  Callers (with count) that contribute at least for 1%:
      600003  100 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
########################
  31,250       4    7,813
System.Reflection.MonoMethod::Invoke(object,BindingFlags,Binder,object[],Cul
tureInfo)
  Callers (with count) that contribute at least for 1%:
           4  100 %
System.MonoType::InvokeMember(string,BindingFlags,Binder,object,object[],Par
ameterModifier[],CultureInfo,string[])
########################
  31,250       2   15,625
System.Text.Encoding::InvokeI18N(string,object[])
  Callers (with count) that contribute at least for 1%:
           2  100 % System.Text.Encoding::GetEncoding(int)
########################
  31,250       2   15,625   System.Text.Encoding::GetEncoding(int)
  Callers (with count) that contribute at least for 1%:
           2  100 % System.Console::.cctor()
########################
  31,250       5    6,250
System.MonoType::InvokeMember(string,BindingFlags,Binder,object,object[],Par
ameterModifier[],CultureInfo,string[])
  Callers (with count) that contribute at least for 1%:
           4  80 % System.Text.Encoding::InvokeI18N(string,object[])
           1  20 % I18N.Common.Manager::Instantiate(string)
########################
  31,250  600015    0,000
System.Globalization.CultureInfo::get_IsNeutralCulture()
  Callers (with count) that contribute at least for 1%:
      600015  100 % System.Globalization.CultureInfo::CheckNeutral()
########################
  31,250       4    7,813
System.Reflection.MonoMethod::InternalInvoke(object,object[])
  Callers (with count) that contribute at least for 1%:
           4  100 %
System.Reflection.MonoMethod::Invoke(object,BindingFlags,Binder,object[],Cul
tureInfo)
########################
  15,625       1   15,625
System.IO.StreamWriter::.ctor(Stream,Encoding,int)
  Callers (with count) that contribute at least for 1%:
           1  100 % System.IO.StreamWriter::.cctor()
########################
  15,625       4    3,906   System.Collections.Hashtable::get_Item(object)
  Callers (with count) that contribute at least for 1%:
           4  100 % I18N.Common.Manager::Instantiate(string)
########################
  15,625       2    7,813
I18N.Common.Manager::runtime_invoke_Manager(object,intptr,intptr,intptr)
  Callers (with count) that contribute at least for 1%:
           2  100 %
System.Reflection.MonoMethod::InternalInvoke(object,object[])
########################
  15,625       1   15,625   I18N.Common.Manager::LoadInternalClasses()
  Callers (with count) that contribute at least for 1%:
           1  100 % I18N.Common.Manager::LoadClassList()
########################
  15,625       1   15,625   I18N.Common.Manager::LoadClassList()
  Callers (with count) that contribute at least for 1%:
           1  100 % I18N.Common.Manager::.ctor()
########################
  15,625       2    7,813   I18N.Common.Manager::Instantiate(string)
  Callers (with count) that contribute at least for 1%:
           2  100 % I18N.Common.Manager::GetEncoding(int)
########################
  15,625     347    0,045   System.Globalization.TextInfo::ToLower(string)
  Callers (with count) that contribute at least for 1%:
         347  100 %
System.Collections.CaseInsensitiveHashCodeProvider::GetHashCode(object)
########################
  15,625       1   15,625   System.Collections.Comparer::.cctor()
  Callers (with count) that contribute at least for 1%:
           1  100 %
System.String::runtime_invoke_void(object,intptr,intptr,intptr)
########################
  15,625       2    7,813   I18N.Common.Manager::GetEncoding(int)
  Callers (with count) that contribute at least for 1%:
           2  100 %
I18N.Common.Manager::runtime_invoke_Encoding_int(object,intptr,intptr,intptr
)
########################
  15,625       1   15,625   System.Collections.Comparer::.ctor(CultureInfo)
  Callers (with count) that contribute at least for 1%:
           1  100 % System.Collections.Comparer::.cctor()
########################
  15,625       2    7,813   I18N.Common.Manager::get_PrimaryManager()
  Callers (with count) that contribute at least for 1%:
           2  100 %
I18N.Common.Manager::runtime_invoke_Manager(object,intptr,intptr,intptr)
########################
  15,625       2    7,813
System.IO.UnexceptionalStreamWriter::.ctor(Stream,Encoding)
  Callers (with count) that contribute at least for 1%:
           2  100 % System.Console::.cctor()
########################
  15,625       1   15,625   I18N.Common.Manager::.ctor()
  Callers (with count) that contribute at least for 1%:
           1  100 % I18N.Common.Manager::get_PrimaryManager()
########################
  15,625       2    7,813
I18N.Common.Manager::runtime_invoke_Encoding_int(object,intptr,intptr,intptr
)
  Callers (with count) that contribute at least for 1%:
           2  100 %
System.Reflection.MonoMethod::InternalInvoke(object,object[])
########################
  15,625       1   15,625   System.IO.StreamWriter::.cctor()
  Callers (with count) that contribute at least for 1%:
           1  100 %
System.String::runtime_invoke_void(object,intptr,intptr,intptr)
########################
  15,625       2    7,813
System.Globalization.CompareInfo::.ctor(CultureInfo)
  Callers (with count) that contribute at least for 1%:
           2  100 % System.Globalization.CultureInfo::get_CompareInfo()
########################
  15,625       2    7,813
System.IO.UnexceptionalStreamWriter::.ctor(Stream,Encoding)
  Callers (with count) that contribute at least for 1%:
           2  100 %
System.IO.UnexceptionalStreamWriter::.ctor(Stream,Encoding)
########################
  15,625     347    0,045
System.Collections.CaseInsensitiveHashCodeProvider::GetHashCode(object)
  Callers (with count) that contribute at least for 1%:
         347  100 % System.Collections.Hashtable::GetHash(object)
########################
  15,625     173    0,090   System.Collections.Hashtable::Find(object)
  Callers (with count) that contribute at least for 1%:
         173  100 % System.Collections.Hashtable::Contains(object)
########################
  15,625       2    7,813
System.Globalization.CultureInfo::get_CompareInfo()
  Callers (with count) that contribute at least for 1%:
           1  50 % System.Collections.Comparer::.ctor(CultureInfo)
           1  50 %
System.Collections.CaseInsensitiveComparer::Compare(object,object)
########################
  15,625       3    5,208
System.IO.StreamWriter::.ctor(Stream,Encoding,int)
  Callers (with count) that contribute at least for 1%:
           2  66 % System.IO.StreamWriter::.ctor(Stream,Encoding)
           1  33 % System.IO.StreamWriter::.ctor(Stream,Encoding,int)
########################
  15,625       2    7,813
System.Collections.Hashtable::KeyEquals(object,object)
  Callers (with count) that contribute at least for 1%:
           2  100 % System.Collections.Hashtable::get_Item(object)
########################
  15,625     352    0,044   System.Collections.Hashtable::GetHash(object)
  Callers (with count) that contribute at least for 1%:
         175  49 % System.Collections.Hashtable::PutImpl(object,object,bool)
         173  49 % System.Collections.Hashtable::Find(object)
           4   1 % System.Collections.Hashtable::get_Item(object)
########################
  15,625     173    0,090   System.Collections.Hashtable::Contains(object)
  Callers (with count) that contribute at least for 1%:
         173  100 % I18N.Common.Manager::LoadInternalClasses()
Total number of calls: 23301421

Allocation profiler
Total mem Method
########################
   45486 KB System.Text.StringBuilder::.ctor(string,int,int,int)
       45486 KB   600004 System.String                                   
  Callers (with count) that contribute at least for 1%:
      600004  100 % System.Text.StringBuilder::.ctor(int)
########################
   15022 KB System.String::Substring(int,int)
       15022 KB   600176 System.String                                   
  Callers (with count) that contribute at least for 1%:
      600003  99 % System.Text.StringBuilder::ToString()
########################
   14062 KB
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore,int,Number
FormatInfo,bool,bool)
       14062 KB   600003 System.Text.StringBuilder                       
  Callers (with count) that contribute at least for 1%:
      600006  100 %
System.NumberFormatter::FormatGeneral(NumberFormatter/NumberStore)
########################
   12782 KB .NumberStore::.ctor(int)
       12782 KB   600007 System.Byte[]                                   
  Callers (with count) that contribute at least for 1%:
      600006  99 % System.Int32::ToString()
Total memory allocated: 87428 KB




More information about the Mono-devel-list mailing list