<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Graph Engine</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/graphengine/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/graphengine/feed.xml</link>
    <description>Serving big graphs in real-time</description>
    <lastBuildDate>Wed, 17 Apr 2019 04:10:51 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Graph Engine vs. C++ unordered_map: memory consumption test, Part 2</title>
      <link>https://docs.microsoft.com/archive/blogs/graphengine/graph-engine-vs-c-unordered_map-memory-consumption-test-part-2</link>
      <pubDate>Sun, 07 Jun 2015 06:01:00 GMT</pubDate>
      <dc:creator><![CDATA[yatli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/graphengine/2015/06/07/graph-engine-vs-c-unordered_map-memory-consumption-test-part-2/</guid>
      <description><![CDATA[In my previous post, I demonstrated the memory management efficiency of Graph Engine against C++...]]></description>
      <content:encoded><![CDATA[<p>In my previous post, I demonstrated the memory management efficiency of Graph Engine against C++ unordered_map with MSVC's built-in memory allocator. Note that the test is not about unordered_map alone, but a combination with buffer allocation and deallocation. I would like to measure how does the program consume memory with all these parts coming together. The goal is not to beat C++, not to beat unordered_map, but rather to show that, even the Graph Engine key-value store is designed to manage billions of objects across multiple machines, it does not get bloated with too many abstractions when it comes down to the simplest task, and is even comparable to a bare-metal C++ built-in data structure, which is not performing bad at all. There are a lot of discussions and I'd like to thank all for the comments and feedback. Here are a few important points collected from the discussion:</p>
<p><br />1. The C++ part has memory leak concerning the overwritten entries.<br />2. The rand()*rand() is a bad idea. Also, using different random generators(C routine vs. C# Random class) does not produce precisely aligned semantics.<br />3. There are overheads invoking the random generators.<br />4. For the C++ part, people demand better coding style :-)<br />5. Plot the memory consumption chart with an origin.<br /><span style="text-decoration: line-through;">6. For better performance, call reserve() on the unordered_map.</span><br /><span style="text-decoration: line-through;">7. For better performance, embed content buffer into the cell entries.</span><br /><span style="text-decoration: line-through;">8. Why does the C# program only allocate buffer once outside the loop?</span></p>
<p><br />Let me first explain why some of these proposed changes to the test are ruled out. For reserve(), it does improve the performance on the C++ side. But please note that I did not make any optimizations on the C# side either. And for the purpose of a key-value store, there should not be pre-assumed space reservations hard coded into the program, because generally if you are going to put it online, instead of doing planned experiments, you cannot know the usage patterns in advance. For similar reasons, we cannot embed fixed-length std::array into cell entries, because the value blobs stored into a key-value store may vary in size. Plus, variable length entries do not fit into a map, or we will end up storing references to these content objects, which, logically does not make a difference than a void*. The program only allocate a single C# byte array, because the Graph Engine K/V store has its own memory management subsystem, which is not allocating from the CLR managed heap.<br /><br /></p>
<div class="WordSection1">To make the random number generation fair to both the C++ and C# test programs, I switched to a dedicated C++ program for generating test instructions for both programs. It randomly generates 64M store operations, each containing a cell id and a cell size. The maximum cell size is twice as large as in the last test so that the expected total size should be the same.</div>
<div class="highlight">
<pre><code class="C++" data-lang="C++">#include&nbsp;"stdafx.h"<br />#include&nbsp;&lt;random&gt;<br />#include&nbsp;&lt;functional&gt;<br />#include&nbsp;&lt;unordered_map&gt;<br />#include&nbsp;&lt;algorithm&gt;<br />#include&nbsp;&lt;cstdint&gt;<br />#include&nbsp;&lt;cstdio&gt;<br /><br />#define&nbsp;ENTRY_COUNT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(64&lt;&lt;20)<br />#define&nbsp;ENTRY_MAX_ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ENTRY_COUNT&nbsp;*&nbsp;4)<br />#define&nbsp;ENTRY_MAX_SIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(314&nbsp;*&nbsp;2)<br />#define&nbsp;BUCKET_COUNT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(64)<br />#define&nbsp;BUCKET_GRANULARITY&nbsp;(ENTRY_MAX_SIZE&nbsp;/&nbsp;BUCKET_COUNT)<br /><br />struct&nbsp;param_entry<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;int64_t&nbsp;cell_id;<br />&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;cell_size;<br />};<br /><br />int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;std::default_random_engine&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rnd_gen;<br />&nbsp;&nbsp;&nbsp;&nbsp;std::uniform_int_distribution&lt;int64_t&gt;&nbsp;cell_id_dist(0,&nbsp;ENTRY_MAX_ID);<br />&nbsp;&nbsp;&nbsp;&nbsp;std::uniform_int_distribution&lt;int32_t&gt;&nbsp;cell_size_dist(1,&nbsp;ENTRY_MAX_SIZE);<br />&nbsp;&nbsp;&nbsp;&nbsp;std::vector&lt;param_entry&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param_entries(ENTRY_COUNT);<br />&nbsp;&nbsp;&nbsp;&nbsp;std::unordered_map&lt;int32_t,&nbsp;int32_t&gt;&nbsp;&nbsp;&nbsp;size_buckets;<br />&nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;cell_id_gen&nbsp;&nbsp;&nbsp;=&nbsp;std::bind(cell_id_dist,&nbsp;rnd_gen);<br />&nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;cell_size_gen&nbsp;=&nbsp;std::bind(cell_size_dist,&nbsp;rnd_gen);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(auto&amp;&nbsp;param&nbsp;:&nbsp;param_entries)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param.cell_id&nbsp;&nbsp;&nbsp;=&nbsp;cell_id_gen();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param.cell_size&nbsp;=&nbsp;cell_size_gen();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;FILE*&nbsp;fp;<br />&nbsp;&nbsp;&nbsp;&nbsp;fopen_s(&amp;fp,&nbsp;"params.dat",&nbsp;"wb");<br />&nbsp;&nbsp;&nbsp;&nbsp;fwrite(param_entries.data(),&nbsp;sizeof(param_entry),&nbsp;ENTRY_COUNT,&nbsp;fp);<br />&nbsp;&nbsp;&nbsp;&nbsp;fclose(fp);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;statictical&nbsp;analysis&nbsp;*/<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;BUCKET_COUNT;&nbsp;++i)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_buckets[i]&nbsp;=&nbsp;0;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(auto&amp;&nbsp;param&nbsp;:&nbsp;param_entries)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;bucket_idx&nbsp;=&nbsp;param.cell_size&nbsp;/&nbsp;BUCKET_GRANULARITY;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++size_buckets[bucket_idx];<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;std::sort(param_entries.begin(),&nbsp;param_entries.end(),&nbsp;[](param_entry&nbsp;&amp;a,&nbsp;param_entry&nbsp;&amp;b)&nbsp;{return&nbsp;a.cell_id&nbsp;&lt;&nbsp;b.cell_id;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;new_end&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;std::unique(param_entries.begin(),&nbsp;param_entries.end()&nbsp;,&nbsp;[](param_entry&nbsp;&amp;a,&nbsp;param_entry&nbsp;&amp;b){&nbsp;return&nbsp;a.cell_id&nbsp;==&nbsp;b.cell_id;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;int32_t&nbsp;unique_entry_count&nbsp;=&nbsp;new_end&nbsp;-&nbsp;param_entries.begin();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;printf("&gt;&gt;&gt;");<br />&nbsp;&nbsp;&nbsp;&nbsp;printf("%d&nbsp;total&nbsp;entries\n",&nbsp;ENTRY_COUNT);<br />&nbsp;&nbsp;&nbsp;&nbsp;printf("&gt;&gt;&gt;");<br />&nbsp;&nbsp;&nbsp;&nbsp;printf("%d&nbsp;unique&nbsp;entries\n",&nbsp;unique_entry_count);<br />&nbsp;&nbsp;&nbsp;&nbsp;printf("&gt;&gt;&gt;");<br />&nbsp;&nbsp;&nbsp;&nbsp;printf("Size&nbsp;histogram:\n");<br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;BUCKET_COUNT;&nbsp;++i)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%-2d\t\t%d\n",&nbsp;i&nbsp;*&nbsp;BUCKET_GRANULARITY,&nbsp;size_buckets[i]);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />}<br /> </code></pre>
</div>
<p><br />The resulting data file params.dat takes exactly 1GB of space, with the following statistics information:</p>
<div class="highlight">
<pre><code class="text" data-lang="text">&gt;&gt;&gt;67108864&nbsp;total&nbsp;entries<br />&gt;&gt;&gt;59378342&nbsp;unique&nbsp;entries<br />&gt;&gt;&gt;Size&nbsp;histogram:<br />0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;855411<br />9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962141<br />18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960462<br />27&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961976<br />36&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961641<br />45&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961725<br />54&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;963224<br />63&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961521<br />72&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961336<br />81&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961785<br />90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961053<br />99&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;964048<br />108&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962261<br />117&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961443<br />126&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960895<br />135&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962208<br />144&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960792<br />153&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960925<br />162&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961486<br />171&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961390<br />180&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961762<br />189&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961682<br />198&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962320<br />207&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962201<br />216&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960415<br />225&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962173<br />234&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;963833<br />243&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960419<br />252&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960049<br />261&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;963305<br />270&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;959831<br />279&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;959656<br />288&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961434<br />297&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961652<br />306&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;964231<br />315&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;963157<br />324&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961786<br />333&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960566<br />342&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961645<br />351&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961816<br />360&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961421<br />369&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961435<br />378&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962095<br />387&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960651<br />396&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962341<br />405&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961820<br />414&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961355<br />423&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961368<br />432&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960645<br />441&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961942<br />450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960869<br />459&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962132<br />468&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962618<br />477&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960927<br />486&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962067<br />495&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;960434<br />504&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962018<br />513&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962531<br />522&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962315<br />531&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961781<br />540&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;962139<br />549&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961675<br />558&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;963721<br />567&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;961252<br /> </code></pre>
</div>
<p>Uniform enough. I limited the range of cell ids to [0, 256M] to introduce some duplicated entries. And the test code, updated, here:</p>
<div class="highlight">
<pre><code class="C++" data-lang="C++">#include&nbsp;"stdafx.h"<br />#include&nbsp;&lt;vector&gt;<br />#include&nbsp;&lt;iostream&gt;<br />#include&nbsp;&lt;unordered_map&gt;<br />#include&nbsp;&lt;random&gt;<br />#include&nbsp;&lt;functional&gt;<br />#include&nbsp;&lt;chrono&gt;<br />#include&nbsp;&lt;cstdint&gt;<br />#include&nbsp;&lt;ctime&gt;<br />using&nbsp;namespace&nbsp;std;<br /><br />#define&nbsp;ENTRY_COUNT&nbsp;(1&lt;&lt;20)*64<br /><br />typedef&nbsp;int64_t&nbsp;cellid_t;<br /><br />struct&nbsp;CellEntry<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;void*&nbsp;&nbsp;&nbsp;&nbsp;ptr;<br />&nbsp;&nbsp;&nbsp;&nbsp;int32_t&nbsp;&nbsp;len;<br />&nbsp;&nbsp;&nbsp;&nbsp;uint16_t&nbsp;cellType;<br />};<br /><br />struct&nbsp;param_entry<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;int64_t&nbsp;cell_id;<br />&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;cell_size;<br />};<br /><br />param_entry&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param_entries[ENTRY_COUNT];<br />unordered_map&lt;cellid_t,&nbsp;CellEntry&gt;&nbsp;LocalStorage;<br /><br />int&nbsp;main(int&nbsp;argc,&nbsp;char**&nbsp;argv)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;srand(time(NULL));<br />&nbsp;&nbsp;&nbsp;&nbsp;std::chrono::steady_clock::time_point&nbsp;start_time,&nbsp;end_time;<br />&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;&lt;&lt;&nbsp;"Preparing&nbsp;keys"&nbsp;&lt;&lt;&nbsp;std::endl;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;FILE*&nbsp;fp;<br />&nbsp;&nbsp;&nbsp;&nbsp;fopen_s(&amp;fp,&nbsp;"params.dat",&nbsp;"rb");<br />&nbsp;&nbsp;&nbsp;&nbsp;fread(param_entries,&nbsp;sizeof(param_entry),&nbsp;ENTRY_COUNT,&nbsp;fp);<br />&nbsp;&nbsp;&nbsp;&nbsp;fclose(fp);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;&lt;&lt;&nbsp;"Test&nbsp;started."&nbsp;&lt;&lt;&nbsp;std::endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;start_time&nbsp;=&nbsp;std::chrono::steady_clock::now();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(auto&nbsp;entry&nbsp;:&nbsp;param_entries)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void*&nbsp;cell_buf&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;new&nbsp;char[entry.cell_size];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;&nbsp;upsert_result&nbsp;=&nbsp;LocalStorage.emplace(entry.cell_id,&nbsp;CellEntry{&nbsp;cell_buf,&nbsp;entry.cell_size,&nbsp;0&nbsp;});<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!upsert_result.second)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::swap(cell_buf,&nbsp;upsert_result.first-&gt;second.ptr);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete[]&nbsp;cell_buf;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(rand()&nbsp;%&nbsp;3&nbsp;==&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete[]&nbsp;upsert_result.first-&gt;second.ptr;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalStorage.erase(entry.cell_id);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;end_time&nbsp;=&nbsp;std::chrono::steady_clock::now();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;&lt;&lt;&nbsp;"done."&nbsp;&lt;&lt;&nbsp;std::endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;&lt;&lt;&nbsp;"Time&nbsp;=&nbsp;"&nbsp;&lt;&lt;&nbsp;chrono::duration_cast&lt;chrono::seconds&gt;(end_time&nbsp;-&nbsp;start_time).count()&nbsp;&lt;&lt;&nbsp;std::endl;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;getchar();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />}<br /> </code></pre>
</div>
<p>C#:</p>
<div class="highlight">
<pre><code class="C#" data-lang="C#">using&nbsp;System;<br />using&nbsp;System.Collections.Generic;<br />using&nbsp;System.Diagnostics;<br />using&nbsp;System.IO;<br />using&nbsp;System.Linq;<br />using&nbsp;System.Runtime.InteropServices;<br />using&nbsp;System.Text;<br />using&nbsp;System.Threading.Tasks;<br />using&nbsp;Trinity;<br />using&nbsp;Trinity.Core.Lib;<br />namespace&nbsp;MemoryConsumptionTest_CSharp<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;unsafe&nbsp;internal&nbsp;class&nbsp;Program<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;&nbsp;ENTRY_COUNT&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;(64&nbsp;&lt;&lt;&nbsp;20);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;&nbsp;ENTRY_MAX_SIZE&nbsp;=&nbsp;(314&nbsp;*&nbsp;2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[StructLayout(LayoutKind.Explicit)]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;param_entry<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[FieldOffset(0)]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;long&nbsp;cell_id;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[FieldOffset(8)]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;cell_size;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[FieldOffset(12)]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;__pad;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[DllImport("msvcrt.dll")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;extern&nbsp;void*&nbsp;fopen(string&nbsp;fname,&nbsp;string&nbsp;mode);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[DllImport("msvcrt.dll")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;extern&nbsp;long&nbsp;fread(void*&nbsp;ptr,&nbsp;long&nbsp;size,&nbsp;long&nbsp;count,&nbsp;void*&nbsp;fp);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[DllImport("msvcrt.dll")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;extern&nbsp;void&nbsp;fclose(void*&nbsp;fp);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;SaveCell&nbsp;will&nbsp;alloc&nbsp;memory&nbsp;inside&nbsp;GE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;so&nbsp;we&nbsp;just&nbsp;allocate&nbsp;a&nbsp;single&nbsp;buffer&nbsp;from&nbsp;C#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte*&nbsp;buf&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;(byte*)Memory.malloc(ENTRY_MAX_SIZE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;param_entry*&nbsp;entries&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;(param_entry*)Memory.malloc((ulong)(ENTRY_COUNT&nbsp;*&nbsp;sizeof(param_entry)));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TrinityConfig.CurrentRunningMode&nbsp;=&nbsp;RunningMode.Embedded;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stopwatch&nbsp;stopwach&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;new&nbsp;Stopwatch();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;dice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;new&nbsp;Random();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Preparing&nbsp;keys");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;fp&nbsp;=&nbsp;fopen("params.dat",&nbsp;"rb");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fread(entries,&nbsp;sizeof(param_entry),&nbsp;ENTRY_COUNT,&nbsp;fp);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fclose(fp);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Test&nbsp;started.");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stopwach.Start();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(param_entry*&nbsp;p&nbsp;=&nbsp;entries,&nbsp;e&nbsp;=&nbsp;entries&nbsp;+&nbsp;ENTRY_COUNT;&nbsp;p&nbsp;!=&nbsp;e;&nbsp;++p)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveCell(p-&gt;cell_id,&nbsp;buf,&nbsp;p-&gt;cell_size,&nbsp;cellType:&nbsp;0);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(dice.Next(3)&nbsp;==&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.RemoveCell(p-&gt;cell_id);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stopwach.Stop();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Done.");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Time&nbsp;=&nbsp;{0}",&nbsp;stopwach.ElapsedMilliseconds&nbsp;/&nbsp;1000);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.ReadKey();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /> </code></pre>
</div>
<p>Updated performance result:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/2677.GE_vs_CPP_2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/2677.GE_5F00_vs_5F00_CPP_5F00_2.png"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/2677.GE_vs_CPP_2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/2677.GE_5F00_vs_5F00_CPP_5F00_2.png" alt="" border="0" /></a></p>
<p>Looking forward to your comments!<br /><br /><br /><br /></p>]]></content:encoded>
    </item>
    <item>
      <title>Unmanaged memory management in C# without P/Invoke</title>
      <link>https://docs.microsoft.com/archive/blogs/graphengine/unmanaged-memory-management-in-c-without-pinvoke</link>
      <pubDate>Sat, 06 Jun 2015 06:25:00 GMT</pubDate>
      <dc:creator><![CDATA[binshao@live.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/graphengine/2015/06/06/unmanaged-memory-management-in-c-without-pinvoke/</guid>
      <description><![CDATA[.NET CLR provides great managed memory management. But there are occasions when we want to manage...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal">.NET CLR provides great managed memory management. But there are occasions when we want to manage memory on our own. And yes, CLR allows a C# program to call native code via <a href="https://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspx" target="_blank"> P/Invoke</a>. There is nothing wrong with P/Invoke until we need to call a native API many times in a performance critical code section. Graph Engine provides a set of lightweight <a href="https://www.graphengine.io/APIRefs/html/T_Trinity_Core_Lib_Memory.htm" target="_blank"> wrappers</a> for the commonly-used C memory management functions, such as malloc, free, memcpy, memmove, and memcmp.</p>
<p class="MsoNormal">To use these functions, create a &ldquo;Graph Engine Application Project&rdquo; in Visual Studio and add the namespace &ldquo;<span style="font-family: Consolas; color: black;">Trinity.Core.Lib</span>&rdquo;. Compared with their P/Invoked counterparts, the Graph Engine wrappers can usually give us a little bit performance edge. Try out the following code snippet:</p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> System;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> System.Diagnostics;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> System.Runtime.InteropServices;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> System.Text;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> System.Threading.Tasks;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> Trinity;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">using</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> Trinity.Core.Lib;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">namespace</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> GraphEngineApp1</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">unsafe</span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">internal</span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">class</span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Program</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span> </span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">static</span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">void</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> Main(</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">string</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">[] args)</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span> {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">int</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> count = 1000000;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">void</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">* src = </span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Memory</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">.malloc(300);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">void</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">* dst = </span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Memory</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">.malloc(300);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Stopwatch</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> sw = </span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Stopwatch</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">.StartNew();</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">for</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> (</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: blue;">int</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> i = 0; i &lt; count; i++)</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Memory</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">.memcpy(dst, src, 300);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sw.Stop();</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Console</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">.WriteLine(sw.ElapsedMilliseconds);</span></p>
<p class="MsoNormal"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: #2b91af;">Memory</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;">.free(src);</span></p>
<p class="MsoNormal"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; line-height: 107%; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span> <span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; line-height: 107%; font-family: Consolas; color: #2b91af;"> Memory</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; line-height: 107%; font-family: Consolas; color: black;">.free(dst);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span> }</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; font-family: Consolas; color: black;"> <span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p class="MsoNormal"><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; line-height: 107%; font-family: Consolas; color: black;">}</span></p>
<p class="MsoNormal">Now replace the &ldquo;<span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; line-height: 107%; font-family: Consolas; color: #2b91af;">Memory</span><span style="font-size: 9.0pt; mso-bidi-font-size: 14.0pt; line-height: 107%; font-family: Consolas; color: black;">.memcpy(dst, src, 300)</span> &rdquo; with <a href="https://www.pinvoke.net/default.aspx/msvcrt.memcpy" target="_blank">the P/Invoked version</a>, and run again. On my machine, the GE wrapper version takes 14 ms, while the P/Invoked version takes 25 ms.</p>
<p class="MsoNormal">Use P/Invoke if its overhead is negligible for your application. Consider giving the GE wrappers a shot if the overhead caused by P/Invoke starts to bother you. <span style="mso-spacerun: yes;">&nbsp;</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Graph Engine vs. C++ unordered_map: memory consumption test</title>
      <link>https://docs.microsoft.com/archive/blogs/graphengine/graph-engine-vs-c-unordered_map-memory-consumption-test</link>
      <pubDate>Fri, 05 Jun 2015 02:33:00 GMT</pubDate>
      <dc:creator><![CDATA[yatli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/graphengine/2015/06/05/graph-engine-vs-c-unordered_map-memory-consumption-test/</guid>
      <description><![CDATA["How is Graph Engine&nbsp;K/V&nbsp;store&nbsp;different from a... C++ map?"
Well, virtually they do...]]></description>
      <content:encoded><![CDATA[<h1>"How is Graph Engine&nbsp;K/V&nbsp;store&nbsp;different from a... C++ map?"</h1>
<p>Well, virtually they do the same thing after all: to store key-value pairs. So why bother using Graph Engine against the other? I'm not going to talk about remote access, fault tolerance and other fancy stuff for now... Let's take a look at a much easier, but critical factor: memory consumption.</p>
<h1>Data structures</h1>
<p>First let's calibrate our data structures. For the Graph Engine side, we drop TSL (wait didn't you just write a long post about the importance of TSL?) and leave only the bare metal memory cache. It stores cell IDs (keys), cell types (unsigned short values), cell blob location and cell size. To simulate this, we use the following data structure in C++:</p>
<div class="highlight">
<pre><code class="language-C++" data-lang="C++">struct&nbsp;CellEntry<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;void*&nbsp;&nbsp;&nbsp;&nbsp;ptr;<br />&nbsp;&nbsp;&nbsp;&nbsp;int32_t&nbsp;&nbsp;len;<br />&nbsp;&nbsp;&nbsp;&nbsp;uint16_t&nbsp;cellType;<br />};<br /> </code></pre>
</div>
<p>Initially I picked map&lt;K,V&gt; instead of unordered_map&lt;K,V&gt;. After watching it crawling bytes at 20MB/s for minutes... ^C^C^C^C^C.</p>
<p>And here's the code.</p>
<p>C++:</p>
<div class="highlight">
<pre><code class="language-C++" data-lang="C++">#include&nbsp;&lt;vector&gt;<br />#include&nbsp;&lt;iostream&gt;<br />#include&nbsp;&lt;unordered_map&gt;<br />#include&nbsp;&lt;cstdint&gt;<br />#include&nbsp;&lt;ctime&gt;<br />using&nbsp;namespace&nbsp;std;<br /><br />#define&nbsp;ENTRY_SIZE&nbsp;314&nbsp;<br />#define&nbsp;ENTRY_COUNT&nbsp;(1&lt;&lt;20)*64<br /><br />struct&nbsp;CellEntry<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;void*&nbsp;&nbsp;&nbsp;&nbsp;ptr;<br />&nbsp;&nbsp;&nbsp;&nbsp;int32_t&nbsp;&nbsp;len;<br />&nbsp;&nbsp;&nbsp;&nbsp;uint16_t&nbsp;cellType;<br />};<br />unordered_map&lt;long,&nbsp;CellEntry&gt;&nbsp;LocalStorage;<br /><br />int&nbsp;_tmain(int&nbsp;argc,&nbsp;_TCHAR*&nbsp;argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;srand(time(NULL));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;ENTRY_COUNT;&nbsp;++i)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;id&nbsp;=&nbsp;rand()&nbsp;*&nbsp;rand();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void*&nbsp;p&nbsp;=&nbsp;new&nbsp;char[ENTRY_SIZE];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalStorage[id]&nbsp;=&nbsp;{&nbsp;p,&nbsp;ENTRY_SIZE,&nbsp;/*cellType:*/0&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(rand()&nbsp;%&nbsp;3&nbsp;==&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LocalStorage.erase(id);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete[]&nbsp;p;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />}<br /> </code></pre>
</div>
<p>C# w/ Graph Engine:</p>
<div class="highlight">
<pre><code class="language-C++" data-lang="C++">using&nbsp;System;<br />using&nbsp;System.Collections.Generic;<br />using&nbsp;System.Linq;<br />using&nbsp;System.Text;<br />using&nbsp;System.Threading.Tasks;<br />using&nbsp;Trinity;<br />namespace&nbsp;MemoryConsumptionTest_CSharp<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;internal&nbsp;class&nbsp;Program<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;ENTRY_SIZE&nbsp;=&nbsp;314;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;ENTRY_COUNT&nbsp;=&nbsp;(1&nbsp;&lt;&lt;&nbsp;20)&nbsp;*&nbsp;64;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;r&nbsp;=&nbsp;new&nbsp;Random();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;buf&nbsp;=&nbsp;new&nbsp;byte[ENTRY_SIZE];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TrinityConfig.CurrentRunningMode&nbsp;=&nbsp;RunningMode.Embedded;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;ENTRY_COUNT;&nbsp;++i)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;id&nbsp;=&nbsp;r.Next()&nbsp;*&nbsp;r.Next();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveCell(id,&nbsp;buf,&nbsp;0,&nbsp;ENTRY_SIZE,&nbsp;cellType:&nbsp;0);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(r.Next(3)&nbsp;==&nbsp;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.RemoveCell(id);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /> </code></pre>
</div>
<p>We store cells with a fixed length 314, 64 millions of them. Both C++ and C# will assure that the allocated memory gets committed unlike the malloc() function, which would need a memset() to trigger that. Also, along cell insertions, we randomly remove entries with the probability 1/3... This is to test their behaviors with regard to the memory fragmentations introduced by removals.</p>
<p>If the environment is correctly set up, the two programs should have the same semantics (remember to compile 64-bit programs, and make sure your machine can handle that ~20GB of memory allocation). The following performance data is acquired on a server with Xeon <a href="mailto:X5650@2.67Ghz">X5650@2.67Ghz</a> and 96GB of RAM:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/7851.GE_vs_CPP.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/7851.GE_5F00_vs_5F00_CPP.png"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/7851.GE_vs_CPP.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/320x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/7851.GE_5F00_vs_5F00_CPP.png" alt="" width="403" height="392" border="0" /></a></p>
<p>The "Raw performance data" row shows the screen shots of the task manager memory window during the two runs. Notice that for GE, there's a slight memory usage drop (only about a hundred of megabytes). This is due to the garbage collector running in the background collecting the space taken by the removed entries. I will go into the details of our garbage collector in another post.</p>
<p>As we can see, Graph Engine outperforms the unordered_map in both memory consumption (nearly 2GB less space) and speed (nearly 20 seconds faster).<br />And we're talking about the comparison between a full-fledged key-value store and a data structure...</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Graph Engine, Private Customized: Key-value store with an attitude</title>
      <link>https://docs.microsoft.com/archive/blogs/graphengine/graph-engine-private-customized-key-value-store-with-an-attitude</link>
      <pubDate>Sat, 30 May 2015 03:44:00 GMT</pubDate>
      <dc:creator><![CDATA[yatli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/graphengine/2015/05/30/graph-engine-private-customized-key-value-store-with-an-attitude/</guid>
      <description><![CDATA[Since our preview release announcement, we have been asked about what Graph Engine actually is....]]></description>
      <content:encoded><![CDATA[<p>Since our preview release announcement, we have been asked about what Graph Engine actually is. People want to know, what does GE provide to help them to achieve their goals, and what can they do to customize GE to fit their needs. Of all the interesting questions, today I'll cover the following two of them:</p>
<ol>
<li>Is&nbsp;GE essentially a hosted version of Titan or neo4j?</li>
<li>Can I process non-graph data with GE?</li>
</ol>
<p><strong>For question one:</strong> "no" for "hosted". We do offer the ability to deploy your GE application to the cloud. But there is nothing stopping you to deploy and run it locally. By downloading the SDK from the Visual Studio gallery you have already acquired everything you needed to gear up your local environment, be it standalone or distributed. (Please check out <a href="https://www.graphengine.io/docs/manual/Utilities/geconfig.html">https://www.graphengine.io/docs/manual/Utilities/geconfig.html</a>, we have provided tools to&nbsp;configure a local GE cluster) Still not satisfied? Please move on to:</p>
<p><strong>For question two: </strong>Yes, because you can use the underlying key-value store and the computation engine without introducing the graph model! No hacks or tweaks to the system! Indeed, the architecture of GE is illustrated as follows:<strong> <br /></strong></p>
<p style="text-align: center;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/6038.overview[1].png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/6038.overview_5B00_1_5D00_.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/6038.overview[1].png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/6038.overview_5B00_1_5D00_.png" alt="" width="256" height="260" border="0" /></a></p>
<p style="text-align: left;"><br />However, notice the inherent message: the Graph Model layer is defined with TSL(Trinity Specification Language), so that if you define something non-graph with TSL, your system will become like:</p>
<p style="text-align: center;"><br /><br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/4024.millennium%20falcon%20key%20value%20store.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/4024.millennium-falcon-key-value-store.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/4024.millennium%20falcon%20key%20value%20store.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/4024.millennium-falcon-key-value-store.png" alt="" width="254" height="282" border="0" /></a></p>
<p style="text-align: center;"><em>(Oh look, that makes a good millennium falcon.)</em></p>
<p>The key point here is about the TSL, of which the elements shape what GE really is. It defines both the storage schemata and the communication protocols, without which the GE would fall back to a basic memory cache. In other words, it defines how GE should understand your data. (Of course, if you do not want GE to understand the data, for example, when the data chunks are encrypted, the bare-metal distributed memory cache that GE offers is still a valid option). Let me go further and explain how to turn the ship^H^H^H^H GE into your very-own enhanced key-value store.</p>
<h1>Schema matters.</h1>
<p>There are quite a few options when we talk about the storage organization. For example, some stores allow you to save JSON document, others let you store primitive data structures, etc. These stores do not enforce a strong schema of the data. However, in many practical cases, the data is intrinsically structural. When you have a stream of many entries, it is likely that they will share some properties, which allows us to make an generalization of them. Also, to process the entries, we have to implement logics. The most obvious case is that, for statically-typed languages, people write data-access-objects(DAOs) to establish a connection between entries and runtime objects. Even if your language of choice is dynamically typed, despite not having to explicitly define the data types, logics do not run away -- your code is still data-driven, or you would end up ignoring the non-generalized part of the data. Hopefully the data does not get so complex that one has to leverage machine learning algorithms only for type inference.<br />Next, let's look at the physical storage layout when a data stream hits the storage, which is schema-free, and natively stores JSON objects. Suppose we have the following incoming json object(Taken from Twitter Developers <a href="https://dev.twitter.com/rest/reference/get/search/tweets">https://dev.twitter.com/rest/reference/get/search/tweets</a>):</p>
<div class="highlight">
<pre><code class="language-json" data-lang="json">{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;"statuses":&nbsp;[&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"coordinates":&nbsp;null,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"favorited":&nbsp;false,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"truncated":&nbsp;false,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"created_at":&nbsp;"Mon&nbsp;Sep&nbsp;24&nbsp;03:35:21&nbsp;+0000&nbsp;2012",<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"id_str":&nbsp;"250075927172759552",<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Lots&nbsp;of&nbsp;other&nbsp;properties&nbsp;that&nbsp;fill&nbsp;up&nbsp;5&nbsp;blog&nbsp;pages...&nbsp;*/&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;},<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"coordinates":&nbsp;null,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;And&nbsp;it&nbsp;goes&nbsp;on...&nbsp;for&nbsp;another&nbsp;few&nbsp;pages...&nbsp;*/&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;},<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"coordinates":&nbsp;null,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;And&nbsp;on...&nbsp;*/&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Imaging&nbsp;that&nbsp;there&nbsp;are&nbsp;thousands&nbsp;of&nbsp;this...&nbsp;*/&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;}],&nbsp;/*&nbsp;Other&nbsp;stuff...&nbsp;*/&nbsp;<br />}<br /> </code></pre>
</div>
<p>We can see that, even in a single JSON object, there are a lot of repetitive patterns. In the given example, the most significant member of the JSON object is "statuses", an array list of nested JSON objects, each complying a fixed schema. However, JSON does not enforce a fixed schema so that a JSON store should always store the field names together with their values, and their field types.</p>
<p>Again and again.</p>
<p>This is a typical example of, when one has a data schema, but suffers from it. Overhead (which becomes so significant that it takes up more than a half of the storage space) to store and overhead to retrieve(where one has to enumerate the fields and check by name one after another to get something). We want our users to benefit from the schemata, not to suffer from it. By taking advantage of the schemata defined in the TSL, GE organizes fields in a data entry in a fixed format so that it is not necessary to store the name and data type of each field. Also, during a field retrieval, GE does not have to enumerate all the fields, as it can calculate the offset of the target field according to the predefined data schemata.</p>
<h1>Tables? Collections? Types.</h1>
<p>In a relational database, we group data records of the same type into a table. When we want to find a relation between different types of objects, we join. What options do we have if we leave the relational world and dive into NOSQL? In the following example, I will introduce two types of objects: kung-fu master and apprentice. Let's assume that each character has a name, and additionally, each apprentice can identify his/her master. The sample data set is presented in the following table:</p>
<div class="highlight">
<pre><code class="language-text" data-lang="text">ID&nbsp;&nbsp;&nbsp;&nbsp;Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MasterID&nbsp;&nbsp;&nbsp;&nbsp;Name<br />&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Master&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;&nbsp;Shifu<br />&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Panda<br />&nbsp;3&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Tiger<br />&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Snake<br />&nbsp;5&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Crane<br />&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Monkey<br />&nbsp;7&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;Mantis<br />&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Master&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;&nbsp;Oogway<br />&nbsp;9&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Fenghuang<br />10&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Monkey<br />11&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Junjie<br />12&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Chao<br />13&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Taotie<br />14&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Wushen<br />15&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Thundering&nbsp;Rhino<br />16&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Storming&nbsp;Ox<br />17&nbsp;&nbsp;&nbsp;&nbsp;Apprentice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;Golden&nbsp;Takin<br /> </code></pre>
</div>
<p>With an RDBMS, we can create two tables for master and apprentice respectively, which is a straightforward solution. Each table has its own schema and we can omit the type of the character. Nothing much to worry about.</p>
<p>With a document store, the most intuitive way, if you are coming from a SQL background, is to create two collections and regard each of them as a table in an RDBMS. However, most document stores do not provide cross-collection data access patterns and manual implementation (through map-reduce or client-side logic) of that would have high cost. Instead of thinking about tables, now we have to break the wall and put everything in a single bag. We can store the type of the character as a string to identify them. However, given the absence of "data types", it is unlikely that a document store would provide a facility to focus on a single type of objects, and we have to apply our own filtering logic and use string matching to achieve type discrimination. Not to mention joins, which would probably involve two layers of that.</p>
<p>With a raw key-value store, the situation is worsen, as the ability to handle hierarchical data would be lost, and we have to "flatten" our data (a type of serialization, in some sense) so that each object fits in a single value slot(or we lost atomicity). Be it either a mystical placement logic over a weakly-typed array/dictionary or just plain object-string (de)serialization, the form of the data will drift away from its logical shape. One would end up turning both data and logic into strings with a lot of manual parsing and string concatenations.</p>
<p>What about Graph Engine? As we already know, GE is more than a key-value store with fixed data types. It allows you to define your own data structures, so storing hierarchical data is natively supported, with data access objects(DAOs) automatically generated by the TSL compiler. On the other hand, GE is not a fully-fledged document store so you cannot throw in arbitrary JSON data, which does fit very well in some particular scenarios, and is especially useful when, for example, you have a recursively-defined document and you encode a whole tree into a document(like XML or HTML). So, we can see GE as a storage lying between a primitive key value store and a document store, which is why I call it an "enhanced key-value store" :-) Here is an example of how to model our sample data set:</p>
<div class="highlight">
<pre><code class="language-C#" data-lang="C#">Cell&nbsp;Master<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;Name;<br />}<br /><br />Cell&nbsp;Apprentice<br />{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;CellId&nbsp;MyMaster;<br />&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;Name;<br />}<br /> </code></pre>
</div>
<p>Note that we still have to throw all the data into a single storage and there is no concept like the relational table. However, with data types defined in your schema, we provide type selectors on the storage, acting effectively as tables. Enumerating a type selector will return all the records of that type, plus that you can conduct LINQ queries over them.</p>
<p>From the schema we can see that, our masters are so forgetful that they do not remember their apprentices. Let's see how to help them to get a reunion with the help of a join operation:</p>
<div class="highlight">
<pre><code class="language-C#" data-lang="C#">static&nbsp;void&nbsp;Main(string[]&nbsp;args)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;TrinityConfig.CurrentRunningMode&nbsp;=&nbsp;RunningMode.Embedded;<br />&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;https://kungfupanda.wikia.com/wiki/Kung_Fu_Panda_Wiki&nbsp;*/<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveMaster(1,&nbsp;"Shifu");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(2,&nbsp;1,&nbsp;"Panda");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(3,&nbsp;1,&nbsp;"Tiger");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(4,&nbsp;1,&nbsp;"Snake");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(5,&nbsp;1,&nbsp;"Crane");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(6,&nbsp;1,&nbsp;"Monkey");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(7,&nbsp;1,&nbsp;"Mantis");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveMaster(8,&nbsp;"Oogway");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(9,&nbsp;8,&nbsp;"Fenghuang");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(10,&nbsp;8,&nbsp;"Monkey");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(11,&nbsp;8,&nbsp;"Junjie");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(12,&nbsp;8,&nbsp;"Chao");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(13,&nbsp;8,&nbsp;"Taotie");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(14,&nbsp;8,&nbsp;"Wushen");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(15,&nbsp;8,&nbsp;"Thundering&nbsp;Rhino");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(16,&nbsp;8,&nbsp;"Storming&nbsp;Ox");<br />&nbsp;&nbsp;&nbsp;&nbsp;Global.LocalStorage.SaveApprentice(17,&nbsp;8,&nbsp;"Golden&nbsp;Takin");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;relations&nbsp;=&nbsp;from&nbsp;master&nbsp;in&nbsp;Global.LocalStorage.Master_Selector()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;join&nbsp;apprentice&nbsp;in&nbsp;Global.LocalStorage.Apprentice_Selector()&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;on&nbsp;master.CellID&nbsp;equals&nbsp;apprentice.MyMaster&nbsp;into&nbsp;apprenticeGroup<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;select&nbsp;new&nbsp;{&nbsp;MasterName&nbsp;=&nbsp;master.Name,&nbsp;Apprentices&nbsp;=&nbsp;apprenticeGroup&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;foreach(var&nbsp;relation&nbsp;in&nbsp;relations)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(relation.MasterName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach(var&nbsp;apprentice&nbsp;in&nbsp;relation.Apprentices)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("&gt;&gt;&nbsp;{0}",&nbsp;apprentice.Name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /> </code></pre>
</div>
<p>In the example given above, I first store the sample dataset into the local storage. To get a list of all the apprentices for a kung-fu master, we have to join the two types, which is achieved with a LINQ query over Global.LocalStorage.Master_Selector() and Global.LocalStorage.Apprentice_Selector(): just think of these two selectors as two tables in a relational database.</p>
<h1>Atomicity, more than the usual ones.</h1>
<p><br />Graph Engine comes with atomicity guaranteed. Operations applied to a cell are serialized, and observed in the same order by all readers. What makes it more useful than the usual atomic semantics is that, a user is not restricted to pre-defined operators like GET and PUT. Instead, we allow arbitrary operations to be applied to a cell, so that tasks like atomic increment of a counter, or atomic insertion into a list, can be easily achieved with simple user code. Also, you are not restricted to performing one operation at a time during an atomicity-guaranteed period. For example, during a single atomic "session", you can perform an atomic increment to a counter, an atomic write to a string, and an atomic insert into a list, at the same time, which would likely require a transaction in other NOSQL stores.</p>
<h1>RESTful APIs extend your call stack to the remote side.</h1>
<p>&nbsp;A stock Graph Engine instance does not provide any RESTful APIs. Instead of providing a fixed set of RESTful endpoints, we allow users to roll their own RESTful interfaces declaratively with the help of TSL. For example, you can define protocols to describe GET/PUT interfaces:</p>
<div class="highlight">
<pre><code class="language-C#" data-lang="C#">cell&nbsp;Entry<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;data;<br />}<br />struct&nbsp;GETRequest<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;entry_id;<br />}<br />protocol&nbsp;GET<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;type:HTTP;<br />&nbsp;&nbsp;&nbsp;&nbsp;request:GETRequest;<br />&nbsp;&nbsp;&nbsp;&nbsp;response:Entry;<br />}<br />struct&nbsp;PUTResponse<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;message;<br />}<br />protocol&nbsp;PUT<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;type:HTTP;<br />&nbsp;&nbsp;&nbsp;&nbsp;request:Entry;<br />&nbsp;&nbsp;&nbsp;&nbsp;response:PUTResponse;<br />}<br />server&nbsp;KeyValueStore<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;protocol&nbsp;GET;<br />&nbsp;&nbsp;&nbsp;&nbsp;protocol&nbsp;PUT;<br />}<br /> </code></pre>
</div>
<p>And define their semantics with simple user code:</p>
<div class="highlight">
<pre><code class="language-C#" data-lang="C#">internal&nbsp;class&nbsp;KeyValueStore&nbsp;:&nbsp;KeyValueStoreBase<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;override&nbsp;void&nbsp;GETHandler(GETRequest&nbsp;request,&nbsp;out&nbsp;Entry&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response&nbsp;=&nbsp;Global.LocalStorage.LoadEntry(request.entry_id);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;override&nbsp;void&nbsp;PUTHandler(Entry&nbsp;request,&nbsp;out&nbsp;PUTResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(Global.LocalStorage.SaveEntry(request))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.message&nbsp;=&nbsp;"OK";<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.message&nbsp;=&nbsp;"FAIL";<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /> </code></pre>
</div>
<p>A more comprehensive example, which mimics many of Redis's interfaces can be found here: <a href="https://github.com/jiabaohan/SimpleRedis">https://github.com/jiabaohan/SimpleRedis</a></p>
<p>At the end of this article I'd like to have some extra fun. Note that you can set the request or response type of a RESTful endpoint to "stream" so that you can directly operate on the HTTP stream. I'm going to stream some media through Graph Engine... Like this:</p>
<div class="highlight">
<pre><code class="language-C#" data-lang="C#">struct&nbsp;StreamingRequest<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;filename;&nbsp;&nbsp;&nbsp;&nbsp;<br />}<br /><br />protocol&nbsp;Streaming<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;Type:HTTP;<br />&nbsp;&nbsp;&nbsp;&nbsp;Request:StreamingRequest;<br />&nbsp;&nbsp;&nbsp;&nbsp;Response:stream;&nbsp;&nbsp;&nbsp;&nbsp;<br />}<br /><br />server&nbsp;StreamingServer<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;protocol&nbsp;Streaming;&nbsp;<br />}<br /> </code></pre>
</div>
<p>With a simple handler that pumps media data to the web endpoint:</p>
<div class="highlight">
<pre><code class="language-C#" data-lang="C#">internal&nbsp;class&nbsp;StreamingServer&nbsp;:&nbsp;Trinity.Extension.StreamingServerBase<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;override&nbsp;void&nbsp;StreamingHandler(StreamingRequest&nbsp;request,&nbsp;HttpListenerResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;content&nbsp;=&nbsp;File.ReadAllBytes(request.filename);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.OutputStream.Write(content,&nbsp;0,&nbsp;content.Length);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /> </code></pre>
</div>
<p>Party on!</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/7658.Streaming_media_2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/7658.Streaming_5F00_media_5F00_2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/34/7658.Streaming_media_2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/350x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-34/7658.Streaming_5F00_media_5F00_2.png" alt="" border="0" /></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Graph Engine 1.0 Preview Released</title>
      <link>https://docs.microsoft.com/archive/blogs/graphengine/graph-engine-1-0-preview-released</link>
      <pubDate>Mon, 18 May 2015 10:00:00 GMT</pubDate>
      <dc:creator><![CDATA[binshao@live.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/graphengine/2015/05/18/graph-engine-1-0-preview-released/</guid>
      <description><![CDATA[We are very pleased to announce that the Graph Engine 1.0 preview has finally been released to the...]]></description>
      <content:encoded><![CDATA[<div id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV[3]" class="conM ">
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[0]">We are very pleased to announce that the Graph Engine 1.0 preview has finally been released to the public. Graph Engine, previously known as Trinity, is a distributed, in-memory, large graph processing engine.</p>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[1]">Graphs play an indispensable role in a wide range of domains. Graph processing at scale, however, is facing challenges at all levels, ranging from system architectures to programming models. Underpinned by a strongly-typed RAM store and a general computation engine, Graph Engine helps users build both real-time online query processing applications and high-throughput offline analytics systems with ease.</p>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[2]">Graph Engine&rsquo;s RAM store can manage billions of run-time objects. The RAM store provides efficient memory management with high memory utilization ratios. It takes full advantage of the RAM to speed up data accesses and computations in a way that makes every single byte count.</p>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[3]">As a versatile general-purpose computation engine, Graph Engine is extremely extensible. It provides a unified declarative language for both data modeling and message passing. Instead of using a fixed data schema and hard-wired computation paradigms, it allows users to freely define data schemata and extend the system capability by performing fine-grained message passing.</p>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[4]">Graph Engine is ready to integrate with other system stacks as well. The user-defined APIs can be easily exposed via RESTful interfaces. An application powered by Graph Engine is accessible to any connected device.</p>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[5]">Graph Engine has many built-in features for distributed programming, including:</p>
<ul id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/UL">
<li id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/UL/LI[0]">Declarative data modeling and network programming;</li>
<li id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/UL/LI[1]">Full IntelliSense support;</li>
<li id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/UL/LI[2]">Language-Integrated Query;</li>
<li id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/UL/LI[3]">Remote application deployment, control, monitoring, and debugging.</li>
</ul>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[6]">Graph Engine provides seamless integration with the powerful integrated development environment Visual Studio and Microsoft&rsquo;s cloud computing platform Azure. It offers developers the best experience in both development and deployment on both the local and cloud sides. Developers can create Graph Engine applications in the all-purpose Visual Studio and deploy the applications to the cloud (Azure) with just a few clicks.</p>
<p id="/HTML/BODY/DIV/DIV/DIV/DIV/DIV/DIV/DIV/P[7]">This is our first official public preview. We look forward to feedback and suggestions. We will continue to provide routine maintenance and regular new releases, turning our research ideas into real-life functionality.</p>
<p>To get more information, documents, and downloads, please visit: <a href="https://www.graphengine.io/" target="_blank">https://graphengine.io</a>.</p>
</div>]]></content:encoded>
    </item>
  </channel>
</rss>