Передача большого количества переменных между потоками для эффективного ведения журнала - PullRequest
1 голос
/ 19 октября 2011

Я использую VS 2005

У меня есть около 10-12 переменных разных типов, таких как double, int, string, long и bool, которые я хочу войти в другой поток с мин.накладные расходы.

В настоящее время каждый раз, когда мне приходится регистрироваться, я «создаю» массив, добавляю в него все переменные и затем передаю его в качестве аргумента LoggerClass.Log ().В LoggerClass он устанавливает / сбрасывает событие, и другой поток считывает значение arraylist.

Поскольку функция часто вызывается, выполнение «new» не очень эффективно, и я ищу способ передать всезначения в другой поток без динамического выделения памяти.

Одна альтернатива, на мой взгляд, состоит в том, чтобы не «создавать» новый массив, а просто продолжать добавлять его, но проблема в том, что размер массива может стать огромным.*

Есть предложения?

Вот код

//This function gets called a thousand times per second

private void MyLogic(MyClass LogObj)
{

  ArrayList MyArrList = new ArrayList(15);//This line causes periodic performance outliers
                            MyArrList.Add("Q");
                            MyArrList.Add(LogObj.valString);
                            MyArrList.Add(LogObj.ValDouble);
                            MyArrList.Add(LogObj.ValInt);
                            MyArrList.Add(LogObj.valString2);
                            MyArrList.Add(LogObj.ValDouble2);
                            MyArrList.Add(LogObj.ValInt2);
                            MyArrList.Add(LogObj.valString3);
                            MyArrList.Add(LogObj.ValDouble3);
                            MyArrList.Add(LogObj.ValInt3);
                            MyArrList.Add(LogObj.valString4);
                            MyArrList.Add(LogObj.ValDouble4);
                            MyArrList.Add(LogObj.ValInt4);

  MyLogger.Log(MyArrList);
}


Class MyLogger
{
  private Queue         m_logQueue;

  public void Log(ArrayList LogArr) 
  {
    lock (m_LogQueue)
    {
      m_LogQueue.Enqueue(LogArr);
    }

    m_Event.Set();
  }
}

private void LogThread()
{           
  ArrayList ValuesToLog = new ArrayList(); 
  StringBuilder StringBuf = new StringBuilder(); 
  string csFileName = "MyLog";

  using (StreamWriter sw = new StreamWriter(new FileStream(csFileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)))
  {
    while (m_bRun)
    {
      m_Event.WaitOne();
      try 
      {
        while (true)
        {
          lock (m_logQueue)
          {
            if (m_logQueue.Count > 0)
            {
              ValuesToLog = (ArrayList)m_logQueue.Dequeue();                                     
            }
            else
              break;
          }

          StringBuf = new StringBuilder();
          StringBuf.Append(DateTime.Now);
          StringBuf.Append(",");

          for (int i = 0; i < (ValuesToLog.Count - 1); i++)
          {
            StringBuf.Append(",");
            StringBuf.Append(ValuesToLog[i]);
          }

          sw.WriteLine(StringBuf.ToString());

          if (m_bAbort) break;
          if (m_EventStop.WaitOne(0, true))
            break;
        }
        sw.Flush();
      }
      catch(Exception e)
      {

      }
    }
  }
}

Ответы [ 3 ]

0 голосов
/ 19 октября 2011

Я не делал никаких измерений, и я не обязательно согласен с вашими выводами или вашим подходом к этому типу лесозаготовок, но я не позволю этому помешать мне сделать предложение ...

//Use this to hold the information that you want to log.
public class LogRecord
{
  private IEnumerable<object> values;

  public LogRecord(params object [] data)
  {
    LogTime = DateTime.Now;
    values = data;
  }

  public DateTime LogTime { get; set; }
  public IEnumerable<object> Values 
  { 
    get
    {
      yield return LogTime;
      foreach(var v in values) yield return v;
    }
  }
}

//
private void MyLogic(MyClass LogObj)
{
  var record = new LogRecord("Q", LogObj.Var1, LogObj.Var2, LogObj.Var3);
  MyLogger.Log(record);
}

class MyLogger
{
  public void Log(LogRecord record)
  {
    //Do a bunch of threading stuff...

    string msg = string.Join(",", record.Values);

    //Write the message to the stream
  }
}

Там все еще есть new, но этот подход (создание новой записи в журнале для каждого оператора ведения журнала) используется другими популярными средами ведения журнала, такими как log4net, NLog и EnterpriseLibrary.Насколько я знаю, вы не слышите много жалоб на производительность в этих системах.

Я не знаю, лучше это или хуже, чем у вас.Кроме того, я не знаю, решает ли это проблему с производительностью, о которой вы говорили, что видели.С другой стороны, он немного упрощает код, как для создания сообщения регистрации, так и для объединения его в строку для записи в поток.

Если вы чрезвычайно заинтересованы в производительности и если ваша информациярегистрируется тысячу раз в секунду, вы можете посмотреть на реализацию CurrentTimeGetter в NLog.Его целью является оптимизация поиска по времени, учитывая, что время часто не меняется от одного зарегистрированного сообщения к следующему.Вы можете найти его по этой ссылке:

https://github.com/jkowalski/NLog/blob/master/src/NLog/Internal/CurrentTimeGetter.cs

Моя окончательная рекомендация, вероятно, будет заключаться в том, чтобы попытаться использовать существующую структуру ведения журналов, такую ​​как log4net или NLog.В эти дни я лично рекомендовал бы сначала попробовать NLog, поскольку он только что выпустил новую версию.

0 голосов
/ 19 октября 2011

DateTime.Now.ToString () может быть проблемой.Возможно, рассмотрите возможность использования чего-то еще?Это дало мне прирост производительности -

char[] buffer = new char[21];

static string DateTimeNowToString()     
{

    var now = DateTime.Now;
    buffer[2] = buffer[5] = '.';         
    buffer[8] = ' ';         
    buffer[11] = buffer[14] = buffer[17] = ':';         
    Write2(buffer, now.Month, 0);         
    Write2(buffer, now.Day, 3);         
    Write2(buffer, now.Year % 100, 6);         
    Write2(buffer, now.Hour, 9);         
    Write2(buffer, now.Minute, 12);         
    Write2(buffer, now.Second, 15);         
    Write3(buffer, now.Millisecond, 18);

    return new String(buffer)
}     

static void Write2(char[] buffer, int value, int offset)     
{         
    buffer[offset++] = (char)('0' + (value / 10));         
    buffer[offset] = (char)('0' + (value % 10));     
}

static void Write3(char[] buffer, int value, int offset)     
{        
    buffer[offset++] = (char)('0' + (value / 100));         
    buffer[offset++] = (char)('0' + ((value / 10) % 10));         
    buffer[offset] = (char)('0' + (value % 10));     
} 
0 голосов
/ 19 октября 2011

Если вы действительно уверены, что проблема заключается в выделении нового списка каждый раз, вы можете перезапустить их.

Иметь коллекцию неиспользуемых списков, которая начинается пусто.Когда в коллекции есть список, возьмите его (и удалите из коллекции).В противном случае создайте новый.Когда вы закончите регистрацию, Clear() список и верните его в коллекцию.

Конечно, вы получаете доступ к коллекции из нескольких потоков, поэтому вы должны убедиться, что вы получаете к ней доступ в многопоточном режиме..

...