Optimization and IIf

Dec 2, 2010 at 9:01 PM

Hi Throb,

I use Obtics massively in a major project management planning that needs dynamic view. Obtics is perfect for that.
I need to fully optimize the creation of my objects. I have two hotspots.

The first I'd like you to help resolve comes from the following class : 

    public class DynamicWorkDay : INotifyPropertyChanged, IValueProvider<WorkDay>
    {
        private static readonly Func<Employee, DateTime, IValueProvider<WorkDay>> WorkDayProvider = ExpressionObserver.Compile((Employee e, DateTime date) => e.WorkDays == null ? null : e.WorkDays.SingleOrDefault(wd => wd.Date == date));

        private static readonly Func<IValueProvider<DynamicTypicalDayAtDate>, IValueProvider<EntityCollection<TimeSlot>>> TypicalDayTimeSlotsProvider = ExpressionObserver.Compile((IValueProvider<DynamicTypicalDayAtDate> typicalDay) => typicalDay.Value == null ? null : typicalDay.Value.Version.Value == null ? null : typicalDay.Value.Version.Value.TimeSlots);
        private static readonly Func<IValueProvider<WorkDay>, IValueProvider<EntityCollection<TimeSlot>>> TimeSlotsProvider = ExpressionObserver.Compile((IValueProvider<WorkDay> wd) => wd.Value == null ? null : wd.Value.TimeSlots);
        
        
        private static readonly Func<IValueProvider<WorkDay>, IValueProvider<bool>> UseTypicalDayProvider = ExpressionObserver.Compile((IValueProvider<WorkDay> wd) => wd.Value != null && wd.Value.TypicalDay != null && wd.Value.TypicalWeek == null);
        private static readonly Func<IValueProvider<WorkDay>, IValueProvider<bool>> UseTypicalWeekProvider = ExpressionObserver.Compile((IValueProvider<WorkDay> wd) => wd.Value != null && wd.Value.TypicalWeek != null);
        
        
        private static readonly Func<IValueProvider<WorkDay>, IValueProvider<DynamicTypicalDayAtDate>> TypicalDayProvider = ExpressionObserver.Compile((IValueProvider<WorkDay> wd) => wd.Value == null ? null : wd.Value.TypicalDay == null ? null : new DynamicTypicalDayAtDate(wd.Value.TypicalDay, wd.Value.Date));
        private static readonly Func<IValueProvider<WorkDay>, IValueProvider<DynamicTypicalDayAtDate>> TypicalDayOfTypicalWeekProvider = ExpressionObserver.Compile((IValueProvider<WorkDay> wd) => wd.Value == null ? null : wd.Value.TypicalWeek == null ? null : new DynamicTypicalWeekAtDate(wd.Value.TypicalWeek, wd.Value.Date).GetDynamicTypicalDay(wd.Value.Date).Value);


        private readonly IValueProvider<WorkDay> _workDay;
        private IValueProvider<String> _color;
        private IValueProvider<EntityCollection<TimeSlot>> _timeSlots;
        private IValueProvider<DynamicTypicalDayAtDate> _typicalDay;
        private IValueProvider<bool> _useTypical;
        private IValueProvider<bool> _useTypicalDay;
        private IValueProvider<bool> _useTypicalWeek;
        private IValueProvider<string> _content;

        public DynamicWorkDay(Employee employee, DateTime dateTime)
        {
            if (employee == null) throw new ArgumentNullException("employee");

            this.Employee = employee;
            this.DateTime = dateTime;

            this._workDay = WorkDayProvider(this.Employee, this.DateTime);
    
            Debug.Assert(_workDay != null);
        }

        public IValueProvider<DynamicTypicalDayAtDate> TypicalDay
        {
            get { return this._typicalDay ?? (this._typicalDay = this.UseTypicalDay.IIf(TypicalDayProvider(this._workDay), this.UseTypicalWeek.IIf(TypicalDayOfTypicalWeekProvider(this._workDay), ValueProvider.Static((DynamicTypicalDayAtDate) null)))); }
        }

        public Employee Employee { get; set; }
        public DateTime DateTime { get; set; }

        public IValueProvider<String> Color
        {
            get { return this._color ?? (this._color = this.UseTypical.IIf(this.TypicalDay.Select(td => td == null ? ValueProvider.Static((String)null) : td.Version.Select(tdv => tdv == null ? null : tdv.Color)), (String)null)); }
        }

        public IValueProvider<String> Content
        {
            get { return this._content ?? (this._content = this.UseTypical.IIf(this.TypicalDay.Select(td => td == null ? ValueProvider.Static((String)null) : td.Version.Select(tdv => tdv == null ? "Chargement..." : tdv.Name)), (String)null)); }
        }

        public IValueProvider<bool> UseTypical
        {
            get { return this._useTypical ?? (this._useTypical = this.UseTypicalDay.Or(this.UseTypicalWeek)); }
        }

        public IValueProvider<bool> UseTypicalDay
        {
            get
            {
                return this._useTypicalDay ?? (this._useTypicalDay = UseTypicalDayProvider(this._workDay));
            }
        }

        public IValueProvider<bool> UseTypicalWeek
        {
            get
            {
                return this._useTypicalWeek ?? (this._useTypicalWeek = UseTypicalWeekProvider(this._workDay));
            }
        }

        public IValueProvider<EntityCollection<TimeSlot>> TimeSlots
        {
            get { return this._timeSlots ?? (this._timeSlots = this.UseTypical.IIf(TypicalDayTimeSlotsProvider(this._typicalDay), TimeSlotsProvider(this._workDay))); }
        }

        #region INotifyPropertyChanged Members

        private PropertyChangedEventHandler _propertyChanged;
        private bool register;

        private void OnWorkDayPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "Value")
                this.RaisePropertyChanged("Value");
        }

        public event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                if(register == false)
                    ((INotifyPropertyChanged)this._workDay).PropertyChanged += OnWorkDayPropertyChanged;


                this._propertyChanged += value;
            }
            remove { this._propertyChanged -= value; }
        }

        #endregion

        #region IValueProvider<WorkDay> Members

        public WorkDay Value
        {
            get { return this._workDay.Value; }
            set { throw new NotSupportedException(); }
        }

        object IValueProvider.Value
        {
            get { return this.Value; }
            set { throw new NotSupportedException(); }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        #endregion

        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this._propertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

On the getter of the Color property, there is a big waste of time.
Can
 I optimize the IIf operation ?


The second point is that when the binding between the View and my ViewModel, there is a loss of time on

 Obtics.NCSourcedObjectToVP `2.add_PropertyChanged (PropertyChangedEventHandler)

Is it possible to do something ?

Thank you very much in advance.
Regs,
Vincent BOUZON

Dec 2, 2010 at 9:07 PM

For information, the DynamicWorkDay class is widely used and very often instantiated.
It would be nice if instantiation of 140 DynamicWorkDay (2 weeks for 10 persons) take less than half a second.

Vincent

Coordinator
Dec 6, 2010 at 6:35 PM

Hi Vincent,

There are a few things that might help:

You create a few object types (DynamicTypicalDayAtDate, DynamicTypicalWeekAtDate and probably also DynamicWorkDay) on the fly in observable expressions. The consequence might be that many of these object instance will be created. In such a case I always use the Carrousel pattern. Every of these objects has (I guess) an identifying key. These are probably the parameters you create them with. So, for every unique set of parameters you would like to instantiate one and one only of you object types. You will need a public static X.Create(p1,p2..px) function that for every unique combination of p1,p2..px returns one and one the same object instance. You would use this function instead of 'new X(p1, p2, .. px)'

WorkDay is probably the identifying key for DynamicWorkDay yet you do not instantiate it with WorkdDay as parameter?

I always make all my observable properties lazy and don't use fields to store pipeline instances. Your WorkDay property is not lazy. I don't feel I get a significant improvement when storing pipelines in fields and it clutters the code a bit.

Sometimes when binding IValueProviders from the UI I find that the UI can access the Value property many times. Obtics by default does not buffer values, meaning that a result would be calculated many times over. You could use the ValueProvider.Buffer() extension method to explicitly buffer the result. In that case 'UseTypicalWeekProvider(this._workDay)' would be extended to 'UseTypicalWeekProvider(this._workDay).Buffer()'.

IIf can sometimes be optimized by letting it return a pipeline generating function instead of the pipeline itself (only when you expect the first parameter not to change value too often). Like so:

this._typicalDay = this.UseTypicalDay.IIf(TypicalDayProvider, this.UseTypicalWeek.IIf(TypicalDayOfTypicalWeekProvider, new Func<IValueProvider<WorkDay>, IValueProvider<DynamicTypicalDayAtDate>>( d => ValueProvider.Static((DynamicTypicalDayAtDate) null)))).Select( f => f(this._workDay)) 

Below you can find some carrousel code. You could use it like:

public class DynamicTypicalDayAtDate
{
    public static readonly Func<TypicalDay,DateTime,DynamicTypicalDayAtDate> Create =
        Carrousel.Create(
            (TypicalDay day, DateTime date) => new DynamicTypicalDayAtDate( day, date ),
            dtd => dtd.Day,
            dtd => dtd.Date
        )
    ;
}
Note that once the Day and Date Properties are set they should never change.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TvdP.Collections;

namespace NameSpace
{
    static class Carrousel
    {
        sealed class InternalCarrousel<TKey, TObject> : ConcurrentWeakHashtable<KeyValuePair<UInt32, WeakReference>, KeyValuePair<UInt32, TKey>>
            where TObject : class
        {
            public InternalCarrousel(IEqualityComparer<TKey> keyComparer, Func<TObject, TKey> keyExtractor, Func<TKey, TObject> factory)
            {
                Initialize();

                _KeyComparer = keyComparer;
                _KeyExtractor = keyExtractor;
                _Factory = factory;
            }

            IEqualityComparer<TKey> _KeyComparer;
            Func<TObject, TKey> _KeyExtractor;
            Func<TKey, TObject> _Factory;

            protected override bool IsGarbage(ref KeyValuePair<uint, WeakReference> item)
            {
                var wr = item.Value;
                return wr != null && wr.Target == null;
            }

            protected override bool ItemEqualsItem(ref KeyValuePair<uint, WeakReference> item1, ref KeyValuePair<uint, WeakReference> item2)
            {
                var item1_Second = item1.Value;
                var item2_Second = item2.Value;

                var tgt1 = item1_Second.Target;
                return tgt1 != null ? tgt1.Equals(item2_Second.Target) : object.ReferenceEquals(item1_Second, item2_Second);
            }

            protected override bool ItemEqualsKey(ref KeyValuePair<uint, WeakReference> item, ref KeyValuePair<uint, TKey> key)
            {
                var tgt = (TObject)item.Value.Target;
                return tgt != null && _KeyComparer.Equals( _KeyExtractor(tgt), key.Value );
            }

            protected override uint GetKeyHashCode(ref KeyValuePair<uint, TKey> key)
            { return key.Key; }

            protected override uint GetItemHashCode(ref KeyValuePair<uint, WeakReference> item)
            { return item.Key; }

            protected override bool IsEmpty(ref KeyValuePair<uint, WeakReference> item)
            { return item.Value == null; }

            protected override Type GetKeyType(ref KeyValuePair<uint, WeakReference> item)
            {
                if (item.Value == null)
                    return null;

                var obj = item.Value.Target;

                return obj == null ? null : obj.GetType();
            }

            uint Rehash(int v)
            {
                unchecked
                {
                    ulong m = (ulong)v * 0x00000000e85791a6UL;
                    return (uint)(m ^ (m >> 32) ^ 0x00000000827a092bUL);
                }
            }

            public TObject Get(TKey key)
            {
                var searchKey =
                    new KeyValuePair<UInt32, TKey>(
                        Rehash(_KeyComparer.GetHashCode(key)),
                        key
                    );

                KeyValuePair<UInt32, WeakReference> oldStoredItem;

                TObject res = null;

                if (base.FindItem(ref searchKey, out oldStoredItem))
                    res = (TObject)oldStoredItem.Value.Target;

                if (res == null)
                {
                    var newItem = _Factory(key);

                    if (newItem != null)
                    {
                        var newStoredItem = new KeyValuePair<UInt32, WeakReference>(searchKey.Key, new WeakReference(newItem));

                        do
                        {
                            if (base.GetOldestItem(ref newStoredItem, out oldStoredItem))
                                res = (TObject)oldStoredItem.Value.Target;
                            else
                                res = newItem;
                        }
                        while (res == null);
                    }
                }

                return res;
            }
        }

        public static Func<TKey, TObject> Create<TKey, TObject>(Func<TKey, TObject> factory, Func<TObject, TKey> keyExtractor, IEqualityComparer<TKey> keyComparer)
            where TObject : class
        { return new InternalCarrousel<TKey, TObject>(keyComparer, keyExtractor, factory).Get; }

        public static Func<TKey, TObject> Create<TKey, TObject>(Func<TKey, TObject> factory, Func<TObject, TKey> keyExtractor)
            where TObject : class
        { return Create(factory, keyExtractor, EqualityComparer<TKey>.Default); }
        public static Func<TK1, TK2, TObject> Create<TK1, TK2, TObject>(Func<TK1, TK2, TObject> factory, Func<TObject, TK1> key1Extractor, Func<TObject, TK2> key2Extractor)
            where TObject : class
        {
            var itm =
                Create<Tuple<TK1, TK2>, TObject>(
                    t => factory(t.Item1, t.Item2),
                    o => Tuple.Create(key1Extractor(o), key2Extractor(o))
                )
            ;

            return new Func<TK1, TK2, TObject>( (k1, k2) => itm( Tuple.Create(k1, k2) ) );
        }

} }

 

 

 

 

 

 

Dec 6, 2010 at 9:31 PM

Hi Throb,

Thank you for your answer. Indeed, for the Carousel I started to implement as well, but I'll reuse yours as it is rather easy to use :)

At the Get, if there are two threads that together create the same object how it goes there in your implementation ?
In fact, why call this pattern "Carousel" ? :)

Here's my version :) must inherit and implement the different methods.

 

    public abstract class RepositoryBase<TBase, TArgs> : IEqualityComparer<TArgs>
    {
        protected Dictionary<TArgs, TBase> Created;
        protected List<TArgs> Creating;

        protected RepositoryBase()
        {
            this.Creating = new List<TArgs>();
            this.Created = new Dictionary<TArgs, TBase>(this);
        }

        public virtual TBase GetOrCreate(TArgs args)
        {
            Monitor.Enter(this.Creating);
            Monitor.Enter(this.Created);
            TBase baseObject;
            if (this.Created.TryGetValue(args, out baseObject))
            {
                Monitor.Exit(this.Creating);
                Monitor.Exit(this.Created);

                return baseObject;
            }

            TArgs tmpArgs = this.Creating.FirstOrDefault(c => this.Equals(c, args));
            if (tmpArgs != null)
            {
                Monitor.Exit(this.Creating);
                Monitor.Exit(this.Created);
                Monitor.Enter(tmpArgs);

                baseObject = this.Created[tmpArgs];

                Monitor.Exit(tmpArgs);

                return baseObject;
            }
            else
            {
                this.Creating.Add(args);

                Monitor.Enter(args);
                Monitor.Exit(this.Creating);
                Monitor.Exit(this.Created);

                baseObject = this.Create(args);

                Monitor.Enter(this.Creating);
                Monitor.Enter(this.Created);

                this.Creating.Remove(args);
                this.Created.Add(args, baseObject);

                Monitor.Exit(this.Creating);
                Monitor.Exit(this.Created);

                Monitor.Exit(args);

                return baseObject;
            }
        }
        protected abstract TBase Create(TArgs args);
        protected abstract IEnumerable<TArgs> GetCreators();
        public abstract bool Equals(TArgs x, TArgs y);
        public abstract int GetHashCode(TArgs obj);
    }


Otherwise thank you for the optimization of IIf, I'll do that.
By cons, in my version, I have not the extension method "Buffer". I already try to find a way because I realized the recalculation.

 

Otherwise what time zone are you?

Thank you again.

Best regards,
Vincent BOUZON 

Coordinator
Dec 9, 2010 at 9:26 PM

Hi Vincent,

You are right, the name of the method is not "Buffer" but "Cached". Sorry for the confusion.

With the Carrousel implementation it is possible but unlikely for multiple threads to create a new instance simultanously. When that happens only one instance will actually be used. The other(s) will be released immediately. The advantage is that the carrousel is completely concurrent. Naturally your constructors need to be threadsafe too in case you use multiple threads. 

Why the pattern is called carrousel I don't know. I found it in a book once (with regard to strings). The writer must not have been very famous because I can't find anything about it on the internet.

Regs,

Thomas

Dec 17, 2010 at 2:52 PM

Hi Throb,

I have not the extension method "Cached" too :( Have you an idea when a new stable version will be available?

Regs,
Vincent 

Coordinator
Dec 22, 2010 at 11:32 AM

Hi Vincent,

"Async( )" then? The old version used to cache the result.

Btw. Did you manage to get any performance improvements?

Regs,

Thomas.

Dec 27, 2010 at 2:38 PM

Hi Thomas,

Yes i use but I thought it only allowed to change the thread of events. 

About performance, I managed to get better performance but not enough. I still have performance issues at the Binding between the view and the model :( 

28,46 %   add_PropertyChanged  -  1?788 ms  -  156 calls  -  Obtics.NCSourcedObjectToVP`2.add_PropertyChanged(PropertyChangedEventHandler)
  28,35 %   add_PropertyChanged  -  1?781 ms  -  156 calls  -  Obtics.NCToNPC.add_PropertyChanged(PropertyChangedEventHandler)
    28,35 %   set_HavePropertyChangedListeningClients  -  1?781 ms  -  156 calls  -  Obtics.NCToNPC.set_HavePropertyChangedListeningClients(Boolean)
      28,34 %   SubscribeINC  -  1?781 ms  -  156 calls  -  Obtics.NCObservableObjectBase`1.SubscribeINC(IReceiveChangeNotification)
        28,34 %   set_HaveChangedListeningClients  -  1?781 ms  -  156 calls  -  Obtics.NCObservableObjectBase`1.set_HaveChangedListeningClients(Boolean)
          28,34 %   UpdateHaveChangedListeningClients  -  1?781 ms  -  156 calls  -  Obtics.NCSourcedObject`1.UpdateHaveChangedListeningClients(Boolean, Boolean)
            28,34 %   set_NeedSourceChangedListener  -  1?780 ms  -  156 calls  -  Obtics.NCSourcedObject`1.set_NeedSourceChangedListener(Boolean)
              28,34 %   UpdateNeedSourceChangedListener  -  1?780 ms  -  156 calls  -  Obtics.NCSourcedObject`1.UpdateNeedSourceChangedListener(Boolean, Boolean)
                28,33 %   SubscribeINC  -  1?780 ms  -  156 calls  -  Obtics.NCObservableObjectBase`1.SubscribeINC(IReceiveChangeNotification)
                  28,29 %   set_HaveChangedListeningClients  -  1?777 ms  -  156 calls  -  Obtics.NCObservableObjectBase`1.set_HaveChangedListeningClients(Boolean)
                    28,29 %   UpdateHaveChangedListeningClients  -  1?777 ms  -  156 calls  -  Obtics.NCSourcedObject`1.UpdateHaveChangedListeningClients(Boolean, Boolean)
                      28,29 %   set_NeedSourceChangedListener  -  1?777 ms  -  156 calls  -  Obtics.NCSourcedObject`1.set_NeedSourceChangedListener(Boolean)
                        28,29 %   UpdateNeedSourceChangedListener  -  1?777 ms  -  156 calls  -  Obtics.NCSourcedObject`1.UpdateNeedSourceChangedListener(Boolean, Boolean)
                          28,29 %   SubscribeINC  -  1?777 ms  -  156 calls  -  Obtics.NCObservableObjectBase`1.SubscribeINC(IReceiveChangeNotification)
                            28,28 %   set_HaveChangedListeningClients  -  1?777 ms  -  156 calls  -  Obtics.NCObservableObjectBase`1.set_HaveChangedListeningClients(Boolean)
                              22,90 %   UpdateHaveChangedListeningClients  -  1?438 ms  -  84 calls  -  Obtics.Values.Transformations.CascadingTransformationBase`5.UpdateHaveChangedListeningClients(Boolean, Boolean)
                                22,88 %   RefreshBufferCache  -  1?438 ms  -  84 calls  -  Obtics.Values.Transformations.CascadingTransformationBase`5.RefreshBufferCache
                                  22,37 %   set_Buffer  -  1?405 ms  -  84 calls  -  Obtics.Values.Transformations.CascadingTransformationBase`5.set_Buffer(TItm)
                                    22,37 %   UpdateBuffer  -  1?405 ms  -  84 calls  -  Obtics.Values.Transformations.CascadeTransformation`2.UpdateBuffer(IValueProvider[TType], IValueProvider[TType])
                                      22,37 %   SubscribeINC  -  1?405 ms  -  16?380 calls  -  Obtics.NCObservableObjectBase`1.SubscribeINC(IReceiveChangeNotification)
                                        21,49 %   set_HaveChangedListeningClients  -  1?350 ms  -  16?380 calls  -  Obtics.NCObservableObjectBase`1.set_HaveChangedListeningClients(Boolean)
                                          16,28 %   UpdateHaveChangedListeningClients  -  1?023 ms  -  3?528 calls  -  Obtics.Values.Transformations.CascadingTransformationBase`5.UpdateHaveChangedListeningClients(Boolean, Boolean)
                                            16,20 %   RefreshBufferCache  -  1?018 ms  -  3?528 calls  -  Obtics.Values.Transformations.CascadingTransformationBase`5.RefreshBufferCache
                                              16,06 %   ItmFromSource  -  1?009 ms  -  3?192 calls  -  Obtics.Values.Transformations.CascadeTransformation`2.ItmFromSource
                                                16,04 %   get_Value  -  1?008 ms  -  19?488 calls  -  Obtics.NCSourcedObjectToVP`2.get_Value
                                                  9,50 %   ProtectedGetValue  -  597 ms  -  1?848 calls  -  Obtics.Values.Transformations.PropertyTransformation`2.ProtectedGetValue
                                                    9,49 %   GetValue  -  597 ms  -  504 calls  -  Obtics.Values.Transformations.PropertyTransformation+PropertyClass`2.GetValue(TIn)
                                                      9,49 %   get_RealWorkTimeSlots  -  596 ms  -  168 calls  -  Arcan.Planning.ViewModels.DynamicWorkDay.get_RealWorkTimeSlots
                                                        7,72 %   get_WorkTimeSlots  -  485 ms  -  168 calls  -  Arcan.Planning.ViewModels.DynamicWorkDay.get_WorkTimeSlots
                                                          4,87 %   get_TypicalDay  -  306 ms  -  168 calls  -  Arcan.Planning.ViewModels.DynamicWorkDay.get_TypicalDay
                                                            2,69 %   get_UseTypicalDay  -  169 ms  -  168 calls  -  Arcan.Planning.ViewModels.DynamicWorkDay.get_UseTypicalDay
                                                              ?2,65 %   Create  -  166 ms  -  168 calls  -  Obtics.Values.Transformations.PipelineResultTransformation`3.Create(TKey)
                                                               0,05 %   CreateDelegate  -  3 ms  -  168 calls  -  System.Reflection.Emit.DynamicMethod.CreateDelegate(Type, Object)
                                                            1,01 %   get_UseTypicalWeek  -  63 ms  -  168 calls  -  Arcan.Planning.ViewModels.DynamicWorkDay.get_UseTypicalWeek
                                                              ?0,96 %   Create  -  60 ms  -  168 calls  -  Obtics.Values.Transformations.PipelineResultTransformation`3.Create(TKey)
                                                               0,05 %   CreateDelegate  -  3 ms  -  168 calls  -  System.Reflection.Emit.DynamicMethod.CreateDelegate(Type, Object)
                                                            ?0,55 %   IIf  -  34 ms  -  168 calls  -  Obtics.Values.ValueProvider.IIf(IValueProvider[Boolean], TType, IValueProvider[TType])
                                                            ?0,35 %   IIf  -  22 ms  -  168 calls  -  Obtics.Values.ValueProvider.IIf(IValueProvider[Boolean], TType, TType)
                                                            ?0,18 %   Create  -  11 ms  -  168 calls  -  Obtics.Values.Transformations.UnarySelectTransformation`2.Create(IValueProvider[TIn], Func[TInTOut])
                                                            ?0,10 %   GeneralCreate  -  6 ms  -  168 calls  -  Obtics.Values.Transformations.CascadeTransformation`2.GeneralCreate(IValueProvider[TItm])
                                                          ?2,77 %   Create  -  174 ms  -  168 calls  -  Obtics.Values.Transformations.PipelineResultTransformation`3.Create(TKey)
                                                           0,07 %   CreateDelegate  -  5 ms  -  168 calls  -  System.Reflection.Emit.DynamicMethod.CreateDelegate(Type, Object)
                                                        ?1,68 %   Create  -  106 ms  -  168 calls  -  Obtics.Values.Transformations.PipelineResultTransformation`3.Create(TKey)
                                                  ?6,23 %   ProtectedGetValue  -  392 ms  -  3?192 calls  -  Obtics.Values.Transformations.UnarySelectTransformation`2.ProtectedGetValue
                                          ?4,87 %   UpdateHaveChangedListeningClients  -  306 ms  -  1?260 calls  -  Obtics.Collections.Transformations.UnorderedNotifyVpcTransformation`2.UpdateHaveChangedListeningClients(Boolean, Boolean)
                              ?5,39 %   UpdateHaveChangedListeningClients  -  338 ms  -  72 calls  -  Obtics.NCSourcedObject`1.UpdateHaveChangedListeningClients(Boolean, Boolean)
Regs,
Vincent

 

Coordinator
Jan 8, 2011 at 8:41 PM

Hi Vincent,

Wish I had a better overview of your project to spot the bottleneck. Your DynamicWorkDay class doesn't seem overly complex. Did you check that no more than necessairy DynamicWorkDay's and Weeks are being created?

How many workday are there in your e.WorkDays collection? It might be worth trying to use e.WorkDays.ToDictionary(wd => wd.Date)[date]

Hope you can solve your problems.

Regs,

Thomas.

 

Coordinator
Jan 20, 2011 at 8:59 AM

Hi Vincent,

Here is a piece of code that may be interesting. It defines 2 'Compile' variations (CompileDelayedObservable). The resulting Observable expressions first calculate the result value using the expression as provided. Only later the whole transformation pipeline will be built.

This may be very usefull when building UI's and I'm getting some positive results. The UI can be drawn quickly and later reactivity is added. Because a small pipeline is still being built it works best with largish expressions. If this idea works I could incorporate it into the core and that would make it even more effective.

Regs,

Thomas

    public static class ExpressionObserverExtensions
    {
        public static Func<P1,IValueProvider<T>> CompileDelayedObservable<P1,T>(Expression<Func<P1,T>> exp, Obtics.Async.IWorkQueue workQueue)
        {
            var directFunc = exp.Compile();
            var observableFunc = ExpressionObserver.Compile(exp);
            var carrousel = 
                Carrousel.Create(
                    (P1 p1) =>
                    {
                        var s = ValueProvider.Dynamic( new { p1, vp = ValueProvider.Static( directFunc(p1) ) } );

                        workQueue.QueueWorkItem(
                            obj => s.Value = new { p1, vp = observableFunc(p1) },
                            null
                        );

                        return s;
                    },
                    o => o.Value.p1
                )
            ;

            return new Func<P1,IValueProvider<T>>( p1 => carrousel(p1).Select(o => o.vp) );
        }

        public static Func<P1, IValueProvider<T>> CompileDelayedObservable<P1, T>(Expression<Func<P1, T>> exp)
        { return CompileDelayedObservable( exp, Obtics.Async.WorkQueue.DefaultWorkQueueProvider.GetWorkQueue()); }

        public static Func<P1, P2, IValueProvider<T>> CompileDelayedObservable<P1, P2, T>(Expression<Func<P1, P2, T>> exp, Obtics.Async.IWorkQueue workQueue)
        {
            var directFunc = exp.Compile();
            var observableFunc = ExpressionObserver.Compile(exp);
            var carrousel =
                Carrousel.Create(
                    (P1 p1, P2 p2) =>
                    {
                        var s = ValueProvider.Dynamic(new { p1, p2, vp = ValueProvider.Static(directFunc(p1, p2)) });

                        workQueue.QueueWorkItem(
                            obj => s.Value = new { p1, p2, vp = observableFunc(p1, p2) },
                            null
                        );

                        return s;
                    },
                    o => o.Value.p1,
                    o => o.Value.p2
                )
            ;

            return new Func<P1, P2, IValueProvider<T>>( (p1, p2) => carrousel(p1, p2).Select(o => o.vp));
        }

        public static Func<P1, P2, IValueProvider<T>> CompileDelayedObservable<P1, P2, T>(Expression<Func<P1, P2, T>> exp)
        { return CompileDelayedObservable(exp, Obtics.Async.WorkQueue.DefaultWorkQueueProvider.GetWorkQueue()); }

    }

 


Jan 22, 2011 at 9:29 AM

Hello Throb,

Thank you for your answers, I was very busy. Regarding my code, I create the least possible DynamicWorkDay (Data Virtualization & IU in my grid). In e.WorkDays, there are not many Workday.

Thank you for these extensions, I try to reproduce this behavior but I gave up. When I use it I get:

System.MethodAccessException was unhandled by user code
  Message=Échec de la tentative d'accès de la méthode 'Obtics.Carrousel.ConstructObservableObject<System.__Canon,Obtics.Tuple`2<System.__Canon,System.__Canon>>(Int32, Obtics.Tuple`2<System.__Canon,System.__Canon>)' à la méthode 'Obtics.Values.Transformations.UnarySelectTransformation`2<System.__Canon,System.__Canon>..ctor()'.
  StackTrace:
       à System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
       à System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
       à System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
       à System.Activator.CreateInstance[T]()
       à Obtics.Carrousel.ConstructObservableObject[TOut,TPrms](Int32 hash, TPrms prms)
       à Obtics.Carrousel.InternalCarrousel`2.Get(TKey key, Func`3 creator)
       à Obtics.Carrousel.Get[TOut,TKey1,TKey2](TKey1 key1, TKey2 key2)
       à Obtics.Values.Transformations.UnarySelectTransformation`2.Create(IValueProvider`1 source, Func`2 converter)
       à Obtics.Values.ValueProvider.Select[TSource,TResult](IValueProvider`1 source, Func`2 valueConverter)
       à Obtics.Values.ValueProvider.Select[TSource,TResult](IValueProvider`1 source, Func`2 valueConverter)
       à Xxxx.Planning.Infrastructure.ExpressionObserverExtensions.<>c__DisplayClass10`3.<CompileDelayedObservable>b__e(P1 p1, P2 p2)
       à Xxxx.Planning.ViewModels.DynamicWorkDay..ctor(Employee employee, DateTime dateTime)
       à Xxxx.Planning.ViewModels.DynamicWorkDay.<.cctor>b__e(Employee employee, DateTime date)
      .... 


I use the version of Obtics for Silverlight 4.0 found here: http://obtics.codeplex.com/Thread/View.aspx?ThreadId=215764

Best regards,
Vincent

Coordinator
Jan 23, 2011 at 4:33 AM

Hi Vincent,

Sorry; I didn't test the code with silverlight and forgot that under silverlight lambda expressions containing anonymous types will not compile. Here is a slightly modified version that I tested with silverlight 4.0.

Regs,

Thomas.

    public static class ExpressionObserverExtensions
    {
        public static Func<P1, IValueProvider<T>> CompileDelayedObservable<P1, T>(Expression<Func<P1, T>> exp, Obtics.Async.IWorkQueue workQueue)
        {
            var directFunc = exp.Compile();
            var observableFunc = ExpressionObserver.Compile(exp);
            var carrousel =
                Carrousel.Create(
                    (P1 p1) =>
                    {
                        var s = ValueProvider.Dynamic(Tuple.Create( p1, ValueProvider.Static(directFunc(p1)) ));

                        workQueue.QueueWorkItem(
                            obj => s.Value =Tuple.Create(p1, observableFunc(p1)),
                            null
                        );

                        return s;
                    },
                    o => o.Value.Item1
                )
            ;

            return new Func<P1, IValueProvider<T>>(p1 => carrousel(p1).Select(o => o.Item2));
        }

        public static Func<P1, IValueProvider<T>> CompileDelayedObservable<P1, T>(Expression<Func<P1, T>> exp)
        { return CompileDelayedObservable(exp, Obtics.Async.WorkQueue.DefaultWorkQueueProvider.GetWorkQueue()); }

        public static Func<P1, P2, IValueProvider<T>> CompileDelayedObservable<P1, P2, T>(Expression<Func<P1, P2, T>> exp, Obtics.Async.IWorkQueue workQueue)
        {
            var directFunc = exp.Compile();
            var observableFunc = ExpressionObserver.Compile(exp);
            var carrousel =
                Carrousel.Create(
                    (P1 p1, P2 p2) =>
                    {
                        var s = ValueProvider.Dynamic( Tuple.Create(p1, p2, ValueProvider.Static(directFunc(p1, p2)) ));

                        workQueue.QueueWorkItem(
                            obj => s.Value = Tuple.Create(p1, p2, observableFunc(p1, p2) ),
                            null
                        );

                        return s;
                    },
                    o => o.Value.Item1,
                    o => o.Value.Item2
                )
            ;

            return new Func<P1, P2, IValueProvider<T>>((p1, p2) => carrousel(p1, p2).Select(o => o.Item3));
        }

        public static Func<P1, P2, IValueProvider<T>> CompileDelayedObservable<P1, P2, T>(Expression<Func<P1, P2, T>> exp)
        { return CompileDelayedObservable(exp, Obtics.Async.WorkQueue.DefaultWorkQueueProvider.GetWorkQueue()); }

    }

 

Feb 14, 2011 at 8:32 AM

Hi Throb,

Sorry for the response time, i am very busy theses days.
My application is now in production, a second release coming soon, I test CompileDelayedObservable so at that time.

I would like to update Obtics in my second release by cons, I can not find stable version,when it comes next?

Vincent 

Coordinator
Feb 15, 2011 at 8:40 AM

Hi Vincent,

No probs, bussy here too. Progress on Obtics is slow at the moment. The code base is hughe and I have little time. Working on it in small steps. I think the project needs extra hands to get to any form of new release soon.

Regs,

Thomas.

Mar 3, 2011 at 3:16 PM

Hi Throb,

I try the CompileDelayedObservable but i have a question. Why use UIThread to create the pipeline?

Another question, is there an extension method for the buffered CollectionChanged?

For example, if there are 100 items added all at once. Instead of triggering the CollectionChanged in a single loop, it is better than CollectionChanged event is fired 100 times at regular intervals of 20ms, for example?
This prevents the UI from freezing.

Sorry for my english.

Vincent

Coordinator
Mar 7, 2011 at 11:30 PM

Hi Vincent,

No reason to use the UI thread as far as Obtics is concerned. I used the UI thread as default because it is safer. You can use any other thread. I guess you will need to make sure you switch back to the UI thread when you bind to your view elements (use the Async() methods).

Also; ObservableEnumerable.Async() is the extension method that uses the BufferTransformation. Allows you to specify a 'drop off' percentage. Say if that is 10% then if number of events in queue compared to the number of elements in the collection is more than 1/10 then the buffertransformation will default to a 'Reset' event.

Obtics at the moment only supports single item changes. This is because multi item changes become very complicated with some transformations. May introduce it again though in the future.

Regs,

Thomas.

Mar 8, 2011 at 8:57 AM

Thank you for your reply.

Yes, the problem is not that Obtics manages a single item at a time, my question is:

Is there a transformation that forces between each triggering event CollectionChanged he spends at least 20 ms? To not freeze the UI.

Vincent

Coordinator
Mar 8, 2011 at 10:44 AM

What you could do is implement your own IWorkQueue. Obtics already provides 2. One for doing jobs using the default Dispatcher (WPF and Silverlight) and one for doing jobs on the ThreadPool.

You could create one that processes jobs on a regular interval (every 20ms). Then you use this one when you call Async(IWorkQueue queue) just before you bind your UI elements.

Lowerdown in the transformation pipeline it may be more benificial to use the ThreadPool work queue. So roughly it would look like:

YourObservableCollection.Async( WorkQueueOnThreadPoolAdapter.Get() ).. do your complicated stuff .. .Async( your20MsWorkQueue )

Or:

static Func<In,IValueProvider<Out>> _f = CompileDelayedObservable<In, Out>( in => ..the expression.. , WorkQueueOnThreadPoolAdapter.Get());

IValueProvider<Out> F(In in) { return _f(in).Async( your20MsWorkQueue ); }

Regs,

Thomas