Steema Issues Database

Note: This database is for bugs and wishes only. For technical support help, if you are a customer please visit our online forums;
otherwise you can use StackOverflow.
Before using this bug-tracker we recommend a look at this document, Steema Bug Fixing Policy.



Bug 2539

Summary: ColorRange is inefficient
Product: .NET TeeChart Reporter: Fraser Ross <FRASER.ROSS8>
Component: SeriesAssignee: Steema Issue Manager <issuemanager>
Status: CONFIRMED ---    
Severity: major CC: chris, yeray
Priority: ---    
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Windows   
Chart Series: --- Delphi / C++ Builder RAD IDE Version:

Description Fraser Ross 2022-07-24 08:17:05 EDT
I have known about this problem for many years.  I did think it had been reported.  I remember being shown the functions implementation somehow.  Here it is taken from a forum post in 2010:

        private void ColorRange(Steema.TeeChart.Styles.Series ASeries, Steema.TeeChart.Styles.ValueList AValueList, double FromValue, double ToValue, Color AColor)
        {
            double tmpValue;
            for (int t = 0; t < AValueList.Count; t++)
            {
                tmpValue = AValueList.Value[t];
                if ((tmpValue >= FromValue) && (tmpValue <= ToValue) && (!ASeries.IsNull(t)))
                    ASeries.Colors[t] = AColor;
            }
            tChart1.Refresh();
        }

My alternative to using is like this:

	auto const xEnd= range.End();
	for (auto x= range.Start(); xEnd != x; ++x)
		pulseLengthsSeries->ValueColor[x]= TColor(colour);

The constant xEnd is thought to significantly optimise speed.
Comment 1 yeray alonso 2022-09-23 07:08:21 EDT
Sounds as the code posted here:

https://www.steema.com/support/viewtopic.php?f=4&t=10655&p=44710&hilit=ColorRange#p44710

I'm moving this to TeeChart .NET
Comment 2 christopher ireland 2022-09-23 08:12:06 EDT
It's an interesting issue. I've created three classes which we can copy/paste into https://sharplab.io and decompile to CIL:

1. the code as-is:
*************
using System.Collections.Generic;
using System.Drawing;

public class C
{
  List<Color> Colors = new List<Color>();

  public void ColorRange(List<double> AValueList, double FromValue, double ToValue, Color AColor)
  {
    double tmpValue;
    for (var t = 0; t < AValueList.Count; t++)
    {
      tmpValue = AValueList[t];
      if ((tmpValue >= FromValue) && (tmpValue <= ToValue))
      {
        Colors[t] = AColor;
      }
    }
  }
}
*************

2. the code in which we don't query Count on every iteration:
*************
using System.Collections.Generic;
using System.Drawing;

public class D
{
  List<Color> Colors = new List<Color>();

  public void ColorRange(List<double> AValueList, double FromValue, double ToValue, Color AColor)
  {
    double tmpValue;
    int xEnd = AValueList.Count;
    for (var t = 0; t < xEnd; t++)
    {
      tmpValue = AValueList[t];
      if ((tmpValue >= FromValue) && (tmpValue <= ToValue))
      {
        Colors[t] = AColor;
      }
    }
  }
}
*************

3. And a LINQ one-liner:
*************
using System.Collections.Generic;
using System.Drawing;
using System.Linq;


public class E
{
  List<Color> Colors = new List<Color>();

  public void ColorRange(List<double> AValueList, double FromValue, double ToValue, Color AColor)
  {
    Colors = AValueList.Select((x, i) => (x <= ToValue && x <= FromValue) ? AColor : Colors[i]).ToList();
  }
}
*************

If we compare the three CIL ouputs in a diff tool, we can see that:
a) version 3) has the shortest CIL for the method, but the longest CIL for the class
b) version 1) has the shortest CIL for the class
c) both version 1) and version 2) have three callvirt calls, but 1) has all three in the loop
d) version 3) has only one callvirt call