Monday, November 30, 2009

Things, that I got during writing .Net connector for Redis.

Redis is a distributed key-value storage. Connector is a protocol-driver for storing/retrieving data from Redis.  Recently I have developed one. I taught following simple lessons from it:

SRP principle makes code better prepared for changes

During development of the connector, I were stick to "SRP" and "TDD" principle.  Later i have realized that SRP is really helps, if you face requirement of unexpected code changes.

In short SRP says that you should have only one reason to change the class.

.NET code is as fast as native implementation

Spuriously, native ANSI-C implementation of Redis benchmark is as fast, as .NET implementation of the connector with same benchmark, moreover with all those patterns and enterprise stuff.

.NET buffered stream is a good abstraction

Connector is written in a way – that avoids copying arguments (byte arrays) in one big array, for sending to Redis. Instead it saves references to all arguments and writes in in a sequence. That causes serious performance problems. I have avoided this, just by wrapping socket's write stream with buffered stream. It's gave me 2x performance 'boost' (2000 requests per second vs approx. 5000).

comand pipelininG is a good IDEA

Command pipelining, it's a feature, when you put all you requests trough single socket. For example, is you send (in a row) 3 GET's for "foo", "bar" and "baz" keys – you will receive commands results in the same order. It gave me good results over the 100MBit network:

512k Localhost
.....Normal :131.095123900879
.....Pipelined 1 conn :59.8802395209581
.....Pipelined 2 conn :79.8403193612775
.....Pipelined 5 conn :99.7804829375374
.....Pipelined 10 conn :120
.....Pipelined 50 conn :137.793707686181
512k Remote host
.....Normal :37.9241516966068
.....Pipelined 1 conn :40.7918416316737
.....Pipelined 2 conn :42.4084816963393
.....Pipelined 5 conn :42.3238171291675
.....Pipelined 10 conn :40.4080816163233
.....Pipelined 50 conn :39.8962696987832
1kb localhost
.....Normal :7330.13589128697
.....Pipelined 1 conn :6500.89802434644
.....Pipelined 2 conn :7438.32335329341
.....Pipelined 5 conn :8085.56577369052
.....Pipelined 10 conn :7821.23575284943
.....Pipelined 50 conn :6627.87442511498
1kb Remote host
.....Normal :500
.....Pipelined 1 conn :6543.7125748503
.....Pipelined 2 conn :9555.51110222044
.....Pipelined 5 conn :11052.7788884446
.....Pipelined 10 conn :10608
.....Pipelined 50 conn :520

multithreaded code is VERY hard to debug


During implementation of pipelining I faced with a racing problem. Old pipelining algorithm allows  racing during parsing response from Redis. Microsoft Chess helped me a lot to test and localize this problem. It's a special tool, that runs test code with worst-case thread switching scenario.



Free profilers exists (NProf and Slimtune)



Earlier, I had no idea how to profile application at low cost (JetBrains and Ant's Profilers are expensive). Recently I found NProf and SlimTune. They helped me a lot to find bottlenecks.

No comments:

Post a Comment