the mine universe

пятница, 24 апреля 2009 г.

Профилирование WCF Трафика

Пролог

Память, процессор, сеть и диск. Вот они четыре системных ресурса без которых ни одно приложение жить не может:

К этой четверке я бы добавил энергопотребление. Кто знает, возможно со временем в блоки питания будут встраивать счетчики энергопотребления и Windows будет считать микро- и нано-ватты так же точно как и четверку выше. Если вдруг это случится возможно моя заметка окажется одной их первых о мониторинге и профилировании энергопотребления :)

Для ЦП и памяти есть JetBrains, Для дисковой очереди - MS SQL Express и старше. Для трафика - ни-че-го :(

- А зачем задумываться над количеством трафика?, резонно звучит легкомысленный вопрос,
- Гигабитная копеешная сеть легко справится с трафком большим, чем может обработать моя система N! Нечего трафик профилировать!

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

Traffic Statisic Behavior

Профилирование трафика критически необходимо для случаев медленного либо дорогого трафика. Т.е. необходимость профилирования трафика между компонентами возникает в двух случаях:

  • Клиенты используют платные каналы связи между компонентами системы и активно требуют минимизации расходов на трафик
  • Клиенты используют медленные каналы связи и выражают недовольство производительностью на медленных каналах связи.

Мне хотелось добиться максимальной изоляции клиентского кода от инфраструктуры, реализующей удаленные вызовы. Необходимость вызвана не простой прихотью, а потребностью миграции на WCF от Remoting продукта, в разработке которого я участвую. В нем используется Sink на сервере, который на каждый удаленный вызов вызывает делегат и передает в нем:

  • Тип серверного объекта и имя метода
  • Кол-во переданных и полученных байт

Реализация делегата накапливает сумму трафика и группирует по типам и методам удаленных вызовов.

Behavior, о котором сейчас идет речь, реализует ту же концепцию для WCF. Небольшое отличие заключается в идентификации удаленного вызова. Вместо типа и метода используется имя Action. Вся работа behavior заключается в вызове делегата на каждую пару запрос-ответ:

delegate void Handler(string action, long requests, long recieved, long sent);

Для облегчения такой рутины библиотека включает класс TrafficStatistic, реализующий кандидат на обработчик делегата:

public void Merge(string action, long requests, long recieved, long sent)

Сколько трафика стоит определенная работа?

Этот вопрос – это начало, побудительный мотив, движущая сила и определяющий business value профилирования. На основе ответа становится легко доступным два простых в реализации шага:

  1. Поиск бутылочного горлышка - самого «тяжелого» запроса, или запросов
  2. Рефакторинг клиентской и или серверной стороны рядом с бутылочным горлышком с целью снижения трафика

Обычно рефакторинг не требуется. Концептуальной перестройки логики мне не потребовалось ни разу. Меня всегда выручало два способа которыми сейчас поделюсь:

  • Кэширование нужных данных
  • Удаление ненужных из сообщений

Возвращаясь к вопросу в заголовке. Поначалу я делал копии отчета, об оригинале отчета дальше расскажу, перед и после выполнения некоторой работы. Затем пробегал оба отчета глазами и легко находил самый тяжелый запрос. Со временем меня это стало утомлять – намного увлекательней, обнаружив бутылочное горлышко, точно и быстро это самое бутылочное горло устранить.

Параллельно есть возможность в коде точно замерить трафик и его структуру. Достаточно перед и после определенной работы сохранить в памяти трафик и структуру (трафик и структура описывается экземпляром TrafficStatistic) и потом вычесть «перед» из «после». Получится очень точно и наглядно. Это очень красивая возможность. Я о такой удобной возможности подумал сразу и реализовал по аналогии с JetBrains dotTrace Profiler и его SDK. Увы и ах – с трафиком точной подсчет пока не пригодился.

В сухом остатке мне оказалось лень заниматься вычитанием и делать копии отчетов структуры трафика в первом случае. Мне так же оказалось лень искать точки съема показаний трафика в коде для второго случая. Все благодаря тому же dotTrace от, не рекламы ради, JetBrains.

Ну действительно, когда память и CPU надо профилировать, то достаточно в GUI нажать «Start profiling» перед, и «Get Snapshot» после. А когда трафик приходится профилировать, нужно искать отчет структуры трафика и выискивать глазами разницу, а потом еще и с клавиатурой калькулятора упражняться в вычитании натуральных чисел. В итоге лень взяла свое – и появилась GUI Traffic Profiler с кнопками Start & Stop и немедленным отчетом о структуре трафика. Не хотел показаться нескромным, но все как у JetBrains.

Voilà:

WCF Profiler

вторник, 21 октября 2008 г.

Обработка строк без учета регистра. Окончание.

В продолжение предыдущего поста со стремным кодом приведу неполный список языков с их алфавитами (продолжение в коментариях :)
  • Греческий
  • Немецкий
  • Русский
  • Турецкий
  • Украинский
  • ...

И конечно множество поддерживаемых букв в картинках:
United Alphabets

Обработка строк без учета регистра

Казалось бы космические карабли бороздят просторы нашей вселенной и сообщество devелопмента активно сопротивляется сдвигам парадигмы и коментариями забрасывают новое. А мне почему-то в первом посте хочется вернуться в историю и вспомнить что сегодня все еще существуют и активно продаются устройства с очень... Хотя нет, с драматически урезанными платформами. Урезанными еще мягко сказано. Вот представте себе что в базовом наборе библиотек нет математики вещественных чисел. Или сортировки, или встроенной поддержки форматирования дат и чисел хотя бы для европы и америки. И тем неменее некоторые реализации урезанных платформ представляются не только юзабельными но и эстетически привлекательными в базовой поставке. И что? А вот что нашел в своих архивах - преобразование символов к нужному регистру и сравнение строк без учета регистра, без которой та самая сортировка строк оказывается неприемлемой. Еще попробую претендовать на самый компактный код - после обсфукации получается бинарник размером менее 2K. Поддерживаются всякие разные языки. Какие напишу позже. Продолжение следует.
  1: public class UnicodeInfoCompact
  2: {
  3:     
  4:     private UnicodeInfoCompact()
  5:     {
  6:     }
  7:     
  8:     public static boolean IsLetter(char value)
  9:     {
 10:         return GetLetter(value) != 0L;
 11:     }
 12:     
 13:     public static char ToUpper(char value)
 14:     {
 15:         long info = GetLetter(value);
 16:         return
 17:           info == 0
 18:           ? value
 19:           : (char) ((info >> 16) & 0xFFFF);
 20:     }
 21:     
 22:     public static char ToLower(char value)
 23:     {
 24:         long info = GetLetter(value);
 25:         return
 26:           info == 0
 27:           ? value
 28:           : (char) (info & 0xFFFF);
 29:     }
 30:     
 31:     
 32:     private static long GetLetter(char value)
 33:     {
 34:         int v = (int) value;
 35:         int lb = _blocks.length;
 36:         for(int i=lb-1; i>=0; i--)
 37:         {
 38:             int bv = _blocks[i];
 39:             int firstUpper = (bv >> 16) & 0xFFFF;
 40:             int translation = (int) (byte) (bv & 0xFF);
 41:             int delta = ((bv & 0x100) == 0) ? 1 : 2;
 42:             int length = (bv >> 9) & 0x7F;
 43:             int firstLower = firstUpper + translation;
 44:             // is upper case
 45:             if (v >= firstUpper && v <= firstUpper + (length - 1) * delta)
 46:             {
 47:                 if (((v-firstUpper) % delta) == 0)
 48:                 {
 49:                     long ret = (v << 16) | (firstLower + (v-firstUpper));
 50:                     return ret;
 51:                 }
 52:             }
 53:             
 54:             // is lower case
 55:             if (v >= firstLower && v <= firstLower + (length - 1) * delta)
 56:             {
 57:                 if (((v - firstLower) % delta) == 0)
 58:                 {
 59:                     long ret = ((firstUpper + (v - firstLower)) << 16) | v;
 60:                     return ret;
 61:                 }
 62:             }
 63:         }
 64:         
 65:         // outside of blocks
 66:         int lc = _chars.length;
 67:         for(int i=0; i<lc; i++)
 68:         {
 69:             int lower = _chars[i] & 0xFFFF;
 70:             int upper = _chars[i] >> 16;
 71:             if (v == lower || v == upper)
 72:             {
 73:                 long ret = (upper << 16) | lower;
 74:                 return ret;
 75:             }
 76:         }
 77:         
 78:         return 0L;
 79:     }
 80:     
 81:     private static final int _blocks[] = new int[] {
 82:         0x00413420, //    Block: 0041 ( 26*1) U=L 32
 83:         0x00C02E20, //    Block: 00C0 ( 23*1) U=L 32
 84:         0x00D80E20, //    Block: 00D8 (  7*1) U=L 32
 85:         0x01003101, //    Block: 0100 ( 24*2) U=L 1
 86:         0x01320701, //    Block: 0132 (  3*2) U=L 1
 87:         0x01391101, //    Block: 0139 (  8*2) U=L 1
 88:         0x014A2F01, //    Block: 014A ( 23*2) U=L 1
 89:         0x01790701, //    Block: 0179 (  3*2) U=L 1
 90:         0x01A00701, //    Block: 01A0 (  3*2) U=L 1
 91:         0x01CD1101, //    Block: 01CD (  8*2) U=L 1
 92:         0x01DE1301, //    Block: 01DE (  9*2) U=L 1
 93:         0x01FC1D01, //    Block: 01FC ( 14*2) U=L 1
 94:         0x03880625, //    Block: 0388 (  3*1) U=L 37
 95:         0x03912220, //    Block: 0391 ( 17*1) U=L 32
 96:         0x03A31220, //    Block: 03A3 (  9*1) U=L 32
 97:         0x03E20F01, //    Block: 03E2 (  7*2) U=L 1
 98:         0x04011850, //    Block: 0401 ( 12*1) U=L 80
 99:         0x04104020, //    Block: 0410 ( 32*1) U=L 32
100:         0x04602301, //    Block: 0460 ( 17*2) U=L 1
101:         0x04903101, //    Block: 0490 ( 24*2) U=L 1
102:         0x04D01D01, //    Block: 04D0 ( 14*2) U=L 1
103:         0x04EE0901, //    Block: 04EE (  4*2) U=L 1
104:         0x05314C30, //    Block: 0531 ( 38*1) U=L 48
105:         0x10A04C30, //    Block: 10A0 ( 38*1) U=L 48
106:         0x1E009701, //    Block: 1E00 ( 75*2) U=L 1
107:         0x1EA05B01, //    Block: 1EA0 ( 45*2) U=L 1
108:         0x1F0810F8, //    Block: 1F08 (  8*1) U=L -8
109:         0x1F180CF8, //    Block: 1F18 (  6*1) U=L -8
110:         0x1F2810F8, //    Block: 1F28 (  8*1) U=L -8
111:         0x1F3810F8, //    Block: 1F38 (  8*1) U=L -8
112:         0x1F480CF8, //    Block: 1F48 (  6*1) U=L -8
113:         0x1F5909F8, //    Block: 1F59 (  4*2) U=L -8
114:         0x1F6810F8, //    Block: 1F68 (  8*1) U=L -8
115:         0x1FC808AA, //    Block: 1FC8 (  4*1) U=L -86
116:         0xFF213420  //    Block: FF21 ( 26*1) U=L 32
117:           
118:     };
119:     private static final int _chars[] = new int[] {
120:         0x017800FF,
121:         0x01810253,
122:         0x01820183,
123:         0x01840185,
124:         0x01860254,
125:         0x01870188,
126:         0x01890256,
127:         0x018A0257,
128:         0x018B018C,
129:         0x018E01DD,
130:         0x018F0259,
131:         0x0190025B,
132:         0x01910192,
133:         0x01930260,
134:         0x01940263,
135:         0x01960269,
136:         0x01970268,
137:         0x01980199,
138:         0x019C026F,
139:         0x019D0272,
140:         0x019F0275,
141:         0x01A701A8,
142:         0x01A90283,
143:         0x01AC01AD,
144:         0x01AE0288,
145:         0x01AF01B0,
146:         0x01B1028A,
147:         0x01B2028B,
148:         0x01B301B4,
149:         0x01B501B6,
150:         0x01B70292,
151:         0x01B801B9,
152:         0x01BC01BD,
153:         0x01C401C6,
154:         0x01C701C9,
155:         0x01CA01CC,
156:         0x01F101F3,
157:         0x01F401F5,
158:         0x01FA01FB,
159:         0x038603AC,
160:         0x038C03CC,
161:         0x038E03CD,
162:         0x038F03CE,
163:         0x040E045E,
164:         0x040F045F,
165:         0x04C104C2,
166:         0x04C304C4,
167:         0x04C704C8,
168:         0x04CB04CC,
169:         0x04F804F9,
170:         0x1FBA1F70,
171:         0x1FBB1F71,
172:         0x1FDA1F76,
173:         0x1FDB1F77,
174:         0x1FF81F78,
175:         0x1FF91F79,
176:         0x1FEA1F7A,
177:         0x1FEB1F7B,
178:         0x1FFA1F7C,
179:         0x1FFB1F7D,
180:         0x1FB81FB0,
181:         0x1FB91FB1,
182:         0x1FD81FD0,
183:         0x1FD91FD1,
184:         0x1FE81FE0,
185:         0x1FE91FE1,
186:         0x1FEC1FE5
187:           
188:     };
189:     
190:     
191: }
192: 

Ярлыки (Tags)