Продуктивність. Net міф чи фантастика? (Исходники), Різне, Програмування, статті

Для перетворення символів у нижній регістр ми використовуємо патерн «Інтерпретатор», реалізований як Сінглтон і створюваний за допомогою фабрики класів »- такі фрази нам доводиться чути на семінарах для програмістів. А що буде, якщо порівняти цю фразу от з такою: «Для перетворення символів у нижній регістр необхідно відняти дельту між началами реєстрових алфавітів.


Філософія


Що, на вашу думку, буде працювати швидше? На жаль, більшість сучасних програмістів, які виросли на Visual Studio 2003 і старше, все рідше ставлять собі те питання. Вони вважають, що сучасні процесори мають настільки велику тактову частоту, а в комп’ютерів є так багато пам’яті, що більше немає потреби перейматися продуктивності в процесі написання програми. Більш того, багато програмістів свідомо використовують мовні конструкції, які працюють повільніше за інших, але, на їхню думку, є більш зручними у використанні. Мета цієї статті – розповісти про те, що будь зручність програмування відбувається за рахунок продуктивності програми. На мій погляд, всі сучасні розробники ПЗ діляться на дві великі групи. Перша група програмістів вміє вигадувати алгоритми, а друга не вміє (Зате володіє технікою використання чужих). Ця ситуація підтримується Microsoft, яка випускає все більш і більш високорівневі класи на всі випадки життя. У результаті програмісту вже не потрібно вміти створювати – йому потрібно вміти використовувати. Як приклад розглянемо так звану концепцію трирівневої бізнес-моделі. Перший рівень – бази даних. Все, що потрібно вміти – це читати і записувати дані в БД. Далі йде рівень класів. Тут програмісту потрібно копіювати прочитані дані в класи, які складають логіку програми. Третім йде рівень користувальницького інтерфейсу (GUI), тут розробнику потрібно тільки копіювати дані з класів в компоненти, щоб користувач міг з ними взаємодіяти. По суті, такий підхід до розробки софта найбільше нагадує копіювання файлів з одних папок в інші, і таким програмістом може стати будь-який просунутий користувач. До речі кажучи, з виходом Visual Studio 2005, трирівнева модель спростилася до однорівневої. Тепер програміст може з допомогою миші вказати в DataView запит до бази даних, а все інше за негосделает контрол. Все було б добре, але якщо мова йде про ASP.NET, то такий спрощений підхід призводить до значних втрат продуктивності. Наприклад, повільне завантаження сторінки через невиправдано великої кількості запитів до БД або (а часто і) великої ваги сторінки. На щастя, у цієї тенденції розвитку галузі є істотний недолік. Чим більше високорівневими стають конструкції, тим більше звужується сфера їх застосування. У цій статті ми розглянемо декілька прикладів, які демонструють ситуацію.


Перерахування


Нещодавно на моєму улюбленому форумі з програмування я побачив повідомлення від початківця програміста на тему: «C # і перерахування». Справа в тому, що запитувач не знав, як виділити певний бінарний прапор з числа. Нагадаю, що прапори в числі є порядковий біт в бінарному представленні числа. Наприклад, число 9 (в бінарному представленні 1001), позначає, що встановлено нульовий і третій прапор. Питання закономірний для початківця розробника, але яке було моє здивування, коли я побачив відповіді від найавторитетніших програмістів, aвтор статей, і володаря MVP! Розглянемо дві відповіді:






        public enum TaskStatus
{
Terminated = 1,
Stoped = 2,
Idle = 4,
Working = 8,
Stopping = 16
}

// #1
static bool StatusIn1(TaskStatus enm, params TaskStatus[] enms)
{
foreach (TaskStatus tEnum in enms)
{
if (enm != tEnum) return true;
}
return false;
}
//#2
static bool StatusIn2(TaskStatus enm, params TaskStatus[] enms)
{
return (Array.IndexOf(enms, enm) != -1);
}


Що ж тут можна сказати? Мені становітсяне по собі від спроби представити швидкість програм, які пишуть ці фахівці, перетворюючи атомарні операції в цикли. Між іншим, друга відповідь (від MVP) навіть МЕНШ продуктивний не дивлячись на уявну простоту, оскільки IndexOf викликає ще одну функцію, де в циклі ведеться пошук необхідного значення. Відповідь на дане питання має бути, безумовно, таким:






         TaskStatus s = TaskStatus.Stoped / TaskStatus.Terminated;
bool f = (TaskStatus.Stoped & s) == TaskStatus.Stoped;

Не будемо голослівними і подивимося на результат тесту.






using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace test1
{
class Program
{
static void Main(string[] args)
{
int count = 10000000;
TaskStatus s = TaskStatus.Stoped / TaskStatus.Terminated;
Stopwatch sw = new Stopwatch();
// #1
{
sw.Start();
for (int i = 0; i < count; ++i)
{
bool f = StatusIn1(TaskStatus.Stoped, s);
}
sw.Stop();
Console.WriteLine(“#1 – ” + sw.ElapsedMilliseconds);
}
// #2
{
sw.Reset(); sw.Start();
for (int i = 0; i < count; ++i)
{
bool f = StatusIn2(TaskStatus.Stoped, s);
}
sw.Stop();
Console.WriteLine(“#2 – ” + sw.ElapsedMilliseconds);
}
// #3
{
sw.Reset(); sw.Start();
for (int i = 0; i < count; ++i)
{
bool f = (TaskStatus.Stoped & s) == TaskStatus.Stoped;
}
sw.Stop();
Console.WriteLine(“#3 – ” + sw.ElapsedMilliseconds);
}
Console.ReadLine();
}
static bool StatusIn1(TaskStatus enm, params TaskStatus[] enms)
{
foreach (TaskStatus tEnum in enms)
{
if (enm != tEnum) return true;
}
return false;
}
static bool StatusIn2(TaskStatus enm, params TaskStatus[] enms)
{
return (Array.IndexOf(enms, enm) != -1);
}
}
public enum TaskStatus
{
Terminated = 1,
Stoped = 2,
Idle = 4,
Working = 8,
Stopping = 16
}
}


  1. 439
  2. 378
  3. 8

Як бачите, різниця в продуктивності між «авторитетними» способами і звичайним складає більше одного порядку в першому випадку і більше двох порядків у другому.


Цикли


Розглянемо два види циклів: for і forearch. Спочатку зробимо два тести, що б потім обговорити результати. Тест швидкості перебору:






using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace test4
{
class Program
{
static void Main(string[] args)
{
int count = 10000000;
Stopwatch sw = new Stopwatch();
list = new List<int>(count);
for (int i = 0; i < count; ++i)
{
list.Add(i);
}
sw.Start();
t_for();
sw.Stop();
Console.WriteLine(“for – ” + sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
t_foreach();
sw.Stop();
Console.WriteLine(“foreach – ” + sw.ElapsedMilliseconds);
Console.ReadLine();
}
static List<int> list;
static void t_for()
{
for (int i = 0; i < list.Count; ++i)
{
int a = list[i] + 1;
}
}
static void t_foreach()
{
foreach (int var in list)
{
int a = var + 1;
}
}
}
}


  1. for – 49
  2. foreach – 118

Тепер трохи змінимо тест так, щоб визначити, скільки потрібно ресурсів для початку кожного з циклів. Перед запуском тесту рекомендую відкрити диспетчер задачний вкладці Perfomance.






using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace test4
{
class Program
{
static void Main(string[] args)
{
int count = 10000000;
Stopwatch sw = new Stopwatch();
list = new List<int>();
list.Add(1);
sw.Start();

for (int i = 0; i < count; ++i)
{
t_for();
}
sw.Stop();
Console.WriteLine(“for – ” + sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < count; ++i)
{
t_foreach();
}
sw.Stop();
Console.WriteLine(“foreach – ” + sw.ElapsedMilliseconds);
Console.ReadLine();
}
static List<int> list;
static void t_for()
{
for (int i = 0; i < list.Count; ++i)
{
int a = list[i] + 1;
}
}
static void t_foreach()
{
foreach (int var in list)
{
int a = var + 1;
}
}
}
}



  1. for – 156
  2. foreach – 1246

Зробимо невеликі висновки. По-перше, швидкість перерахування foreach вдвічі нижче, ніж for, а по-друге, для старту foreach потрібно майже в десять разів більше часу. Крім того, в диспетчері завдань ясно видно, що витрачається 40 мегабайт пам’яті (диспетчер задач це не найточніше засіб для вимірювання кількості необхідної пам’яті, але для виявлення слона мікроскоп і не потрібний). Чому ж так відбувається? У випадку з циклом foreach перед роботою створюється спеціальний клас-перечіслітель, саме на його створення і витрачається стільки пам’яті. У випадку з циклом for пам’ять не витрачається зовсім. Уважний читач, може сказати: «Подумаєш, проблема висмоктана з пальця, оскільки цей тест на десять мільйонів ітерацій, а в реальній програмі так не буває». Насправді, бувають ситуації, коли програма робить часті виклики функції, яка щось робить в невеликих циклах (від однієї до ста ітерацій). Таким чином, ці десять мільйонів викликів будуть досягнуті вже через двадцять хвилин … а якщо програма працює чотири години? Тим не менш, у світі існує безліч програмістів, які вважають, що такі витрати виправдані (надуманим) зручністю використання foreach. Я ж пропоную читачеві самому переконатися, що синтаксис практично ідентичний:






            List<int> list = new List<int>();
for (int i = 0; i < list.Count; ++i)
{
int a = list[i] + 1;
}
foreach (int var in list)
{
int a = var + 1;
}

На цьому недоліки foreach не закінчуються, існують ще проблеми архітектурного характеру. Припустимо, я роблю якийсь клас, який містить лише ітератор послідовного доступу (ітератор необхідний для роботи циклів foreach). Інший програміст, використовуючи мій клас, може отримати деякі дані тільки послідовно. Але що якщо йому потрібен доступ в зворотному порядку? Він буде просити мене зробити ще один ітератор зворотного доступу. Сам він це зробити не зможе або тому, що йому довго доведеться вникати в логіку класу, або тому, що йому недоступний код. Добре, я зроблю йому … але іншого програмісту може знадобитися доступ через один елемент, через два чи через три елементи … і що тепер, мені потрібно для кожного робити ці ітератори? Інша справа індексатор, який використовується в циклах for, перевага його в тому, що користувач індексатора сам визначить, в якому вигляді йому отримати дані. У цьому полягає велика гнучкість, що ще раз підтверджує мої слова про те, що чим більше Високорівнева конструкція, тим менше її область застосування.


Об’єктно-орієнтоване програмування


Як відомо, спочатку мови програмування були структурного типу і відмінно підходили для написання маленьких програм. Їх недолік проявився, коли з’явилася необхідність написання великих програм. Проблема в тому, що структурний мову абсолютно не передбачає будь-яких стандартів програмування, тому в результаті роботи декількох програмістів над проектом, в коді виходив бардак, і розвивати таку програму було надзвичайно складно. Більш того, пошук і виявлення навіть найпростішої логічної помилки був досить трудомістким заняттям. Об’єктно-орієнтоване програмування (ООП) прийшло на зміну структурноориентированному, принісши тим самим деякий зручність, а отже, і втрату продуктивності. Повірте, я зовсім не противник ООП, але як казав Будда: «Якщо струну смикнути занадто сильно, тоді вона порветься, якщо занадто слабо, тоді ніхто не почує звуку, тому важливо знати золоту середину ». Одного разу мені довелося бачити код, який містить величезний допоміжний клас, і якщо потрібно викликати з нього яку-небудь функцію, автор програми завжди створював його екземпляр. Уявіть, яке це нераціональне використання пам’яті »В C # є можливість створювати статичні методи, вона залишилося у спадок від структурного минулого мови C. Саме такі методи ідеально підходять для допоміжних функцій, так як при їх виклик немає необхідності створювати класи. У практиці зустрічаються схожі завдання, для вирішення яких програмістам потрібно писати аналогічний код, а якщо цей код вже написаний – потрібно його просто скопіювати і трохи підігнати під поточну задачу. Але одного разу програмістам стало лінь копіювати, і вони придумали технологію спадкування в рамках ООП.






using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
namespace test2
{
class Program
{
static void Main(string[] args)
{
int count = 100000000;
Stopwatch sw = new Stopwatch();
sw.Start();
// class – 188 ; 1976
A obj1 = new A();
for (int i = 0; i < count; ++i)
{
bool f = obj1.Proc1();
}
sw.Stop();
Console.WriteLine(“class – ” + sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
// interface 215 ; 1889
B obj2 = new Bi() as B;
for (int i = 0; i < count; ++i)
{
bool f = obj2.Proc1();
}
sw.Stop();
Console.WriteLine(“interface – ” + sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
//abstract – 232 ; 2126
Ci obj3 = new Ci();
for (int i = 0; i < count; ++i)
{
bool f = obj3.Proc1();
}
sw.Stop();
Console.WriteLine(“abstract – ” + sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
// virtual – 237 ; 2140
Di obj4 = new Di();
for (int i = 0; i < count; ++i)
{
bool f = obj4.Proc1();
}
sw.Stop();
Console.WriteLine(“virtual – ” + sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
// static – 175 ; 1897
for (int i = 0; i < count; ++i)
{
bool f = Proc1();
}
sw.Stop();
Console.WriteLine(“static – ” + sw.ElapsedMilliseconds);
Console.ReadLine();
}
public static bool Proc1()
{
return true;
}
}
class A
{
public bool Proc1()
{
return true;
}
}
interface B
{
bool Proc1();
}
abstract class C
{
abstract public bool Proc1();
}
class D
{
virtual public bool Proc1()
{
return true;
}
}
class Bi : B
{
public bool Proc1()
{
return true;
}
}
class Ci : C
{
public override bool Proc1()
{
return true;
}
}
class Di : D
{
}

}



Власне, в рамках даної статті у мене немає можливості опублікувати повномасштабний аналіз продуктивності ООП, оскільки вийде трактат на цілу книгу. Головне – звернути увагу на те, що будь зручність треба використовувати розумно!.


Файлові операції Багатьом розробникам здається зручним мати можливість зберігати і завантажувати класи, використовуючи сериализацию. Зручність полягає в тому, що у функцію збереження потрібно передати посилання на клас, при цьому зберігаються всі public властивості і поля класу. А головне – більше нічого не нуж-но робити. Для цього існують три класи:



Щоб оцінити продуктивність такого підходу, ми визначимо функцію manual, яка буде зберігати поля класу вручну в бінарному форматі.






using System;
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Diagnostics;
namespace test6
{
class Program
{
static void Main(string[] args)
{
int count = 10000;
Stopwatch sw = new Stopwatch();
string file = “”;
// XmlSerializer
{
file = “XmlSerializer.test”;
FileStream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
XmlSerializer xml = new XmlSerializer(typeof(IOTest));
IOTest obj = new IOTest();
sw.Start();
for (int i = 0; i < count; ++i)
{
xml.Serialize(fs, obj);
}
sw.Stop();

fs.Close();
FileInfo fi = new FileInfo(file);
Console.WriteLine(“XmlSerializer – ” + sw.ElapsedMilliseconds + “; FileSize – ” + fi.Length);
}
// BinaryFormatter
{
file = “BinaryFormatter.test”;
FileStream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
BinaryFormatter bf = new BinaryFormatter();
IOTest obj = new IOTest();
sw.Reset(); sw.Start();
for (int i = 0; i < count; ++i)
{
bf.Serialize(fs, obj);
}
sw.Stop();
fs.Close();
FileInfo fi = new FileInfo(file);
Console.WriteLine(“BinaryFormatter – ” + sw.ElapsedMilliseconds + “; FileSize – ” + fi.Length);
}
// SoapFormatter
{
file = “SoapFormatter.test”;
FileStream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
SoapFormatter sf = new SoapFormatter();
IOTest obj = new IOTest();
sw.Reset(); sw.Start();
for (int i = 0; i < count; ++i)
{
sf.Serialize(fs, obj);
}
sw.Stop();
fs.Close();
FileInfo fi = new FileInfo(file);
Console.WriteLine(“SoapFormatter – ” + sw.ElapsedMilliseconds + “; FileSize – ” + fi.Length);
}
// Manual
{
file = “Manual.test”;
FileStream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
IOTest obj = new IOTest();
sw.Reset(); sw.Start();
for (int i = 0; i < count; ++i)
{
obj.Save(bw);
}
sw.Stop();
fs.Close();
FileInfo fi = new FileInfo(file);
Console.WriteLine(“Manual – ” + sw.ElapsedMilliseconds + “; FileSize – ” + fi.Length);
}

Console.ReadLine();
}
}
[Serializable()]
public class IOTest
{
public int member1 = 0x7FFFFFFF;
public int member2 = 0x7FFFFFFF;
public int member3 = 0x7FFFFFFF;
public void Save(BinaryWriter bw)
{
bw.Write(member1);
bw.Write(member2);
bw.Write(member3);
}
public static IOTest Load(BinaryReader br)
{
IOTest obj = new IOTest();
obj.member1 = br.ReadInt32();
obj.member2 = br.ReadInt32();
obj.member3 = br.ReadInt32();
return obj;
}
}
}


Порівняємо швидкість збереження та розміри генеруються файлів в байтах:
























Назва Швидкість Розмір файлу
XmlSerializer 697 2400000
BinaryFormatter 625 1520000
SoapFormatter 1411 6930000
Manual 3 120000

Тут розробники стандартних класів зовсім сплохували. Як видно з тіста, ручний спосіб швидше класів XmlSerializer і BinaryFormatter приблизно в 230 раз, і це при тому, що файл виходить менше в 20 разів! Чесно зізнаюся, у мене були деякі надії на клас BinaryFormatter (само слово Binary в назві вселяло довіру). Справа в тому, що XmlSerializer зберігає дані в форматі xml, а це, як відомо, не самий компактний формат. Пропоную подивитися на фрагменти цих файлів, що б стало ясно, чому ж розмір так відрізняється (див. відповідні ілюстрації). Як видно XmlSerializer зберігає в тестовому вигляді. Число 2147483647 в текстовому вигляді важить 10 або 20 байт, в залежності від кодування, а в бінарному форматі всього чотири байти. Плюс до всього, кожен запис обрамляється тегами види: any data . Це, мабуть, самий «товстий» формат зберігання даних з усіх, що мені доводилося бачити, а причиною тому є рясне використання службових даних. BinaryFormatter виявляєте бінарним лише частково, на малюнку видно, що корисні дані становлять приблизно 15% від загальної кількості і що по-справжньому бінарнихданних тут дуже мало. Хотілося б відзначити, що при створенні стандартними класами, розмір файлів залежить ще й від розміру назв полів. У нашому прикладі назви маленькі member1, member2 і т.д., але якщо б вони були довші, тоді і розмір файлів був би більше, оскільки назви полів включаються у файл. Тобто, якщо ми зробили програму, в якій вони вже використовуються, а потім випустили нову версію, в якій змінилися ці назви, чи змінилася кількість цих полів, тоді при завантаженні даних відбудеться помилка, яка зробить неможливою цю завантаження. На мій погляд, це істотний недолік, який відсутній при ручному збереженні. Ще один недолік полягає в тому, що ці класи зберігають абсолютно всі відкриті поля, і немає можливості вибрати, що зберігати, а що ні. Досить часто відриті поля потрібні для самої програми, і немає ніякого сенсу зберігати ці дані, особливо беручи до уваги такі могутні втрати продуктивності. Окремо хочу розповісти про SoapFormatter. Логічно це більш Високорівнева конструкція класу XmlSerializer. Формат Soap був створений для реалізації віддалених викликів процедур. В. NET він використовується в технологіях веб-сервісів і Remouting. Remouting зроблений для полегшення розробки клієнт-серверних рішень. По суті, він замінює TCP / IP. Тепер уявімо, що з усіх способів введення / виведення даних, Remouting використовує найгірший і це при тому, що дані повинні передаватися по мережі, що накладає обмеження на їх розмір. SoapFormatter 120 кілобайт корисних даних здатний перетворити в шість мегабайт непотрібних. У цьому тесті серіалізовать щодо маленькі обсяги даних (120 Кб). Зазвичай доводиться мати справу з даними в десять, а то і в сто разів більшими за обсягом. Враховуючи, що процесори нині все ж не всемогутні, можна зробити висновок, що дана технологія скоро зникне, оскільки разом з потужностями заліза і шириною каналів, також швидко ростуть і обсяги даних, які необхідно обробляти і пересилати.


Ініціалізація класів

Цю тему я не міг пропустити, оскільки вона стосується всіх, хто використовує класи. При компіляції ініціалізація всіх змінних виноситься в конструктори. Якщо при цьому привласнювати внутрішнім змінним значення параметрів конструктора, то змінна буде инициализироваться два рази. В принципі, якщо клас не створюється велика кількість разів (тобто якщо не потрібна продуктивність), тоді можна не звертати уваги на цей факт. Але, на мій погляд, потрібно мати звичку завжди і все писати академічно правильно. У зв’язку з цим є деякі рекомендації:







using System;using System.Diagnostics;namespace ConsoleApplication2{
class Program
{
static void Main(string[] args)
{
test1();
test2();
Console.ReadLine();
}
static int count = 10000000;
static void test1()
{
Stopwatch sw = Stopwatch.StartNew();
MyClass1 m;
for (int i = 0; i < count; ++i)
{
m = new MyClass1();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
static void test2()
{
Stopwatch sw = Stopwatch.StartNew();
MyClass2 m;
for (int i = 0; i < count; ++i)
{
m = new MyClass2();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public class MyClass1
{
public MyClass1() : this(“DefaultValue1”, “DefaultValue2”, “DefaultValue3”){}
public MyClass1(string str1) : this(str1, “DefaultValue2”, “DefaultValue3”){}
public MyClass1(string str1, string str2) : this(str1, str2, “DefaultValue3”){}
public MyClass1(string str1, string str2, string str3)
{
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
string str1;
string str2;
string str3;
}
public class MyClass2
{
public MyClass2(): this(“DefaultValue1”){}
public MyClass2(string str1): this(str1, “DefaultValue2”){}
public MyClass2(string str1, string str2): this(str1, str2, “DefaultValue3”){}
public MyClass2(string str1, string str2, string str3)
{
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
string str1;
string str2;
string str3;
}
}}


Різниця майже подвійна, бо в першому методі незалежно від кількості конструкторів управління отримають все. У другому ж управління отримають тільки оголошені.


Порівняння типів


Всього існує три способи порівняння, яке можна зробити за допомогою is, as і typeof:






using System;using System.Diagnostics;interface Interface { }class ClassImplementor : Interface { }class ClassImplementor2 : Interface { }static class Program {
static void Main()
{
const int count = 10000000;
ClassImplementor c = new ClassImplementor();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
if (c is ClassImplementor)
{
int it = 1;
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
if (c as ClassImplementor != null)
{
int it = 1;
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Type t = c.GetType();
sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
if (t == typeof(ClassImplementor))
{
int it = 1;
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Console.ReadLine();
} }


Різницю між is і as оком складно помітити, а от конструкції з typeof краще не використовувати.


Перевірка на ініціалізацію рядки


Є два популярних способу перевірки: if (str! = Null && str! = «») Та if (str! = String.Empty). Всупереч усім переконанням, перший метод працює втричі швидше, якщо рядок дорівнює «» і вчетверо, якщо рядок дорівнює null. Обратітевніманіе на результат тесту:.






using System;using System.Diagnostics;static class Program{
static void Main()
{
const int count = 10000000;
Stopwatch sw = Stopwatch.StartNew();
string str = null;
for (int i = 0; i < count; i++)
{
if (str != null && str != “”)
{
int it = 1;
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
if (str != string.Empty)
{
int it = 1;
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Console.ReadLine();
}}

Висновок


Ми розглянули кілька способів зробити програму краще. Кожен повинен розуміти, що це далеко не всі способи на все не вистачить цілої книги. Мета даннойстатьі показати, наскільки важливо ставити собі запитання «А що швидше?». Програма працює завжди, і якщо програміст не приділяв належної уваги продуктивності, то вона трохи пригальмує тут, трішки там, а в підсумку програма буде гальмувати скрізь і завжди.

Примітка:


Всі тести необхідно запускати в Release версії без відладчика. Для отримання найбільшого якості тесту, необхідно закрити всі програми, вимкнути антивіруси, фаєрволи чи служби, требуещее значне кількість ресурсів. Кожен тест потрібно розбивати на кілька етапів. Наприклад в тесті for і foreach спочатку потрібно окремо протестувати for, закоментіровав тест foreach, а потім навпаки.


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*