<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>sodaware.sdf.org</title> <description></description> <copyright><![CDATA[]]></copyright> <lastBuildDate>Sat, 28 Feb 2026 09:27:19 -0500</lastBuildDate> <pubDate>Sat, 28 Feb 2026 09:27:19 -0500</pubDate> <link>http://sodaware.sdf.org/</link> <atom:link href="http://sodaware.sdf.org/rss2.xml" rel="self" type="application/rss+xml"/> <generator>Jekyll 3.8.5</generator> <item> <title>Building a basic plugin system in BlitzMax (Win32)</title> <pubDate>Sat, 08 Apr 2023 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-plugin-system-win32/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-plugin-system-win32/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>BlitzMax comes with functionality for loading external libraries - these are “.dll” files on Windows, “.so” files on GNU/Linux, and “.dylib” files on MacOS. This functionality can be used to load code from outside of the running application, which is useful for building plugin systems.</p> <p>The method to load libraries is slightly different depending on the platform. This article will concentrate on the Windows way of doing things as it’s the most straightforward. In a future post I’ll go over methods that work on MacOS &amp; Linux.</p> <ul id="markdown-toc"> <li><a href="#what-were-building" id="markdown-toc-what-were-building">What we’re building</a></li> <li><a href="#loading-a-dll-file-in-blitzmax" id="markdown-toc-loading-a-dll-file-in-blitzmax">Loading a DLL file in BlitzMax</a></li> <li><a href="#wrapping-things-up-in-a-type" id="markdown-toc-wrapping-things-up-in-a-type">Wrapping things up in a Type</a></li> <li><a href="#scanning-a-directory-and-loading-plugins" id="markdown-toc-scanning-a-directory-and-loading-plugins">Scanning a directory and loading plugins</a></li> <li><a href="#running-plugin-functions" id="markdown-toc-running-plugin-functions">Running plugin functions</a></li> <li><a href="#creating-a-dll-in-blitzmax" id="markdown-toc-creating-a-dll-in-blitzmax">Creating a DLL in BlitzMax</a></li> <li><a href="#putting-it-all-together" id="markdown-toc-putting-it-all-together">Putting it all together</a></li> </ul> <h2 id="what-were-building">What we’re building</h2> <p>For this exercise we’re going to build a small BlitzMax application that does the following:</p> <ul> <li>Scans a directory for DLL files (plugins).</li> <li>Loads each plugin file.</li> <li>Runs a function in each plugin.</li> <li>Displays the result.</li> </ul> <p>At the end we’ll also create a DLL written in BlitzMax and use our new application to load it.</p> <p>The complete source, as well as the example DLL files, is available at the end of this post.</p> <p>But before we get started, let’s look at the key functions we’ll be using.</p> <h2 id="loading-a-dll-file-in-blitzmax">Loading a DLL file in BlitzMax</h2> <p>The <code class="highlighter-rouge">pub.win32</code> module contains functions for opening DLL files. We’ll be using the following:</p> <ul> <li><a href="https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya">LoadLibraryA</a> :: Loads the DLL file into memory.</li> <li><a href="https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress">GetProcAddress</a> :: Gets the address of a function within the loaded DLL.</li> </ul> <p><code class="highlighter-rouge">LoadLibraryA</code> returns an integer handle to the loaded library (or <code class="highlighter-rouge">false</code> if the library could not be loaded).</p> <p><code class="highlighter-rouge">GetProcAddress</code> returns a <code class="highlighter-rouge">Byte Ptr</code> which BlitzMax can use as a function pointer. This makes it possible to call a function that was defined in the DLL, as well as getting any returned data.</p> <p>In practice it looks a little bit like this:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Import</span> <span class="nn">pub.win32</span>

<span class="c1">' Load the library.</span>
<span class="k">Local</span> <span class="nv">library</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="n">LoadLibraryA</span><span class="p">(</span><span class="s">"library.dll"</span><span class="p">)</span>

<span class="c1">' Get a reference to the function we want to call.</span>
<span class="k">Local</span> <span class="nv">doSomething</span><span class="p">:</span><span class="kt">String</span><span class="p">()</span> <span class="o">=</span> <span class="n">GetProcAddress</span><span class="p">(</span><span class="n">library</span><span class="p">,</span> <span class="s">"do_something"</span><span class="p">)</span>

<span class="c1">' Call the function.</span>
<span class="k">Print</span> <span class="n">doSomething</span><span class="p">()</span>
<span class="c1">' =&gt; This will call `do_something()` in the DLL.</span>
</code></pre></div></div> <p>(The above example assumes “library.dll” has an exported function called <code class="highlighter-rouge">do_something</code>.)</p> <h2 id="wrapping-things-up-in-a-type">Wrapping things up in a Type</h2> <p>Now that we have the basic idea laid out, let’s wrap things up in a <code class="highlighter-rouge">Type</code>. This will make it easier to manage multiple DLL files later on.</p> <p>We’ll create a simple type called <code class="highlighter-rouge">Plugin</code> that contains a <code class="highlighter-rouge">process</code> field. This field will be a function pointer that accepts and returns a string:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Type</span> <span class="nc">Plugin</span>
    <span class="n">Field</span> <span class="n">path</span><span class="p">:</span><span class="kt">String</span>
    <span class="n">Field</span> <span class="n">library</span><span class="p">:</span><span class="kt">Int</span>
    <span class="n">Field</span> <span class="n">process</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">in</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>

    <span class="c1">' Get just file name of the plugin.</span>
    <span class="k">Method</span> <span class="nf">pluginName</span><span class="p">:</span><span class="kt">String</span><span class="p">()</span>
        <span class="k">Return</span> <span class="n">StripDir</span><span class="p">(</span><span class="n">StripExt</span><span class="p">(</span><span class="n">Self</span><span class="p">.</span><span class="n">path</span><span class="p">))</span>
    <span class="k">End</span> <span class="k">Method</span>

    <span class="k">Function</span> <span class="nf">Load</span><span class="p">:</span><span class="n">Plugin</span><span class="p">(</span><span class="n">path</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
        <span class="k">Local</span> <span class="nv">this</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="k">New</span> <span class="n">Plugin</span>

        <span class="n">this</span><span class="p">.</span><span class="n">path</span>    <span class="o">=</span> <span class="n">path</span>
        <span class="n">this</span><span class="p">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">LoadLibraryA</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
        <span class="n">this</span><span class="p">.</span><span class="n">process</span> <span class="o">=</span> <span class="n">GetProcAddress</span><span class="p">(</span><span class="n">this</span><span class="p">.</span><span class="n">library</span><span class="p">,</span> <span class="s">"process_string"</span><span class="p">)</span>

        <span class="k">Return</span> <span class="n">this</span>
    <span class="k">End</span> <span class="k">Function</span>
<span class="k">End</span> <span class="k">Type</span>
</code></pre></div></div> <p>We can now load a plugin and call a function within it with just a few lines:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Local</span> <span class="nv">p</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="n">Plugin</span><span class="p">.</span><span class="n">Load</span><span class="p">(</span><span class="s">"library.dll"</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">process</span><span class="p">(</span><span class="s">"my string"</span><span class="p">)</span>
</code></pre></div></div> <p>This will be enough for our example, but in the future we’d want to be more defensive: there’s no check that the file exists, that it’s a valid DLL file, or that the function pointer is valid.</p> <p>Other improvements would be allowing the library function name (“process_string”) to be customized, and we’d probably want to load multiple functions too.</p> <p>For now we’ll move on to scanning a directory for plugin files.</p> <h2 id="scanning-a-directory-and-loading-plugins">Scanning a directory and loading plugins</h2> <p>There are a couple of ways to do this. We can use <a href="https://blitzmax.org/docs/en/api/brl/brl.filesystem/#function-readdirbyte-ptr-path-">ReadDir</a> and <code class="highlighter-rouge">NextFile</code> to open a directory and read the contents. Alternatively we can use <a href="https://blitzmax.org/docs/en/api/brl/brl.filesystem/#function-loaddir-dirskip_dotsinttrue-">LoadDir</a> to load the contents into a string array.</p> <p>For this example we’ll use <code class="highlighter-rouge">LoadDir</code> as it’s pretty straightforward to implement and we’re assuming that our plugin directory will just contain DLL files.</p> <p><code class="highlighter-rouge">LoadDir</code> does not recurse into sub-directories, so for this example we’ll assume there is only a top-level “plugins” directory. We’ll scan a directory for DLL files, load them into a <code class="highlighter-rouge">Plugin</code> instance, and store them in an array:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Import</span> <span class="nn">brl.filesystem</span>

<span class="k">Local</span> <span class="nv">plugins</span><span class="p">:</span><span class="n">Plugin</span><span class="err">[]</span>
<span class="k">Local</span> <span class="nv">pluginFiles</span><span class="p">:</span><span class="kt">String</span><span class="err">[]</span> <span class="o">=</span> <span class="n">LoadDir</span><span class="p">(</span><span class="s">"plugins"</span><span class="p">)</span>

<span class="k">For</span> <span class="k">Local</span> <span class="nv">fileName</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="ow">EachIn</span> <span class="n">pluginFiles</span>
    <span class="c1">' Load the plugin file.</span>
    <span class="k">Local</span> <span class="nv">p</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="n">Plugin</span><span class="p">.</span><span class="n">Load</span><span class="p">(</span><span class="s">"plugins/"</span> <span class="o">+</span> <span class="n">fileName</span><span class="p">)</span>

    <span class="c1">' Expand the plugins array and add our instance to the end.</span>
    <span class="n">plugins</span> <span class="o">=</span> <span class="n">plugins</span><span class="err">[</span><span class="p">.</span><span class="err">.</span><span class="n">plugins</span><span class="p">.</span><span class="n">Length</span> <span class="o">+</span> <span class="mi">1</span><span class="err">]</span>
    <span class="n">plugins</span><span class="err">[</span><span class="n">plugins</span><span class="p">.</span><span class="n">length</span> <span class="o">-</span> <span class="mi">1</span><span class="err">]</span> <span class="o">=</span> <span class="n">p</span>
<span class="k">Next</span>
</code></pre></div></div> <p>Now that we have our array of loaded plugins, let’s use them to do something.</p> <h2 id="running-plugin-functions">Running plugin functions</h2> <p>To keep things simple our plugin files will contain a single function called <code class="highlighter-rouge">process_string</code>. This function will accept a string, manipulate it in some way, and then return the result (we’ll also build a plugin using BlitzMax to test this).</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">' Create our string and print it.</span>
<span class="k">Local</span> <span class="nv">ourString</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="s">"Hello, World!"</span>
<span class="k">Print</span> <span class="s">"[initial] "</span> <span class="o">+</span> <span class="n">ourString</span>

<span class="c1">' Run each plugin on the string.</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">p</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="ow">EachIn</span> <span class="n">plugins</span>
    <span class="n">ourString</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">process</span><span class="p">(</span><span class="n">ourString</span><span class="p">)</span>

    <span class="k">Print</span> <span class="s">"["</span> <span class="o">+</span> <span class="n">p</span><span class="p">.</span><span class="n">pluginName</span><span class="p">()</span> <span class="o">+</span> <span class="s">"] "</span> <span class="o">+</span> <span class="n">ourString</span>
<span class="k">Next</span>
</code></pre></div></div> <p>This will give us an output that looks something like this:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[initial] Hello, World!
[downcase] hello, world!
[reverse] !dlorw ,olleh
</code></pre></div></div> <p>Pretty neat! You’re not just limited to using basic data types either; you can use any BlitzMax object, including custom types.</p> <h2 id="creating-a-dll-in-blitzmax">Creating a DLL in BlitzMax</h2> <p>The original BlitzMax supports building applications and modules, but not libraries. Thankfully <a href="https://github.com/bmx-ng/bmx-ng">bmx-ng</a> added a <code class="highlighter-rouge">makelib</code> command which can create libraries from BlitzMax code.</p> <p>We’ll create a simple DLL to convert a string to lower-case:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">' downcase.bmx</span>
<span class="n">SuperStrict</span>

<span class="c1">' Don't want the DLL to import everything.</span>
<span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="k">Function</span> <span class="nf">process_string</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">in</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
    <span class="k">Return</span> <span class="n">in</span><span class="p">.</span><span class="n">ToLower</span><span class="p">()</span>
<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> <p>And compile it using <code class="highlighter-rouge">bmk</code>:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bmk makelib <span class="nt">-r</span> downcase.bmx
</code></pre></div></div> <p>DLL files compiled with BlitzMax also need a <code class="highlighter-rouge">def</code> file that contains their exported functions. <code class="highlighter-rouge">bmk makelib</code> will generate this for you, but if you want to manually create it you can. For our example it would look like this:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>EXPORTS
process_string=bb_process_string
</code></pre></div></div> <p>BlitzMax adds a <code class="highlighter-rouge">bb_</code> prefix to compiled functions, so remember to add that when exporting.</p> <p>More information is available here: <a href="https://blitzmax.org/docs/en/language/creating_dlls/">Creating DLLs</a>.</p> <h2 id="putting-it-all-together">Putting it all together</h2> <p>We now have our application, along with a DLL file that can be loaded as a plugin. The finished application looks like this:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SuperStrict</span>

<span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="k">Import</span> <span class="nn">pub.win32</span>
<span class="k">Import</span> <span class="nn">brl.filesystem</span>

<span class="c1">' Load all plugins.</span>
<span class="k">Local</span> <span class="nv">plugins</span><span class="p">:</span><span class="n">Plugin</span><span class="err">[]</span>
<span class="k">Local</span> <span class="nv">pluginFiles</span><span class="p">:</span><span class="kt">String</span><span class="err">[]</span> <span class="o">=</span> <span class="n">LoadDir</span><span class="p">(</span><span class="s">"plugins"</span><span class="p">)</span>

<span class="k">For</span> <span class="k">Local</span> <span class="nv">fileName</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="ow">EachIn</span> <span class="n">pluginFiles</span>
    <span class="c1">' Load the plugin file.</span>
    <span class="k">Local</span> <span class="nv">p</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="n">Plugin</span><span class="p">.</span><span class="n">Load</span><span class="p">(</span><span class="s">"plugins/"</span> <span class="o">+</span> <span class="n">fileName</span><span class="p">)</span>

    <span class="c1">' Expand the plugins array and add our instance to the end.</span>
    <span class="n">plugins</span> <span class="o">=</span> <span class="n">plugins</span><span class="err">[</span><span class="p">.</span><span class="err">.</span><span class="n">plugins</span><span class="p">.</span><span class="n">Length</span> <span class="o">+</span> <span class="mi">1</span><span class="err">]</span>
    <span class="n">plugins</span><span class="err">[</span><span class="n">plugins</span><span class="p">.</span><span class="n">length</span> <span class="o">-</span> <span class="mi">1</span><span class="err">]</span> <span class="o">=</span> <span class="n">p</span>
<span class="k">Next</span>

<span class="c1">' Create our string and print it.</span>
<span class="k">Local</span> <span class="nv">ourString</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="s">"Hello, World!"</span>
<span class="k">Print</span> <span class="s">"[initial] "</span> <span class="o">+</span> <span class="n">ourString</span>

<span class="c1">' Run each plugin on the string.</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">p</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="ow">EachIn</span> <span class="n">plugins</span>
    <span class="n">ourString</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">process</span><span class="p">(</span><span class="n">ourString</span><span class="p">)</span>

    <span class="k">Print</span> <span class="s">"["</span> <span class="o">+</span> <span class="n">p</span><span class="p">.</span><span class="n">pluginName</span><span class="p">()</span> <span class="o">+</span> <span class="s">"] "</span> <span class="o">+</span> <span class="n">ourString</span>
<span class="k">Next</span>

<span class="k">Type</span> <span class="nc">Plugin</span>
    <span class="n">Field</span> <span class="n">path</span><span class="p">:</span><span class="kt">String</span>
    <span class="n">Field</span> <span class="n">library</span><span class="p">:</span><span class="kt">Int</span>
    <span class="n">Field</span> <span class="n">process</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">in</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>

    <span class="c1">' Get just file name of the plugin.</span>
    <span class="k">Method</span> <span class="nf">pluginName</span><span class="p">:</span><span class="kt">String</span><span class="p">()</span>
        <span class="k">Return</span> <span class="n">StripDir</span><span class="p">(</span><span class="n">StripExt</span><span class="p">(</span><span class="n">Self</span><span class="p">.</span><span class="n">path</span><span class="p">))</span>
    <span class="k">End</span> <span class="k">Method</span>

    <span class="k">Function</span> <span class="nf">Load</span><span class="p">:</span><span class="n">Plugin</span><span class="p">(</span><span class="n">path</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
        <span class="k">Local</span> <span class="nv">this</span><span class="p">:</span><span class="n">Plugin</span> <span class="o">=</span> <span class="k">New</span> <span class="n">Plugin</span>

        <span class="n">this</span><span class="p">.</span><span class="n">path</span>    <span class="o">=</span> <span class="n">path</span>
        <span class="n">this</span><span class="p">.</span><span class="n">library</span> <span class="o">=</span> <span class="n">LoadLibraryA</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
        <span class="n">this</span><span class="p">.</span><span class="n">process</span> <span class="o">=</span> <span class="n">GetProcAddress</span><span class="p">(</span><span class="n">this</span><span class="p">.</span><span class="n">library</span><span class="p">,</span> <span class="s">"process_string"</span><span class="p">)</span>

        <span class="k">Return</span> <span class="n">this</span>
    <span class="k">End</span> <span class="k">Function</span>
<span class="k">End</span> <span class="k">Type</span>
</code></pre></div></div> <p>The full source can be viewed on github: <a href="https://github.com/Sodaware/blitzmax-plugin-example">sodaware/blitzmax-plugin-example</a> or <a href="https://github.com/Sodaware/blitzmax-plugin-example/archive/refs/tags/version-1.0.0.zip">downloaded directly</a>.</p> ]]></description> </item> <item> <title>Finding duplicate BlitzMax code with simian</title> <pubDate>Sun, 26 Apr 2020 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-duplicate-code-simian/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-duplicate-code-simian/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>I’ve recently started using <a href="https://simian.quandarypeak.com/">simian</a> to track down duplicate code blocks in my BlitzMax projects. Although simian doesn’t come with BlitzMax support built-in, it’s possible to use some of the other settings to work with bmx code.</p> <h2 id="examples">Examples</h2> <p>I have <code class="highlighter-rouge">java -jar simian.jar</code> aliased to just <code class="highlighter-rouge">simian</code> so it’s easier to read.</p> <p>An example run looks a little like this (scanning <a href="https://github.com/Sodaware/pangolin.mod/tree/master/contentdb.mod">pangolin/contentdb</a>):</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>simian <span class="nt">-excludes</span><span class="o">=</span><span class="s2">"**/*.tests.bmx"</span> <span class="nt">-language</span><span class="o">=</span>vb <span class="nt">-threshold</span><span class="o">=</span>5 <span class="k">**</span>/<span class="k">*</span>.bmx

Similarity Analyser 2.5.10 - http://www.harukizaemon.com/simian
Copyright <span class="o">(</span>c<span class="o">)</span> 2003-2018 Simon Harris.  All rights reserved.
Simian is not free unless used solely <span class="k">for </span>non-commercial or evaluation purposes.
<span class="o">{</span><span class="nv">failOnDuplication</span><span class="o">=</span><span class="nb">true</span>, <span class="nv">ignoreCharacterCase</span><span class="o">=</span><span class="nb">true</span>, <span class="nv">ignoreCurlyBraces</span><span class="o">=</span><span class="nb">true</span>, <span class="nv">ignoreIdentifierCase</span><span class="o">=</span><span class="nb">true</span>, <span class="nv">ignoreModifiers</span><span class="o">=</span><span class="nb">true</span>, <span class="nv">ignoreStringCase</span><span class="o">=</span><span class="nb">true</span>, <span class="nv">language</span><span class="o">=</span>VB, <span class="nv">threshold</span><span class="o">=</span>5<span class="o">}</span>
Found 5 duplicate lines with fingerprint 58349a293c079cddfe30357e667a5aa4 <span class="k">in </span>the following files:
 Between lines 193 and 206 <span class="k">in </span>pangolin.mod/contentdb.mod/src/entity_template.bmx
 Between lines 151 and 163 <span class="k">in </span>pangolin.mod/contentdb.mod/src/component_schema.bmx
Found 7 duplicate lines with fingerprint 285244b124a3a29e5c65d4106d0aba7d <span class="k">in </span>the following files:
 Between lines 57 and 70 <span class="k">in </span>pangolin.mod/contentdb.mod/src/component_schema.bmx
 Between lines 81 and 96 <span class="k">in </span>pangolin.mod/contentdb.mod/src/component_field.bmx
Found 24 duplicate lines <span class="k">in </span>4 blocks <span class="k">in </span>3 files
Processed a total of 748 significant <span class="o">(</span>1598 raw<span class="o">)</span> lines <span class="k">in </span>9 files
Processing <span class="nb">time</span>: 0.099sec
</code></pre></div></div> <p>Analysis is quick; the entire pangolin project takes under a second to check.</p> <h3 id="treating-blitzmax-code-as-plaintext">Treating BlitzMax code as plaintext</h3> <p>By default simian treats files with unknown extensions as plaintext.</p> <p>The following will find files in the “src” directory that have more than 6 lines duplicated:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>simian src/<span class="k">**</span>/<span class="k">*</span>.bmx
</code></pre></div></div> <h3 id="ignoring-blitzmax-comments">Ignoring BlitzMax comments</h3> <p>Plaintext is enough for most use cases, but it also includes comments in duplication checks. To exclude comments, we can set the language to Visual Basic as it uses the same <code class="highlighter-rouge">'</code> comment syntax as BlitzMax.</p> <p>The adjusted version will now ignore duplicated comments:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>simian <span class="nt">-language</span><span class="o">=</span>vb src/<span class="k">**</span>/<span class="k">*</span>.bmx
</code></pre></div></div> <h3 id="adjusting-the-threshold">Adjusting the threshold</h3> <p>The <code class="highlighter-rouge">-threshold</code> parameter can be used to adjust the number of copied lines that trigger a copy warning.</p> <p>The following will trigger a warning if 5 lines or more match in separate files:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>simian <span class="nt">-language</span><span class="o">=</span>vb <span class="nt">-threshold</span><span class="o">=</span>5 src/<span class="k">**</span>/<span class="k">*</span>.bmx
</code></pre></div></div> ]]></description> </item> <item> <title>BlitzMax optimization - Variable comparisons</title> <pubDate>Sat, 16 Nov 2019 00:00:00 -0500</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-optimization-comparisons/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-optimization-comparisons/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>BlitzMax comes with several built-in data types:</p> <ul> <li><code class="highlighter-rouge">Double</code></li> <li><code class="highlighter-rouge">Long</code></li> <li><code class="highlighter-rouge">Int</code></li> <li><code class="highlighter-rouge">Short</code></li> <li><code class="highlighter-rouge">Byte</code></li> <li><code class="highlighter-rouge">Float</code></li> <li><code class="highlighter-rouge">String</code></li> <li><code class="highlighter-rouge">Object</code></li> </ul> <p>How fast are comparisons for different types? Is using a <code class="highlighter-rouge">String</code> for a variable much faster or slower than an <code class="highlighter-rouge">Int</code>?</p> <h2 id="summary">Summary</h2> <p>Smaller data types such as <code class="highlighter-rouge">Byte</code> and <code class="highlighter-rouge">Short</code> are roughly twice as fast to directly compare than <code class="highlighter-rouge">Double</code> and <code class="highlighter-rouge">Long</code>.</p> <p><code class="highlighter-rouge">String</code> is the slowest comparison, but string length does not affect how long it takes to compare.</p> <h2 id="equality-benchmarks">Equality benchmarks</h2> <p>All benchmarks are in release mode with threading enabled. 1,000,000,000 iterations.</p> <table class="table--striped table--fancy table--benchmark"> <thead> <tr> <th>Check Type</th> <th>Execution Time (milliseconds)</th> </tr> </thead> <tbody> <tr> <td>Double</td> <td>2462</td> </tr> <tr> <td>Long</td> <td>2771</td> </tr> <tr> <td>Integer</td> <td>1565</td> </tr> <tr> <td>Short</td> <td>1253</td> </tr> <tr> <td>Byte</td> <td>1247</td> </tr> <tr> <td>Float</td> <td>2493</td> </tr> <tr> <td>String</td> <td>4099</td> </tr> <tr> <td>Long string</td> <td>4102</td> </tr> <tr> <td>Object</td> <td>1549</td> </tr> </tbody> </table> <h2 id="test-code">Test code</h2> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SuperStrict</span>
<span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="n">SeedRnd</span><span class="p">(</span><span class="n">MilliSecs</span><span class="p">())</span>

<span class="k">Const</span> <span class="nv">ITERATIONS</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1000000000</span>

<span class="k">Local</span> <span class="nv">startTime</span><span class="p">:</span><span class="kt">Int</span>
<span class="k">Local</span> <span class="nv">result</span><span class="p">:</span><span class="kt">Byte</span>

<span class="c1">' ----------------------------------------</span>
<span class="c1">' -- Equality Checks</span>

<span class="k">Local</span> <span class="nv">dval1</span><span class="p">:</span><span class="kt">Double</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">dval2</span><span class="p">:</span><span class="kt">Double</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">dval1</span> <span class="o">=</span> <span class="n">dval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Double check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">lval1</span><span class="p">:</span><span class="kt">Long</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">lval2</span><span class="p">:</span><span class="kt">Long</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">lval1</span> <span class="o">=</span> <span class="n">lval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Long check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">ival1</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">ival2</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">ival1</span> <span class="o">=</span> <span class="n">ival2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Integer check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">sval1</span><span class="p">:</span><span class="kt">Short</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">sval2</span><span class="p">:</span><span class="kt">Short</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">sval1</span> <span class="o">=</span> <span class="n">sval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Short check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">bval1</span><span class="p">:</span><span class="kt">Byte</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">bval2</span><span class="p">:</span><span class="kt">Byte</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">bval1</span> <span class="o">=</span> <span class="n">bval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Byte check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">fval1</span><span class="p">:</span><span class="kt">Float</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">fval2</span><span class="p">:</span><span class="kt">Float</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">fval1</span> <span class="o">=</span> <span class="n">fval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Float check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">stval1</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">),</span> <span class="n">stval2</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">stval1</span> <span class="o">=</span> <span class="n">stval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"String check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">lstval1</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="n">MakeRandomLongString</span><span class="p">()</span>
<span class="k">Local</span> <span class="nv">lstval2</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="n">MakeRandomLongString</span><span class="p">()</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">lstval1</span> <span class="o">=</span> <span class="n">lstval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Long string check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' ----------</span>

<span class="k">Local</span> <span class="nv">obval1</span><span class="p">:</span><span class="kt">Object</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)),</span> <span class="n">obval2</span><span class="p">:</span><span class="kt">Object</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="n">Rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span>

<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">obval1</span> <span class="o">=</span> <span class="n">obval2</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Object check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="k">Function</span> <span class="nf">MakeRandomLongString</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">length</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">255</span><span class="p">)</span>
    <span class="k">Local</span> <span class="nv">result</span><span class="p">:</span><span class="kt">String</span> <span class="o">=</span> <span class="s">""</span>

    <span class="k">For</span> <span class="k">Local</span> <span class="nv">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">To</span> <span class="n">length</span>
        <span class="n">result</span> <span class="p">:</span><span class="o">+</span> <span class="n">Chr</span><span class="p">(</span><span class="n">Rand</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="mi">64</span><span class="p">))</span>
    <span class="k">Next</span>

    <span class="k">Return</span> <span class="n">result</span>
<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> ]]></description> </item> <item> <title>BlitzMax optimization - Loops</title> <pubDate>Thu, 30 May 2019 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-optimization-loops/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-optimization-loops/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>BlitzMax supports the following types of loop:</p> <ul> <li><code class="highlighter-rouge">For...Next</code></li> <li><code class="highlighter-rouge">While...Wend</code></li> <li><code class="highlighter-rouge">Repeat...Until</code></li> </ul> <h2 id="summary">Summary</h2> <p>The different types of loops are almost identical in performance.</p> <p><code class="highlighter-rouge">Repeat...Until</code> loops are slightly faster, but it’s unlikely to be a huge benefit to rewrite everything. It’s far more effective to optimize whatever is being called inside the loop.</p> <p>One slight performance boost was to use a <code class="highlighter-rouge">&lt;&gt;</code> check when using while loops, rather than a <code class="highlighter-rouge">&lt;</code> check. For example:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">While</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="mi">10</span>
    <span class="c1">' Code here</span>
<span class="k">Wend</span>

<span class="k">While</span> <span class="n">x</span> <span class="o">&lt;&gt;</span> <span class="mi">10</span>
    <span class="c1">' Code here</span>
<span class="k">Wend</span>
</code></pre></div></div> <p>The second loop will be slightly faster.</p> <h2 id="loop-calculations">Loop calculations</h2> <p>One important difference between the three types of loop is how limits are checked when using a function.</p> <p>For example:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">' Will print "Called" once.</span>
<span class="k">Print</span> <span class="s">"For...Next"</span>
<span class="k">For</span> <span class="k">Local</span> <span class="nv">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">to</span> <span class="n">getNumber</span><span class="p">()</span>
<span class="k">Next</span>

<span class="c1">' Will print "Called" 5 times.</span>
<span class="k">Print</span> <span class="s">"While...Wend"</span>
<span class="k">Local</span> <span class="nv">w</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">While</span> <span class="n">w</span> <span class="o">&lt;</span> <span class="n">getNumber</span><span class="p">()</span>
    <span class="n">w</span> <span class="p">:</span><span class="o">+</span> <span class="mi">1</span>
<span class="k">Wend</span>

<span class="k">Function</span> <span class="nf">getNumber</span><span class="p">:</span><span class="kt">Int</span><span class="p">()</span>
    <span class="k">Print</span> <span class="s">"Called"</span>
    
    <span class="k">Return</span> <span class="mi">5</span>
<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> <p>The <code class="highlighter-rouge">For...Next</code> loop will only execute <code class="highlighter-rouge">getNumber</code> once. However, the <code class="highlighter-rouge">While...Wend</code> loop will call it every loop iteration. The number should be stored in a variable unless it must be recalculated every loop.</p> <h2 id="loop-benchmarks">Loop benchmarks</h2> <p>All benchmarks are in release mode with threading enabled. 1,000,000,000 iterations.</p> <table class="table--striped table--fancy table--benchmark"> <thead> <tr> <th>Check Type</th> <th>Execution Time (milliseconds)</th> </tr> </thead> <tbody> <tr> <td>For…Next</td> <td>3043</td> </tr> <tr> <td>While…Wend</td> <td>3034</td> </tr> <tr> <td>While…Wend (Not Equal)</td> <td>2766</td> </tr> <tr> <td>Repeat…Until</td> <td>2730</td> </tr> </tbody> </table> <h2 id="test-code">Test code</h2> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SuperStrict</span>

<span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="k">Const</span> <span class="nv">ITERATIONS</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1000000000</span>

<span class="k">Local</span> <span class="nv">startTime</span><span class="p">:</span><span class="kt">Int</span>

<span class="c1">' For/Next</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
	<span class="n">DoSomething</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"For/Next: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 


<span class="c1">' While/Wend</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">Local</span> <span class="nv">whilePos</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">While</span> <span class="n">whilePos</span> <span class="o">&lt;</span> <span class="n">ITERATIONS</span>
	<span class="n">DoSomething</span><span class="p">(</span><span class="n">whilePos</span><span class="p">)</span>
	<span class="n">whilePos</span><span class="p">:</span><span class="o">+</span> <span class="mi">1</span>
<span class="k">Wend</span>
<span class="n">print</span> <span class="s">"While/Wend: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 


<span class="c1">' While/Wend (Not Equal check)</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="n">whilePos</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">While</span> <span class="n">whilePos</span> <span class="o">&lt;&gt;</span> <span class="n">ITERATIONS</span>
	<span class="n">DoSomething</span><span class="p">(</span><span class="n">whilePos</span><span class="p">)</span>
	<span class="n">whilePos</span><span class="p">:</span><span class="o">+</span> <span class="mi">1</span>
<span class="k">Wend</span>
<span class="n">print</span> <span class="s">"While/Wend (NE): "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 


<span class="c1">' Repeat/Until</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">Local</span> <span class="nv">repeatPos</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">Repeat</span>
	<span class="n">DoSomething</span><span class="p">(</span><span class="n">repeatPos</span><span class="p">)</span>
	<span class="n">repeatPos</span><span class="p">:</span><span class="o">+</span> <span class="mi">1</span>
<span class="n">Until</span> <span class="n">repeatPos</span> <span class="o">=</span> <span class="n">ITERATIONS</span>
<span class="n">print</span> <span class="s">"Repeat/Until: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span>

<span class="k">Function</span> <span class="nf">DoSomething</span><span class="p">(</span><span class="n">arg1</span><span class="p">:</span><span class="kt">Int</span><span class="p">)</span>
<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> ]]></description> </item> <item> <title>BlitzMax optimization - Null checks</title> <pubDate>Fri, 17 May 2019 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-optimization-null-checks/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-optimization-null-checks/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>BlitzMax has a couple of ways to check if a variable is <code class="highlighter-rouge">Null</code>:</p> <ul> <li>Null - <code class="highlighter-rouge">If o = Null</code></li> <li>Null (Yoda) - <code class="highlighter-rouge">If Null = o</code></li> <li>Not - <code class="highlighter-rouge">If Not o</code></li> <li>Not (brackets) - <code class="highlighter-rouge">If Not(o)</code></li> <li>Not (explicit) - <code class="highlighter-rouge">If Not(o) = True</code></li> <li>No check - <code class="highlighter-rouge">If o</code></li> </ul> <h2 id="summary">Summary</h2> <p>There’s not a huge amount of performance difference between most checks. Calling <code class="highlighter-rouge">Not</code> adds a very small amount of overhead, and explicit <code class="highlighter-rouge">Not</code> checks are about 2x slower than everything else.</p> <p>It’s probably not worth rebuilding code unless it’s being run millions of times.</p> <h2 id="null-check-benchmarks">Null check benchmarks</h2> <p>All benchmarks are in release mode with threading enabled. 1,000,000,000 iterations.</p> <table class="table--striped table--fancy table--benchmark"> <thead> <tr> <th>Check Type</th> <th>Execution Time (milliseconds)</th> </tr> </thead> <tbody> <tr> <td>Null</td> <td>754</td> </tr> <tr> <td>Null (Yoda)</td> <td>754</td> </tr> <tr> <td>Not</td> <td>889</td> </tr> <tr> <td>Not (brackets)</td> <td>885</td> </tr> <tr> <td>Not (explicit)</td> <td>2082</td> </tr> <tr> <td>No check</td> <td>752</td> </tr> </tbody> </table> <h2 id="test-code">Test code</h2> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SuperStrict</span>
<span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="k">Const</span> <span class="nv">ITERATIONS</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1000000000</span>

<span class="k">Local</span> <span class="nv">startTime</span><span class="p">:</span><span class="kt">Int</span>
<span class="k">Local</span> <span class="nv">counter</span><span class="p">:</span><span class="n">int</span>

<span class="k">Local</span> <span class="nv">result</span><span class="p">:</span><span class="n">int</span>
<span class="k">Local</span> <span class="nv">o</span><span class="p">:</span><span class="kt">Object</span>

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">if</span> <span class="n">o</span> <span class="o">=</span> <span class="n">Null</span> <span class="n">then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Null: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">if</span> <span class="n">Null</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Null (Yoda): "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">if</span> <span class="n">not</span> <span class="n">o</span> <span class="n">then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Not: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">if</span> <span class="n">not</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="n">then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Not (brackets): "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">if</span> <span class="n">not</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="o">=</span> <span class="n">true</span> <span class="n">then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="n">print</span> <span class="s">"Not (explicit): "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' Opposite of a Null check</span>

<span class="n">o</span> <span class="o">=</span> <span class="s">"d"</span>

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="k">If</span> <span class="n">o</span> <span class="k">Then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="k">Print</span> <span class="s">"No check: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="n">result</span> <span class="o">=</span> <span class="n">false</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
    <span class="n">if</span> <span class="p">(</span><span class="n">Null</span> <span class="o">=</span> <span class="n">o</span><span class="p">)</span> <span class="o">=</span> <span class="n">false</span> <span class="n">then</span> <span class="n">result</span> <span class="o">=</span> <span class="n">true</span>
<span class="k">Next</span>
<span class="k">Print</span> <span class="s">"No check (double check): "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 
</code></pre></div></div> ]]></description> </item> <item> <title>BlitzMax optimization - Resizing arrays</title> <pubDate>Sun, 14 Apr 2019 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-optimization-arrays/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-optimization-arrays/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>BlitzMax comes with a number of data structures. Arrays are a staple of the BASIC dialect, but they can be a pain to use if items need to be frequently added and removed. BlitzMax’s <code class="highlighter-rouge">TList</code> structure is more flexible, but comes with a <a href="/blog/blitzmax-optimization-casting/">performance hit when casting objects</a>.</p> <h2 id="resizing-arrays">Resizing arrays</h2> <p>Normally I’d do something like this to resize an array:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="c1">' Create initial array.</span>
<span class="k">Local</span> <span class="nv">original</span><span class="p">:</span><span class="kt">Int</span><span class="err">[]</span> <span class="o">=</span> <span class="err">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="err">]</span>
<span class="k">Print</span> <span class="n">original</span><span class="p">.</span><span class="n">length</span>
<span class="c1">' =&gt; 5</span>

<span class="c1">' Resize to hold 6 elements.</span>
<span class="n">original</span> <span class="o">=</span> <span class="n">ResizeArray</span><span class="p">(</span><span class="n">original</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
<span class="k">Print</span> <span class="n">original</span><span class="p">.</span><span class="n">length</span>
<span class="c1">' =&gt; 6</span>

<span class="k">Function</span> <span class="nf">ResizeArray</span><span class="p">:</span><span class="kt">Int</span><span class="err">[]</span><span class="p">(</span><span class="n">target</span><span class="p">:</span><span class="kt">Int</span><span class="err">[]</span><span class="p">,</span> <span class="n">newSize</span><span class="p">:</span><span class="kt">Int</span><span class="p">)</span>
    <span class="k">Local</span> <span class="nv">dest</span><span class="p">:</span><span class="kt">Int</span><span class="err">[</span><span class="n">newSize</span><span class="err">]</span>

    <span class="k">For</span> <span class="k">Local</span> <span class="nv">i</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">to</span> <span class="n">target</span><span class="p">.</span><span class="n">Length</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">dest</span><span class="err">[</span><span class="n">i</span><span class="err">]</span> <span class="o">=</span> <span class="n">target</span><span class="err">[</span><span class="n">i</span><span class="err">]</span>
    <span class="k">Next</span>
    
    <span class="k">Return</span> <span class="n">dest</span>
<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> <p>It works, but it’s not the most efficient. Things slow down when the target array is large. The main <code class="highlighter-rouge">For</code> loop can be unrolled or converted to a <code class="highlighter-rouge">Repeat...Until</code> loop, but that’s a small optimization. It would be nice to remove the loop entirely.</p> <p><a href="https://github.com/bmx-ng/bmk">bmk-ng</a> comes with a handy function called <code class="highlighter-rouge">ArrayCopy</code> for copying the contents of one array to another.</p> <p>I wrote a <a href="https://gist.github.com/Sodaware/2a4a5ae46d45d0f64a8e50af7c260623">quick performance test</a> to compare <code class="highlighter-rouge">ArrayCopy</code> against copying elements using a <code class="highlighter-rouge">For...Next</code> loop. It’s probably not that accurate, but the results are encouraging.</p> <p>Time to copy a 1,000 element array 1,000,000 times:</p> <table class="table table--fancy table--striped"> <thead> <tr> <th style="text-align: left">Method</th> <th style="text-align: right">Time (millisecs)</th> </tr> </thead> <tbody> <tr> <td style="text-align: left">Copy (<code class="highlighter-rouge">For...Next</code>)</td> <td style="text-align: right">2249</td> </tr> <tr> <td style="text-align: left"><code class="highlighter-rouge">ArrayCopy</code></td> <td style="text-align: right">1127</td> </tr> <tr> <td style="text-align: left">Object Copy (<code class="highlighter-rouge">For...Next</code>)</td> <td style="text-align: right">3264</td> </tr> <tr> <td style="text-align: left">Object <code class="highlighter-rouge">ArrayCopy</code></td> <td style="text-align: right">2152</td> </tr> </tbody> </table> <p>For simple Integer arrays, <code class="highlighter-rouge">ArrayCopy</code> is about twice as fast as copying using a <code class="highlighter-rouge">For...Next</code> loop. Copying objects is slower, but still shows a good speed improvement. Nice!</p> <p>But I wondered if there was a better way to implement resizing.</p> <p>BlitzMax has built-in (and mostly undocumented) functionality for <a href="https://blitzmax.org/docs/en/language/arrays/#copying-an-array">copying and resizing arrays using “slices”</a>.</p> <p>Instead of creating a larger array and copying the contents, resizing can be done in a single command:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Local</span> <span class="nv">myArray</span><span class="p">:</span><span class="kt">Int</span><span class="err">[</span><span class="mi">10</span><span class="err">]</span>

<span class="c1">' Resize array to be 20 elements.</span>
<span class="n">myArray</span> <span class="o">=</span> <span class="n">myArray</span><span class="err">[</span><span class="p">.</span><span class="err">.20]</span>
</code></pre></div></div> <p>Copying can be done in a single line too:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Local</span> <span class="nv">target</span><span class="p">:</span><span class="kt">Int</span><span class="err">[</span><span class="mi">10</span><span class="err">]</span>
<span class="k">Local</span> <span class="nv">destination</span><span class="p">:</span><span class="kt">Int</span><span class="err">[</span><span class="mi">10</span><span class="err">]</span>

<span class="n">destination</span> <span class="o">=</span> <span class="n">myArray</span><span class="err">[</span><span class="p">.</span><span class="err">.]</span>
</code></pre></div></div> <p>Using slices reduces the <code class="highlighter-rouge">ResizeArray</code> method to this:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Method</span> <span class="nf">ResizeArray</span><span class="p">:</span><span class="kt">Int</span><span class="err">[]</span><span class="p">(</span><span class="n">target</span><span class="p">:</span><span class="kt">Int</span><span class="err">[]</span><span class="p">,</span> <span class="n">newCapacity</span><span class="p">:</span><span class="kt">Int</span><span class="p">)</span>
    <span class="k">Return</span> <span class="n">target</span><span class="err">[</span><span class="p">.</span><span class="err">.</span><span class="n">newCapacity</span><span class="err">]</span>
<span class="k">End</span> <span class="k">Method</span>
</code></pre></div></div> <p>I’ve resized arrays using <code class="highlighter-rouge">For...Next</code> loops in a few of my modules. <a href="https://github.com/Sodaware/sodaware.mod/blob/1847b8ecf53754f3452a6595546a87beb058ddb5/objectbag.mod/src/object_bag.bmx#L316">ObjectBag#_grow</a> used it when resizing the internal storage array.</p> <p>A <a href="https://gist.github.com/Sodaware/ed1151972c586a6b62facbe986de2fef">quick performance test</a> gave the following results for adding 10,000 items to a bag 10,000 times:</p> <table class="table table--fancy table--striped"> <thead> <tr> <th style="text-align: left">Method</th> <th style="text-align: right">Time (millisecs)</th> </tr> </thead> <tbody> <tr> <td style="text-align: left">Original <code class="highlighter-rouge">_grow</code></td> <td style="text-align: right">821</td> </tr> <tr> <td style="text-align: left">ArrayCopy <code class="highlighter-rouge">_grow</code></td> <td style="text-align: right">559</td> </tr> <tr> <td style="text-align: left">Slices <code class="highlighter-rouge">_grow</code></td> <td style="text-align: right">482</td> </tr> </tbody> </table> <p><code class="highlighter-rouge">ArrayCopy</code> is a big improvement, but slices are even faster (and work with vanilla BlitzMax too).</p> <hr/> <p>The original issue that spawned this post: <a href="https://github.com/Sodaware/pangolin.mod/issues/2">#2 Array growing using ArrayCopy</a></p> ]]></description> </item> <item> <title>BlitzMax optimization - Object casting</title> <pubDate>Tue, 27 Nov 2018 00:00:00 -0500</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-optimization-casting/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-optimization-casting/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>With the exception of numeric types (<code class="highlighter-rouge">Byte</code>, <code class="highlighter-rouge">Short</code>, <code class="highlighter-rouge">Int</code>, <code class="highlighter-rouge">Float</code>, <code class="highlighter-rouge">Double</code> and <code class="highlighter-rouge">Long</code>), all objects in BlitzMax extend the from base <code class="highlighter-rouge">Object</code> type. This comes in handy for passing around variables that can be different types, but there’s some overhead when converting (i.e. casting) an <code class="highlighter-rouge">Object</code> to a different type.</p> <p>For example:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Type</span> <span class="nc">MyType</span>
    <span class="n">Field</span> <span class="n">something</span><span class="p">:</span><span class="kt">String</span>
     
    <span class="k">Method</span> <span class="nf">New</span><span class="p">()</span>
         <span class="n">Self</span><span class="p">.</span><span class="n">something</span> <span class="o">=</span> <span class="s">"Hello, World!"</span>
    <span class="k">End</span> <span class="k">Method</span>
<span class="k">End</span> <span class="k">Type</span>

<span class="c1">' Create a new MyType but assign it to an Object variable.</span>
<span class="k">Local</span> <span class="nv">o</span><span class="p">:</span><span class="kt">Object</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyType</span>

<span class="c1">' The following code is invalid:</span>
<span class="c1">' Print o.something</span>

<span class="c1">' The variable needs to be cast first:</span>
<span class="k">Print</span> <span class="n">MyType</span><span class="p">(</span><span class="n">o</span><span class="p">).</span><span class="n">something</span>
</code></pre></div></div> <p>There’s normally no need to use plain <code class="highlighter-rouge">Object</code> types, but collections like <code class="highlighter-rouge">TMap</code> and <code class="highlighter-rouge">TList</code> use them for storing generic objects. This makes it easier to create lists on the fly, but comes with some overhead.</p> <h2 id="summary">Summary</h2> <ol> <li>Casting an object is approximately <strong>8x slower</strong> than not casting.</li> <li>The size of the object (i.e. the number of fields and methods) <strong>does not</strong> affect performance.</li> <li>Casting happens automatically when iterating over a collection (such as a TList). If possible, <strong>use arrays or a strongly-typed list</strong> if frequently iterating over the same collection.</li> </ol> <h2 id="casting-benchmarks">Casting benchmarks</h2> <p>All benchmarks are in release mode with threading enabled. Each test was executed 1,000,000,000 times.</p> <table class="table--striped table--fancy table--benchmark"> <thead> <tr> <th>Assignment Type</th> <th>Execution Time (milliseconds)</th> </tr> </thead> <tbody> <tr> <td>No Cast</td> <td>324</td> </tr> <tr> <td>Cast</td> <td>2849</td> </tr> </tbody> </table> <p>The size of the object makes no difference. The same as the previous test, but with a type containing 2,000 fields:</p> <table class="table--striped table--fancy table--benchmark"> <thead> <tr> <th>Assignment Type</th> <th>Execution Time (milliseconds)</th> </tr> </thead> <tbody> <tr> <td>No Cast</td> <td>328</td> </tr> <tr> <td>Cast</td> <td>2916</td> </tr> </tbody> </table> <h2 id="reducing-casting">Reducing casting</h2> <p>For collections that are large, or iterated over frequently, a strongly-typed collection will help with performance. If the size of the collection remains the same, an array will be fastest. A strongly-typed <a href="https://www.sodaware.net/dev/blitz/modules/sodaware.mod/">ObjectBag</a> is almost as fast but can also be resized on the fly.</p> <h2 id="test-code">Test code</h2> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SuperStrict</span>
<span class="n">Framework</span> <span class="n">brl</span><span class="p">.</span><span class="n">basic</span>

<span class="k">Const</span> <span class="nv">ITERATIONS</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="mi">1000000000</span>
<span class="k">Local</span> <span class="nv">startTime</span><span class="p">:</span><span class="kt">Int</span>
<span class="k">Local</span> <span class="nv">counter</span><span class="p">:</span><span class="n">int</span>

<span class="c1">' -- No Casting</span>
<span class="k">Local</span> <span class="nv">result1</span><span class="p">:</span><span class="kt">Object</span>
<span class="k">Local</span> <span class="nv">o</span><span class="p">:</span><span class="kt">Object</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyType</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
	<span class="n">result1</span> <span class="o">=</span> <span class="n">o</span>
<span class="k">Next</span>
<span class="k">Print</span> <span class="s">"No Cast: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="c1">' -- Casting</span>
<span class="k">Local</span> <span class="nv">result2</span><span class="p">:</span><span class="n">MyType</span>
<span class="n">startTime</span> <span class="o">=</span> <span class="n">millisecs</span><span class="p">()</span>
<span class="k">For</span> <span class="n">local</span> <span class="n">i</span><span class="p">:</span><span class="n">int</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">to</span> <span class="n">ITERATIONS</span>
	<span class="n">result2</span> <span class="o">=</span> <span class="n">MyType</span><span class="p">(</span><span class="n">o</span><span class="p">)</span>
<span class="k">Next</span>
<span class="k">Print</span> <span class="s">"Cast: "</span> <span class="o">+</span> <span class="p">(</span><span class="n">millisecs</span><span class="p">()</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> 

<span class="k">Type</span> <span class="nc">MyType</span>
     <span class="n">Field</span> <span class="n">something</span><span class="p">:</span><span class="kt">String</span>
     <span class="n">Field</span> <span class="n">something_else</span><span class="p">:</span><span class="kt">Int</span>
<span class="k">End</span> <span class="k">Type</span>
</code></pre></div></div> ]]></description> </item> <item> <title>BlitzMax optimization guide</title> <pubDate>Mon, 26 Nov 2018 00:00:00 -0500</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-optimization-guide/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-optimization-guide/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p><a href="https://github.com/blitz-research/blitzmax">BlitzMax</a> has been fast enough for my needs, but it’s always fun to squeeze a little more out of code.</p> <p>This post will act as a hub for optimizations I’ve used. Not all of them are worth implementing, but sometimes it’s useful to see benchmarks when deciding on a solution.</p> <h2 id="optimizations">Optimizations</h2> <ul> <li><a href="/blog/blitzmax-optimization-casting/">BlitzMax optimization - Object casting</a></li> <li><a href="/blog/blitzmax-optimization-arrays/">BlitzMax optimization - Resizing arrays</a></li> <li><a href="/blog/blitzmax-optimization-null-checks/">BlitzMax optimization - Null checks</a></li> <li><a href="/blog/blitzmax-optimization-loops/">BlitzMax optimization - Loops</a></li> <li><a href="/blog/blitzmax-optimization-comparisons/">BlitzMax optimization - Variable comparisons</a></li> </ul> ]]></description> </item> <item> <title>Quickly loading text files in BlitzMax</title> <pubDate>Thu, 26 Jul 2018 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-file-loading/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-file-loading/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>A few days ago I was trying to track down a performance issue in a project that loads and parses JSON. I wrote a couple of benchmarks in different languages to see if it was a language issue, a library issue, or something else entirely.</p> <p>One thing that stood out was my BlitzMax test was parsing quickly, but it took an age to load the initial data.</p> <p>Naturally I had to drop everything to try and figure out what was happening.</p> <h2 id="the-problem">The problem</h2> <p>The file to be loaded was around 900KB, and the following function was used to load it into memory:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Function</span> <span class="nf">LoadFileAsString</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">url</span><span class="p">:</span><span class="kt">Object</span><span class="p">)</span>
    <span class="k">Local</span> <span class="nv">streamIn</span><span class="p">:</span><span class="n">TStream</span> <span class="o">=</span> <span class="n">ReadFile</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    <span class="k">Local</span> <span class="nv">contents</span><span class="p">:</span><span class="kt">String</span>  <span class="o">=</span> <span class="s">""</span>

    <span class="k">While</span> <span class="k">Not</span><span class="p">(</span><span class="n">file</span><span class="p">.</span><span class="n">Eof</span><span class="p">())</span>
        <span class="n">contents</span><span class="p">:</span><span class="o">+</span> <span class="n">file</span><span class="p">.</span><span class="n">ReadLine</span><span class="p">()</span> <span class="o">+</span> <span class="s">"~n"</span>
    <span class="k">Wend</span>

    <span class="k">Return</span> <span class="n">contents</span>
<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> <p>Reading the file byte-by-byte (using <code class="highlighter-rouge">ReadByte</code>) is far slower than using <code class="highlighter-rouge">ReadLine</code>. This approach works well enough on small files (&lt; 20kB), but gets very slow as they get larger.</p> <h2 id="the-solution">The solution</h2> <p>I figured loading everything into a bank and converting that to a string would be quicker. After all, a string is just a collection of bytes with a null terminator, and a BlitzMax bank is a collection of bytes.</p> <p>The first approach was to read the bank using <code class="highlighter-rouge">LoadBank</code> and then building a string using <code class="highlighter-rouge">PeekByte</code> for each character.</p> <p>The results were disappointing.</p> <p>After reading the documentation, I found a few functions that looked to do exactly what I needed.</p> <p>The built-in <code class="highlighter-rouge">String</code> type comes with <code class="highlighter-rouge">FromCString</code>, a function that can convert a C-style string into a BlitzMax one. C-style strings are passed around in BlitzMax as a <code class="highlighter-rouge">Byte Ptr</code> type.</p> <p>Even better, BlitzMax banks can use <code class="highlighter-rouge">LockBank</code> to return a pointer to their contents as a <code class="highlighter-rouge">Byte Ptr</code>.</p> <p>The new code looked something like this:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Function</span> <span class="nf">LoadFileAsString</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">url</span><span class="p">:</span><span class="kt">Object</span><span class="p">)</span>

    <span class="c1">' Create a bank and load the file contents.</span>
    <span class="k">Local</span> <span class="nv">bank</span><span class="p">:</span><span class="n">TBank</span> <span class="o">=</span> <span class="n">LoadBank</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>

    <span class="c1">' Ensure bank finishes with a 0 byte.</span>
    <span class="c1">' This prevents the string from having junk bytes at the end.</span>
    <span class="k">Local</span> <span class="nv">size</span><span class="p">:</span><span class="kt">Int</span> <span class="o">=</span> <span class="n">BankSize</span><span class="p">(</span><span class="n">bank</span><span class="p">)</span>
    <span class="n">ResizeBank</span><span class="p">(</span><span class="n">bank</span><span class="p">,</span> <span class="n">size</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">PokeByte</span><span class="p">(</span><span class="n">bank</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>

    <span class="c1">' Get bank contents and convert to a string</span>
    <span class="k">Local</span> <span class="nv">buffer</span><span class="p">:</span><span class="kt">Byte</span> <span class="ow">Ptr</span> <span class="o">=</span> <span class="n">LockBank</span><span class="p">(</span><span class="n">bank</span><span class="p">)</span>
    <span class="k">Local</span> <span class="nv">content</span><span class="p">:</span><span class="kt">String</span>  <span class="o">=</span> <span class="kt">String</span><span class="p">.</span><span class="n">FromCString</span><span class="p">(</span><span class="n">buffer</span><span class="p">)</span>

    <span class="c1">' Cleanup.</span>
    <span class="n">UnlockBank</span><span class="p">(</span><span class="n">bank</span><span class="p">)</span>
    <span class="n">bank</span> <span class="o">=</span> <span class="n">null</span>

    <span class="k">Return</span> <span class="n">content</span>

<span class="k">End</span> <span class="k">Function</span>
</code></pre></div></div> <p>One important addition is adding a null terminator to the end of the loaded content. Because <code class="highlighter-rouge">FromCString</code> expects a null byte terminator, it will continue to read from memory until it finds one. This can lead to junk data at the end of the string.</p> <h2 id="performance">Performance</h2> <p>The following benchmark is for a 900KB text file.</p> <table class="table--striped table--fancy table--function-map"> <thead> <tr> <th>Method</th> <th>Average Time (ms)</th> </tr> </thead> <tbody> <tr> <td>Method 1 (ReadLine)</td> <td>933.18</td> </tr> <tr> <td>Method 2 (LoadBank)</td> <td>2.8</td> </tr> </tbody> </table> <p>Sometimes <a href="http://catb.org/jargon/html/Y/yak-shaving.html">yak shaving</a> comes in handy.</p> ]]></description> </item> <item> <title>Callbacks with BlitzMax</title> <pubDate>Tue, 27 Feb 2018 00:00:00 -0500</pubDate> <link>http://sodaware.sdf.org/blog/blitzmax-callbacks/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/blitzmax-callbacks/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>BlitzMax supports function pointers, but they can be a bit tricky at times.</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Function</span> <span class="nf">my_callback_function</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
    <span class="k">Return</span> <span class="n">name</span> <span class="o">+</span> <span class="s">" was called!"</span>
<span class="k">End</span> <span class="k">Function</span>

<span class="k">Local</span> <span class="nv">function_pointer</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span> <span class="o">=</span> <span class="n">my_callback_function</span>
<span class="k">Print</span> <span class="n">function_pointer</span><span class="p">(</span><span class="s">"callback"</span><span class="p">)</span>
</code></pre></div></div> <p>The example above will print “callback was called” when run.</p> <p>This approach works well enough, but what if you’d like to call a method on an object? You need to work around it a little bit:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Type</span> <span class="nc">MyType</span>
    <span class="k">Function</span> <span class="nf">MyCallback</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">obj</span><span class="p">:</span><span class="n">MyType</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
        <span class="n">return</span> <span class="n">MyType</span><span class="p">(</span><span class="n">obj</span><span class="p">).</span><span class="n">myActualCallback</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
    <span class="k">End</span> <span class="k">Function</span>
    
    <span class="k">Method</span> <span class="nf">myActualCallback</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
        <span class="n">return</span> <span class="n">name</span> <span class="o">+</span> <span class="s">" was called!"</span>
    <span class="k">End</span> <span class="k">Method</span>
<span class="k">End</span> <span class="k">Type</span>

<span class="k">Local</span> <span class="nv">callback</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">obj</span><span class="p">:</span><span class="n">MyType</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span> <span class="o">=</span> <span class="n">MyType</span><span class="p">.</span><span class="n">MyCallback</span>
<span class="k">Local</span> <span class="nv">instance</span><span class="p">:</span><span class="n">MyType</span> <span class="o">=</span> <span class="n">new</span> <span class="n">MyType</span>
<span class="k">Print</span> <span class="n">callback</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="s">"callback"</span><span class="p">)</span>
</code></pre></div></div> <p>This isn’t ideal, as it means all methods need wrapping by another function as well as have the object passed in.</p> <h2 id="using-blitzmax-reflection">Using BlitzMax reflection</h2> <p>BlitzMax has a nice reflection system, which makes it possible to get information about a variety of things at runtime. This can be used to query a type and its variables, methods, and functions.</p> <p>Type method information is returned in a <code class="highlighter-rouge">TMethod</code> object which contains the method name, parameters, and return type. It also has a method called <code class="highlighter-rouge">invoke</code>, which calls the method on an object instance.</p> <p>Using the example <code class="highlighter-rouge">MyType</code> above, we could get information on <code class="highlighter-rouge">myActualCallback</code> using the following:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Local</span> <span class="nv">objectInfo</span><span class="p">:</span><span class="n">TTypeId</span> <span class="o">=</span> <span class="n">TTypeId</span><span class="p">.</span><span class="n">ForName</span><span class="p">(</span><span class="s">"MyType"</span><span class="p">)</span>
<span class="k">Local</span> <span class="nv">methodInfo</span><span class="p">:</span><span class="n">TMethod</span> <span class="o">=</span> <span class="n">objectInfo</span><span class="p">.</span><span class="n">FindMethod</span><span class="p">(</span><span class="s">"myActualCallback"</span><span class="p">)</span>
</code></pre></div></div> <p>When combined with <code class="highlighter-rouge">invoke</code>, a method on <code class="highlighter-rouge">instance</code> can be executed without explicitly calling it.</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">' Executes the 'myActualCallback' method on our instance.</span>
<span class="k">Print</span> <span class="kt">String</span><span class="p">(</span><span class="n">methodInfo</span><span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="err">[</span><span class="s">"invoked callback"</span><span class="err">]</span><span class="p">))</span>
</code></pre></div></div> <p>The <code class="highlighter-rouge">invoke</code> method takes two arguments: an object instance, and an array of objects that are passed as parameters to the method.</p> <h2 id="wrapping-it-all-up">Wrapping it all up</h2> <p>With a little wrapper class we can call a method on any object instance.</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SuperStrict</span>

<span class="k">Import</span> <span class="nn">brl.reflection</span>

<span class="k">Type</span> <span class="nc">CallbackWrapper</span>

    <span class="n">Field</span> <span class="n">_caller</span><span class="p">:</span><span class="kt">Object</span>
    <span class="n">Field</span> <span class="n">_method</span><span class="p">:</span><span class="n">TMethod</span>

    <span class="k">Method</span> <span class="nf">execute</span><span class="p">:</span><span class="kt">Object</span><span class="p">(</span><span class="n">args</span><span class="p">:</span><span class="kt">Object</span><span class="err">[]</span><span class="p">)</span>
        <span class="k">Return</span> <span class="n">Self</span><span class="p">.</span><span class="n">_method</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="n">Self</span><span class="p">.</span><span class="n">_caller</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
    <span class="k">End</span> <span class="k">Method</span>

    <span class="k">Function</span> <span class="nf">Create</span><span class="p">:</span><span class="n">CallbackWrapper</span><span class="p">(</span><span class="n">caller</span><span class="p">:</span><span class="kt">Object</span><span class="p">,</span> <span class="n">methodName</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>

        <span class="k">Local</span> <span class="nv">this</span><span class="p">:</span><span class="n">CallbackWrapper</span> <span class="o">=</span> <span class="k">New</span> <span class="n">CallbackWrapper</span>

        <span class="n">this</span><span class="p">.</span><span class="n">_caller</span> <span class="o">=</span> <span class="n">caller</span>
        <span class="n">this</span><span class="p">.</span><span class="n">_method</span> <span class="o">=</span> <span class="n">TTypeId</span><span class="p">.</span><span class="n">ForObject</span><span class="p">(</span><span class="n">caller</span><span class="p">).</span><span class="n">FindMethod</span><span class="p">(</span><span class="n">methodName</span><span class="p">)</span>

        <span class="c1">' Must be a valid method</span>
        <span class="k">If</span> <span class="n">this</span><span class="p">.</span><span class="n">_method</span> <span class="o">=</span> <span class="n">Null</span> <span class="k">Then</span>
            <span class="k">Throw</span> <span class="s">"Cannot create a callback for missing method: "</span> <span class="o">+</span> <span class="n">methodName</span>
        <span class="k">EndIf</span>

        <span class="k">Return</span> <span class="n">this</span>

    <span class="k">End</span> <span class="k">Function</span>

<span class="k">End</span> <span class="k">Type</span>
</code></pre></div></div> <p>This allows us to do the following using the <code class="highlighter-rouge">MyType</code> object from earlier:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Local</span> <span class="nv">instance</span><span class="p">:</span><span class="n">MyType</span>         <span class="o">=</span> <span class="n">new</span> <span class="n">MyType</span>
<span class="k">Local</span> <span class="nv">wrapper</span><span class="p">:</span><span class="n">CallbackWrapper</span> <span class="o">=</span> <span class="n">CallbackWrapper</span><span class="p">.</span><span class="n">create</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="s">"myActualCallback"</span><span class="p">)</span>

<span class="k">Print</span> <span class="kt">String</span><span class="p">(</span><span class="n">callbackInstance</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">"wrapped callback"</span><span class="p">))</span>
</code></pre></div></div> <p>Hooray!</p> <p>One nice thing about this approach is it can be used on any object, so you don’t have to worry about extending a base class. Also, because the callbacks are BlitzMax objects they can be stored in data structures like <code class="highlighter-rouge">TMap</code> or <code class="highlighter-rouge">TList</code>. Function pointers cannot.</p> <p>This approach is not without its drawbacks. Because <code class="highlighter-rouge">invoke</code> uses <code class="highlighter-rouge">Object</code> for its arguments and return values, there usually needs to be a little data massaging to get it working.</p> <p>To get around this I’ll usually write an <code class="highlighter-rouge">execute</code> method for specific method signatures I want:</p> <div class="language-blitzmax highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">' Wrapping the `execute` method with something nicer.</span>
<span class="k">Method</span> <span class="nf">execute_callback</span><span class="p">:</span><span class="kt">String</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">String</span><span class="p">)</span>
    <span class="k">Return</span> <span class="kt">String</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">_method</span><span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">_caller</span><span class="p">,</span> <span class="err">[</span><span class="n">name</span><span class="err">]</span><span class="p">))</span>
<span class="k">End</span> <span class="k">Method</span>
</code></pre></div></div> <h2 id="performance">Performance</h2> <p>There is a performance hit to using this approach. Testing each approach 10,000,000 times gave the following results on a Linux machine:</p> <table class="table--striped table--fancy table--benchmark"> <thead> <tr> <th>Method Used</th> <th>Total Time (Milliseconds)</th> </tr> </thead> <tbody> <tr> <td>Function Pointer</td> <td>525</td> </tr> <tr> <td>Type Function Pointer</td> <td>570</td> </tr> <tr> <td>Reflection-based Callback</td> <td>2898</td> </tr> <tr> <td>Reflection-based Callback (wrapped)</td> <td>3878</td> </tr> </tbody> </table> <p>Wrapping the callback with casting slows things down considerably, so there is a trade-off between readability and speed.</p> <h2 id="example-usages">Example Usages</h2> <p>I’ve used this approach in a couple of places in my own projects:</p> <ol> <li>The pangolin.events module uses it for <a href="https://github.com/sodaware/pangolin.mod/blob/master/events.mod/src/event_handler.bmx">event handlers</a>. Using this system allows events to have handler objects or methods added and removed dynamically at runtime.</li> <li>The pangolin <a href="https://github.com/sodaware/pangolin.mod/blob/master/contentdb.mod/src/entity_factory.bmx#L116">entity factory</a> uses it to check if an object has an <code class="highlighter-rouge">initializeFromTemplate</code> method. If it does, the method is called. Otherwise a warning is signalled and the object is created using reflection.</li> <li>A couple of tools use it to provide some very basic scripting. For example, something like <code class="highlighter-rouge">addCommand("command_name", MyCallback.create(object, "exec_command_name"))</code> can be used to dynamically add commands.</li> </ol> ]]></description> </item> <item> <title>Roguelike development tips</title> <pubDate>Sun, 19 Mar 2017 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/roguelike-development-tips/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/roguelike-development-tips/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>Reddit’s <a href="https://www.reddit.com/r/roguelikedev/">/r/roguelikedev</a> is a great place for discussing roguelike development. One of its most valuable resources is <strong>FAQ Friday</strong>, a bi-weekly post where developers share their insights about a specific topic.</p> <p>There’s a huge amount of useful information locked away in these posts, and they’re well worth reading through even if you’re not building a roguelike.</p> <h2 id="highlights">Highlights</h2> <p>Below are a couple of my favourites from the archives. There’s so much good stuff I’ll probably be updating this list a lot.</p> <dl> <dt><a href="https://www.reddit.com/r/roguelikedev/comments/2whxdj/faq_friday_5_data_management/">#5 - Data Management</a></dt> <dd>Detailed discussion on how different games get data into the game. There’s examples of different file formats and how objects are represented internally in the game.</dd> <dt><a href="https://www.reddit.com/r/roguelikedev/comments/322c0s/faq_friday_10_project_management/">#10 - Project Management</a></dt> <dd>A look at how people organize their projects and how files are structured. There’s also some discussion on what tools are useful for getting things done on time.</dd> <dt><a href="https://www.reddit.com/r/roguelikedev/comments/3qs4rv/faq_friday_24_world_structure/">#24 - World Structure</a></dt> <dd>How worlds are laid out, what kinds of environments are used and how things are all connected.</dd> <dt><a href="https://www.reddit.com/r/roguelikedev/comments/4ndsfx/faq_friday_40_inventory_management/">#40 - Inventory Management</a></dt> <dd>How different games manage their inventories, challenges along the way and some UI discussion.</dd> </dl> <h2 id="rroguelikedev-faq-friday-archives">/r/roguelikedev/ “FAQ Friday” archives</h2> <p><strong>Last update:</strong> 2017-04-13</p> <ul class="posts-list posts-list--no-indent"> <li> <span class="date">2017-03-31</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/62hwl7/faq_friday_61_questing_and_optional_challenges/"> #61 Questing and Optional Challenges </a> </li> <li> <span class="date">2017-03-17</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5zucfr/faq_friday_60_shops_and_item_acquisition/"> #60 Shops and Item Acquisition </a> </li> <li> <span class="date">2017-03-03</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5x73ec/faq_friday_59_community/"> #59 Community </a> </li> <li> <span class="date">2017-02-17</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5uiybl/faq_friday_58_theme/"> #58 Theme </a> </li> <li> <span class="date">2017-02-03</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5rqnvl/faq_friday_57_story_and_lore/"> #57 Story and Lore </a> </li> <li> <span class="date">2017-01-20</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5p0v7z/faq_friday_56_mob_distribution/"> #56 Mob Distribution </a> </li> <li> <span class="date">2017-01-06</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5ma55p/faq_friday_55_factions_and_cooperation/"> #55 Factions and Cooperation </a> </li> <li> <span class="date">2016-12-23</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5ju7wd/faq_friday_54_map_prefabs/"> #54 Map Prefabs </a> </li> <li> <span class="date">2016-12-09</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5haqeo/faq_friday_53_seeds/"> #53 Seeds </a> </li> <li> <span class="date">2016-11-25</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5eqx0k/faq_friday_52_crafting_systems/"> #52 Crafting Systems </a> </li> <li> <span class="date">2016-11-11</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/5cbj7l/faq_friday_51_licenses/"> #51 Licenses </a> </li> <li> <span class="date">2016-10-28</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/59rgiv/faq_friday_50_productivity/"> #50 Productivity </a> </li> <li> <span class="date">2016-10-14</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/57dnqk/faq_friday_49_awareness_systems/"> #49 Awareness Systems </a> </li> <li> <span class="date">2016-09-30</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/555c4x/faq_friday_48_developer_motivation/"> #48 Developer Motivation </a> </li> <li> <span class="date">2016-09-16</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/52zbkp/faq_friday_47_options_and_configuration/"> #47 Options and Configuration </a> </li> <li> <span class="date">2016-09-02</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/50qbig/faq_friday_46_optimization/"> #46 Optimization </a> </li> <li> <span class="date">2016-08-19</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4yfwf5/faq_friday_45_libraries_redux/"> #45 Libraries Redux </a> </li> <li> <span class="date">2016-08-05</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4w7wrc/faq_friday_44_ability_and_effect_systems/"> #44 Ability and Effect Systems </a> </li> <li> <span class="date">2016-07-22</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4u0213/faq_friday_43_tutorials_and_help/"> #43 Tutorials and Help </a> </li> <li> <span class="date">2016-07-08</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4rrw6u/faq_friday_42_achievements_and_scoring/"> #42 Achievements and Scoring </a> </li> <li> <span class="date">2016-06-24</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4pk2k6/faq_friday_41_time_systems/"> #41 Time Systems </a> </li> <li> <span class="date">2016-06-10</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4ndsfx/faq_friday_40_inventory_management/"> #40 Inventory Management </a> </li> <li> <span class="date">2016-05-27</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4l8dhn/faq_friday_39_analytics/"> #39 Analytics </a> </li> <li> <span class="date">2016-05-13</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4j3t20/faq_friday_38_identification_systems/"> #38 Identification Systems </a> </li> <li> <span class="date">2016-04-29</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4gx30s/faq_friday_37_hunger_clocks/"> #37 Hunger Clocks </a> </li> <li> <span class="date">2016-04-15</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4eu3sx/faq_friday_36_character_progression/"> #36 Character Progression </a> </li> <li> <span class="date">2016-04-01</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4ct2xv/faq_friday_35_playtesting_and_feedback/"> #35 Playtesting and Feedback </a> </li> <li> <span class="date">2016-03-18</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4aw426/faq_friday_34_feature_planning/"> #34 Feature Planning </a> </li> <li> <span class="date">2016-03-04</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/48v5u1/faq_friday_33_architecture_planning/"> #33 Architecture Planning </a> </li> <li> <span class="date">2016-02-19</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/46i4i7/faq_friday_32_combat_algorithms/"> #32 Combat Algorithms </a> </li> <li> <span class="date">2016-02-05</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/448jw6/faq_friday_31_pain_points/"> #31 Pain Points </a> </li> <li> <span class="date">2016-01-22</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/4236j9/faq_friday_30_message_logs/"> #30 Message Logs </a> </li> <li> <span class="date">2016-01-08</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3zyot8/faq_friday_29_fonts_and_styles/"> #29 Fonts and Styles </a> </li> <li> <span class="date">2015-12-25</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3y4z3x/faq_friday_28_map_object_representation/"> #28 Map Object Representation </a> </li> <li> <span class="date">2015-12-11</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3wb1uo/faq_friday_27_color/"> #27 Color </a> </li> <li> <span class="date">2015-11-27</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3ueyqp/faq_friday_26_animation/"> #26 Animation </a> </li> <li> <span class="date">2015-11-13</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3slu9c/faq_friday_25_pathfinding/"> #25 Pathfinding </a> </li> <li> <span class="date">2015-10-30</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3qs4rv/faq_friday_24_world_structure/"> #24 World Structure </a> </li> <li> <span class="date">2015-10-16</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3oxc6f/faq_friday_23_map_design/"> #23 Map Design </a> </li> <li> <span class="date">2015-10-02</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3n63hw/faq_friday_22_map_generation/"> #22 Map Generation </a> </li> <li> <span class="date">2015-09-18</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3ldi50/faq_friday_21_morgue_files/"> #21 Morgue Files </a> </li> <li> <span class="date">2015-09-04</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3jk3xm/faq_friday_20_saving/"> #20 Saving </a> </li> <li> <span class="date">2015-08-21</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3hs9gi/faq_friday_19_permadeath/"> #19 Permadeath </a> </li> <li> <span class="date">2015-08-07</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3g2mcw/faq_friday_18_input_handling/"> #18 Input Handling </a> </li> <li> <span class="date">2015-07-24</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3ee3pr/faq_friday_17_ui_implementation/"> #17 UI Implementation </a> </li> <li> <span class="date">2015-07-10</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3cqsaq/faq_friday_16_ui_design/"> #16 UI Design </a> </li> <li> <span class="date">2015-06-26</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/3b4wx2/faq_friday_15_ai/"> #15 AI </a> </li> <li> <span class="date">2015-06-12</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/39irux/faq_friday_14_inspiration/"> #14 Inspiration </a> </li> <li> <span class="date">2015-05-29</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/37pnjz/faq_friday_13_geometry/"> #13 Geometry </a> </li> <li> <span class="date">2015-05-08</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/358mt5/faq_friday_12_field_of_vision/"> #12 Field of Vision </a> </li> <li> <span class="date">2015-04-24</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/33nscp/faq_friday_11_random_number_generation/"> #11 Random Number Generation </a> </li> <li> <span class="date">2015-04-10</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/322c0s/faq_friday_10_project_management/"> #10 Project Management </a> </li> <li> <span class="date">2015-03-27</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/30fvqp/faq_friday_9_debugging/"> #9 Debugging </a> </li> <li> <span class="date">2015-03-13</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2yuuom/faq_friday_8_core_mechanic/"> #8 Core Mechanic </a> </li> <li> <span class="date">2015-03-06</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2y3rkg/faq_friday_7_loot/"> #7 Loot Distribution </a> </li> <li> <span class="date">2015-02-27</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2xay6f/faq_friday_6_content_creation_and_balance/"> #6 Content Creation and Balance </a> </li> <li> <span class="date">2015-02-20</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2whxdj/faq_friday_5_data_management/"> #5 Data Management </a> </li> <li> <span class="date">2015-02-13</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2vptbj/faq_friday_4_world_architecture/"> #4 World Architecture </a> </li> <li> <span class="date">2015-02-06</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2uxv79/faq_friday_3_the_game_loop/"> #3 The Game Loop </a> </li> <li> <span class="date">2015-01-30</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2u5nj0/faq_friday_2_development_tools/"> #2 Development Tools </a> </li> <li> <span class="date">2015-01-23</span> - <a href="https://www.reddit.com/r/roguelikedev/comments/2tcvaj/faq_friday_1_languages_and_libraries/"> #1 Languages and Libraries </a> </li> </ul> ]]></description> </item> <item> <title>What this site is...</title> <pubDate>Thu, 25 Aug 2016 00:00:00 -0400</pubDate> <link>http://sodaware.sdf.org/blog/junk/</link> <guid isPermaLink="true">http://sodaware.sdf.org/blog/junk/</guid> <author>phil@sodaware.net (Phil Newton)</author> <description><![CDATA[<p>I’ve written a lot of code over the years. Some of it I’m quite proud of, but most of it is useless junk.</p> <p>This site is a collection of stuff I don’t completely hate.</p> ]]></description> </item> </channel> </rss>