воскресенье, 24 октября 2010 г.

Perfomance Counter. Счетчик производительности

Когда мы хотим измерить производительность кода, нас интересует время, потраченое на его выполнение. Для этого можно "запомнить" время ДО, и время ПОСЛЕ, вычислить разницу и анализировать результат. В этом случае считается, что процессор все время будет выполнять только этот код. Но на практике может получится так, что во время выполения поток дойдет до середины, процессор переключится на другие потоки, обойдет их, и через некоторое время вернется к нашему. Естественно измерения в этом случае некорректны.

С учетом вышеизложенного, для точных расчетов я использую следующий класс (сперто где-то с просторов интернета):

  class PerfCounter
  {
    private static TimeSpan spStartKernelTime;
    private static TimeSpan spStartUserTime;
    private static ProcessThread procThread;
    private static Int32 idCurrentThread;

    [DllImport("kernel32.Dll", SetLastError = true)]
    public static extern UInt32 GetCurrentThreadId();
    public static bool StartCountOnThread()
    {
      idCurrentThread = (Int32)GetCurrentThreadId();
      try
      {
        foreach (ProcessThread prThread in Process.GetCurrentProcess().Threads)
        {
          if (prThread.Id == idCurrentThread)
          {
            procThread = prThread;
            spStartKernelTime = prThread.TotalProcessorTime;
            spStartUserTime = prThread.UserProcessorTime;
            return true;
          }
        }
      }
      catch (System.Exception e)
      {
        System.Console.WriteLine(e.Message);
        //System.Windows.Forms.MessageBox.Show(e.Message);
        return false;
      }
      return false;
    }

    public static TimeSpan FinishCountOnThread()
    {
      try
      {
        foreach (ProcessThread prThread in Process.GetCurrentProcess().Threads)
        {
          if (prThread.Id == idCurrentThread)
          {
            return prThread.TotalProcessorTime +
             prThread.UserProcessorTime -
             spStartKernelTime - spStartUserTime;
          }
        }
      }
      catch (System.Exception e)
      {
        //System.Windows.Forms.MessageBox.Show(e.Message);
        System.Console.WriteLine(e.Message);
        return TimeSpan.Zero;
      }
      return TimeSpan.Zero; ;

    }
  }


* This source code was highlighted with Source Code Highlighter.

воскресенье, 17 октября 2010 г.

Ранкинг (Ranking). SQL запросы с рангом (Rank).

Ранкинг (Ranking) - это предоставление информации о соответствии найденной записи определенному запросу. Это соответствие в простейшем случае можно отобразить в процентном соостветсвии. Т.е. записи удовлетворяющие всем критериям имеют 100%-ный ранкинг.

Реализация ранкинга может показаться достаточно сложной задачей. Но это только так кажется. SQL может взять эту задачу на себя! Для примера я использовал MySQL, реально использовал MSSQL.

Итак, таблица:
CREATE TABLE `test_db`.`person` (
`id` INT NOT NULL,
`name` VARCHAR (100),
`age` int ,
PRIMARY KEY (`id`)
)

Наполним данными:
INSERT INTO test_db.person VALUES (1,'Ralf', 20),(2,'Peter', 30),(3,'Patrick', 40);

Найдем записи, которые соответсвуют одному из трех критериев:
- все старше 25;
- все младше 35;
- имена начинаются на "P";

Запрос может выглядеть так:
SELECT p.id, p.name, p.age FROM test_db.person p
WHERE age > 25 OR age < 35 OR name Like 'P%'


А результат запроса, так:
id, name, age
------------
1,'Ralf', 20
2,'Peter', 30
3,'Patrick',40

Добавим ранкинг (ranking). Объяснять не буду, все очень просто:
SELECT p.id, p.name, p.age, (100/3)*under1.hit + (100/3)*under2.hit + (100/3)*under3.hit AS rank FROM test_db.person p
LEFT JOIN (Select id, 1 As hit from test_db.person WHERE age > 25) AS under1 On under1.id = p.id
LEFT JOIN (Select id, 1 As hit from test_db.person WHERE age < 35) AS under2 On under2.id = p.id LEFT JOIN (Select id, 1 As hit from test_db.person WHERE name like 'p%') AS under3 On under3.id = p.id WHERE age > 25 OR age < 35 OR name like 'p%'


Результат:
id, name, age, rank
------------
1,'Ralf', 20, null
2,'Peter', 30, null
3,'Patrick', 40, null

Почти то, что нужно, но есть проблема с приведением арфиметического результа к null:
Для этого в MySql можно использовать функцию IFNULL (в MSSQL это функция ISNULL):
SELECT p.id, p.name, p.age, (100/3)*IFNULL(under1.hit, 0) + (100/3)*IFNULL(under2.hit,0) + (100/3)*IFNULL(under3.hit,0) AS rank FROM test_db.person p
LEFT JOIN (Select id, 1 As hit from test_db.person WHERE age > 25) AS under1 On under1.id = p.id
LEFT JOIN (Select id, 1 As hit from test_db.person WHERE age < 35) AS under2 On under2.id = p.id LEFT JOIN (Select id, 1 As hit from test_db.person WHERE name like 'p%') AS under3 On under3.id = p.id WHERE age > 25 OR age < 35 OR name like 'p%'


Результат:
id, name, age, rank
------------
1,'Ralf', 20, 33.333
2,'Peter', 30, 100.000
3,'Patrick', 40, 66.666

суббота, 16 октября 2010 г.

Запуск дополнительного X-Server.

Обычно Linux десктоп дистрибутив запускает одну инстанцию X-Server, которая работает в "7"-ом терминале (Alt+Ctrl+F7). Эта инстанция работает с первым X-дисплеем (X-Display). Чтобы запустить программу в другой инстанции нужно:
  • выбрать нужный терминал (Alt+Ctrl+Fx)
  • войти в терминал под своим именем паролем
  • $ xinit _prg_ -- :1 (здесь :1 - номер следующего свободного X-дисплея, нумерация с нуля)

Примеры:
$ xinit gedit -- :1
$ xinit nexuiz -- :1

Замечания:
  • Так можно запускать игры или "проблемные" программки, которые приводят с сбою графического сервера;
  • Окна программ не имеют атрибутов вашего рабочего окружения (GNOME, KDE);
  • Закрытие зависших программ - Ctrl+C
  • Информация должна быть действительна во многих Linux-дистрибутивах, проверено на Ubuntu 10.10 (64bit)

среда, 6 октября 2010 г.

XDocument. Работа с элементами без явного указания пространства имен по умолчанию (Default Namespace)

Если вы используете XDocument, Xml которого содержит явное пространство имен по умолчанию, то во время разбора элементов придется его явно указывать. Для ясности кусочки кода:
<Data xmlns="http://selo-blog.blogspot.com/">
 <Name>Иван</Name>
 <Gender>мужской</Gender>
</Data>

Чтобы получить значение тега "Name", придется выполнить следующий код:
XDocument xDoc = XDocument.Parse(inputString);
XNamespace xSpace = "http://selo-blog.blogspot.com/";
String name = xDoc.Root.Element(xSpace + "Name").Value;

Немного раздражительно, не правда ли?

Для облегчения страданий я создал следующие методы-расширения
public static class Extensions {
  public static XElement xElement(this XElement xParent, String name) {
    return xParent.Element(xParent.Name.Namespace + name);
  }

  public static IEnumerable<XElement> xElements(this XElement xParent, String name) {
    return xParent.Elements(xParent.Name.Namespace + name);
  }
}

Теперь работа с документом происходит обычным способом:
String name = xDoc.Root.xElement("Name").Value;

* This source code was highlighted with Source Code Highlighter.