Note that there are some explanatory texts on larger screens.

plurals
  1. PORefactoring code with a lot of "locks" to more lock-free code
    text
    copied!<p><strong>Upd</strong> thanks to Matthew Watson for noticing and note that I plan to port my code to c++-linux so I prefer "platform-independent" code</p> <p>My trading application is almost lock-free. The code below is the <strong>only</strong> place where I do use locks. Let me start with the code, it's pretty long but don't worry there are a lot of repeating parts so it's simple. I prefer to add all "repeating" parts to better demonstrate how my things work:</p> <pre><code>Task.Factory.StartNew(() =&gt; { while (true) { Iterate(); } }, TaskCreationOptions.LongRunning); private void Iterate() { bool marketDataUpdated = false; lock (ordersToRegisterLock) { if (ordersToRegister.Count &gt; 0) { marketDataUpdated = true; while (ordersToRegister.Count &gt; 0) { Order order = ordersToRegister.Dequeue(); // Stage1, Process } } } lock (aggrUpdatesLock) { if (aggrUpdates.Count &gt; 0) { marketDataUpdated = true; while (!aggrUpdates.IsNullOrEmpty()) { var entry = aggrUpdates.Dequeue(); // Stage1, Process } } } lock (commonUpdatesLock) { if (commonUpdates.Count &gt; 0) { marketDataUpdated = true; while (!commonUpdates.IsNullOrEmpty()) { var entry = commonUpdates.Dequeue(); // Stage1, Process } } } lock (infoUpdatesLock) { if (infoUpdates.Count &gt; 0) { marketDataUpdated = true; while (!infoUpdates.IsNullOrEmpty()) { var entry = infoUpdates.Dequeue(); // Stage1, Process } } } lock (tradeUpdatesLock) { if (tradeUpdates.Count &gt; 0) { marketDataUpdated = true; while (!tradeUpdates.IsNullOrEmpty()) { var entry = tradeUpdates.Dequeue(); // Stage1, Process } } } if (marketDataUpdated) { // Stage2 ! // make a lot of work. expensive operation. recalculate strategies, place orders etc. } } private readonly Queue&lt;Order&gt; ordersToRegister = new Queue&lt;Order&gt;(); private readonly object ordersToRegisterLock = new object(); private readonly Queue&lt;AggrEntry&gt; aggrUpdates = new Queue&lt;AggrEntry&gt;(); private readonly object aggrUpdatesLock = new object(); private readonly Queue&lt;CommonEntry&gt; commonUpdates = new Queue&lt;CommonEntry&gt;(); private readonly object commonUpdatesLock = new object(); private readonly Queue&lt;InfoEntry&gt; infoUpdates = new Queue&lt;InfoEntry&gt;(); private readonly object infoUpdatesLock = new object(); private readonly Queue&lt;TradeEntry&gt; tradeUpdates = new Queue&lt;TradeEntry&gt;(); private readonly object tradeUpdatesLock = new object(); public void RegistorOrder(object sender, Gate.RegisterOrderArgs e) { lock (ordersToRegisterLock) { ordersToRegister.Enqueue(e.order); } } public void TradeUpdated(object sender, Gate.TradeArgs e) { lock (tradeUpdatesLock) { foreach (var entry in e.entries) { tradeUpdates.Enqueue(entry); } } } public void InfoUpdated(object sender, Gate.InfoArgs e) { lock (infoUpdatesLock) { foreach (var entry in e.entries) { infoUpdates.Enqueue(entry); } } } public void CommonUpdated(object sender, Gate.CommonArgs e) { lock (commonUpdatesLock) { foreach (var entry in e.entries) { commonUpdates.Enqueue(entry); } } } public void AggrUpdated(object sender, Gate.AggrArgs e) { lock (aggrUpdatesLock) { foreach (var entry in e.entries) { aggrUpdates.Enqueue(entry); } } } </code></pre> <p>In my code I have two stages. <code>Stage1</code> is update stage and <code>Stage2</code> is working stage. I need to switch between these two stages as fast as possible, like that:</p> <ul> <li>any updates? no</li> <li>any updates? no</li> <li>any updated? yes, order updated! apply update, do <code>Stage2</code></li> <li>any updates? no</li> <li>any updates? yes, order need to be registered! apply update, do <code>Stage2</code></li> <li>any updates? yes, trade occured, apply update, do <code>Stage2</code></li> </ul> <p>In <code>Stage2</code> I should not update, but should keep "collecting" updates so I can apply they later.</p> <p>And important thing - this is very latency-critical code so I agree to "spent" one core for having <strong>minimal</strong> latency! So when <strong>any</strong> update occure I need to process it <strong>asap</strong> and perform <code>Stage2</code>.</p> <p>So I hope now it's clear what I need to achieve and it's clear how I have implemented that. Now it's time to discuss how good my code is. I do see several potential problems:</p> <ul> <li>a lot of locks! can it be replaced with some "lock-free" code? spinlock with CAS or something?</li> <li>occupy 100% of CPU core, can I save some CPU resources <strong>without affecting latency</strong>?</li> <li>can/should I tell .NET to use "dedicated" core (set task affinity?) to avoid extra "switch"?</li> <li>I <em>add</em> to Queues from one thread and I <em>read</em> Queues from another thread. Could it be a problem? If adding and reading to a Queue is volatile? Is it possible that my <em>reading</em> thread will not see update from a Queue because cache-update problem?</li> </ul> <p>Any suggestions how to improve what I wrote are welcome, thanks!</p> <p><strong>upd</strong> partly solved - as I understand I better to replace queries to lock-free (likely ring-buffer based?) queries.. i think i will use c++ version of disruptor later. Also I've used this article <a href="http://www.umbraworks.net/bl0g/rebuildall/2010/03/08/Running_NET_threads_on_selected_processor_cores" rel="nofollow">http://www.umbraworks.net/bl0g/rebuildall/2010/03/08/Running_NET_threads_on_selected_processor_cores</a> and replaced Task with a Thread running on the "fixed" core, however i'm still using "busy-spin", probably I should use something smarter?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload