<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Bill / 葛威 on Medium]]></title>
        <description><![CDATA[Stories by Bill / 葛威 on Medium]]></description>
        <link>https://medium.com/@BillGlover?source=rss-194cbbb37a2------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*vvRqSJOstnqtqV5p.jpeg</url>
            <title>Stories by Bill / 葛威 on Medium</title>
            <link>https://medium.com/@BillGlover?source=rss-194cbbb37a2------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 18:53:36 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@BillGlover/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Multi Architecture Docker Builds]]></title>
            <link>https://medium.com/@BillGlover/multi-architecture-docker-builds-4a16e9f6feab?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/4a16e9f6feab</guid>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[raspberry-pi]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Tue, 30 Oct 2018 00:02:59 GMT</pubDate>
            <atom:updated>2018-11-01T10:47:23.236Z</atom:updated>
            <content:encoded><![CDATA[<p>Docker has had the ability to build multi-architecture images for a while. I’ve never had cause to use it, until now. In this post I’ll walk through building a docker image that should work on your laptop and a Raspberry Pi.</p><p>We’ll cover the following:</p><ol><li>A simple test application</li><li>Building docker images for each architecture</li><li>Wrapping images in a multi-architecture manifest</li><li>A small gotcha</li><li>Testing our images</li></ol><h3>The Application</h3><p>Before we can begin building our containers, we need an application to package. We’ll use this small Go program. When executed, it displays the operating system and CPU architecture it. In this example, we expect to see arm (Raspberry Pi) or amd64 (MacBook). In both cases we’ll be running on Linux.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eff3283dd6ad713d7a68787d75d3f753/href">https://medium.com/media/eff3283dd6ad713d7a68787d75d3f753/href</a></iframe><pre>$ go run main.go<br>Hello, 世界!<br>GOOS: darwin<br>GOARCH amd64</pre><h3>Building Docker Containers</h3><p>Although we promise “multi-architecture” builds, we start by building two architecture specific images.</p><p>Create the following Dockerfile. I’ve named it Dockerfile-amd64 as it builds an amd64 version of our application (note the GOARCH=amd64).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d83a1b7717b569e22e9e394c4fc791e6/href">https://medium.com/media/d83a1b7717b569e22e9e394c4fc791e6/href</a></iframe><p>Build and tag the image.</p><pre>$ docker build -f Dockerfile-amd64 -t billglover/hello-arch:amd64 .</pre><p>Run the container to test things are working as expected.</p><pre>$ docker run billglover/hello-arch:amd64<br>Hello, 世界!<br>GOOS: linux<br>GOARCH amd64</pre><p>Next we want to build the arm version of our application (note the GOARCH=arm). I’ve named this file Dockerfile-arm.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/500883227dec7d63cc4f59795b6042cd/href">https://medium.com/media/500883227dec7d63cc4f59795b6042cd/href</a></iframe><p>Build and tag the image.</p><pre>$ docker build -f Dockerfile-arm -t billglover/hello-arch:arm .</pre><h3>Multi-Architecture Manifest</h3><p>We need to push these images to the repository before creating the combined manifest. It isn’t clear why we need to do this, but I ran into issues if I didn’t.</p><pre>$ docker push billglover/hello-arch:amd64 $ docker push billglover/hello-arch:arm</pre><p>Create the multi-architecture manifest.</p><pre>$ docker manifest create billglover/hello-arch billglover/hello-arch:amd64 billglover/hello-arch:arm Created manifest list docker.io/billglover/hello-arch:latest</pre><p>The syntax of this command may not be obvious. The first argument, billglover/hello-arch is the name of our multi-architecture manifest. The remaining arguments are the images we want to include.</p><p>In theory this should be all we need to do. If we were using architecture specific base images then everything would be fine. We are using the scratch image which is architecture agnostic. Take a look inside the multi-architecture image to see why this is a problem.</p><pre>$ docker manifest inspect billglover/hello-arch</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/27701dca47f7e645abc22bf0fa2b074e/href">https://medium.com/media/27701dca47f7e645abc22bf0fa2b074e/href</a></iframe><p>The architecture of both images inside our manifest is amd64. We need to fix this.</p><pre>$ docker manifest annotate --arch arm billglover/hello-arch billglover/hello-arch:arm</pre><p>This corrects the architecture in our manifest list.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/df2f2340809dafcdbfbe2a2e14467b04/href">https://medium.com/media/df2f2340809dafcdbfbe2a2e14467b04/href</a></iframe><p>Push the multi-architecture manifest.</p><pre>$ docker manifest push billglover/hello-arch</pre><h3>Testing</h3><p>Running the multi-architecture image locally (amd64):</p><pre>$ docker run billglover/hello-arch<br>Hello, 世界!<br>GOOS: linux<br>GOARCH amd64</pre><p>Running the multi-architecture image on a Raspberry Pi (arm):</p><pre>pi@node-01:~ $ docker run billglover/hello-arch<br>Hello, 世界!<br>GOOS: linux<br>GOARCH arm</pre><p>It is possible you will never need to work with more than one CPU architecture. If you do, it is reassuring to know that Docker has you covered. The solution is a manifest list, containing two or more architecture specific images. Docker will select the correct image at runtime.</p><p><em>Originally published at </em><a href="https://billglover.me/2018/10/30/multi-architecture-docker-builds/"><em>billglover.me</em></a><em> on October 30, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4a16e9f6feab" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Go App Engine]]></title>
            <link>https://medium.com/@BillGlover/go-app-engine-b0e124bbce25?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/b0e124bbce25</guid>
            <category><![CDATA[go]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[serverless]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Mon, 22 Oct 2018 12:56:58 GMT</pubDate>
            <atom:updated>2018-10-31T23:43:37.672Z</atom:updated>
            <content:encoded><![CDATA[<p>Google recently announced an update to App Engine that brings support for the Go 1.11 runtime. This release also enables the deployment of standard Go applications on App Engine. In this post, I show you how.</p><h3>Our Application</h3><p>The application we’ll be working through is simple. It returns the IP address of the user, nothing more.</p><blockquote><strong><em>Requirement #1:</em></strong><em> As someone who wants to allow others access to my home network, I need to know my external IP address.</em></blockquote><h3>Pre-Requisites</h3><p>If you want to deploy this application yourself, I’m going to make a couple of assumptions:</p><ol><li>You have Go installed and know how to build/run a simple application (<a href="https://golang.org/doc/install">start here</a>).</li><li>You have a Google Cloud account and created a project that allows you to deploy to App Engine (<a href="https://cloud.google.com/appengine/docs/standard/go111/quickstart">start here</a>).</li><li>You have the latest version of the Google Cloud SDK installed, along with the beta components (<a href="https://cloud.google.com/appengine/downloads">start here</a>).</li></ol><p>If you don’t have these set-up, don’t worry. You should still be able to follow along. The ease of publishing to Google App Engine will encourage you to give it a try.</p><h3>Determining the IP address</h3><p>Before we can write our application, I want to touch on how we can identify the user’s IP address. The Go Standard Library provides the RemoteAddr property on <a href="https://golang.org/pkg/net/http/#Request">http.Request</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8c265362c5fd106c3b198e8409f99a4d/href">https://medium.com/media/8c265362c5fd106c3b198e8409f99a4d/href</a></iframe><p>You might think we could use this property to determine the IP address of the user. Often you can, but if your application sits behind a load balancer or reverse proxy this doesn’t work. When we deploy on App Engine, this is exactly the scenario we have. The <a href="https://cloud.google.com/solutions/architecture/webapp">App Engine Architecture</a> shows that our application will sit behind the Google Load Balancer. As a result the RemoteAddr property is set to the address of the load balancer and not that of the requestor.</p><p>This is set-up is so common that load balancers often set a header to get round this. The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For">Mozilla Developer Network</a> documentation describes this header header as follows.</p><blockquote><em>“The X-Forwarded-For (XFF) header is a de-facto standard header for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or a load balancer. When traffic is intercepted between clients and servers, server access logs contain the IP address of the proxy or load balancer only. To see the original IP address of the client, the X-Forwarded-For request header is used.”</em></blockquote><pre>X-Forwarded-For: 109.148.211.158</pre><p>We will be using the XFF header to identify the IP address of requestor and return it in the response.</p><p><strong><em>Note:</em></strong> If the request passes through more than one load balancer, the header may contain several addresses. In this example, I am going to assume that this is not the case and that we only have a single IP address as shown above.</p><h3>The Code</h3><p>This is our main.go file.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/61e36cfe1283696ce05bbad8cb9940e8/href">https://medium.com/media/61e36cfe1283696ce05bbad8cb9940e8/href</a></iframe><p>We can run the application on our local machine. But we’ll need to set the PORT environment variable before execution.</p><p>Test the service with cURL in a separate terminal window.</p><pre>curl -i -H &#39;X-Forwarded-For: 127.0.0.1&#39; <a href="http://localhost:8080/ip">http://localhost:8080/ip</a></pre><pre>HTTP/1.1 200 OK<br>Date: Mon, 22 Oct 2018 13:34:15 GMT<br>Content-Length: 10<br>Content-Type: text/plain; charset=utf-8</pre><pre>127.0.0.1</pre><p>So far, this is a standard Go application that compiles to a single binary. We’ve done nothing to support the App Engine deployment target. In the previous edition of App Engine we would have had to do the following.</p><ul><li>Import google.golang.org/appengine</li><li>Register our handlers in the init() method</li><li>Leave out the main() function</li></ul><p>Without the main() function, this would not compile to a standalone application. The code would be App Engine specific.</p><p>There were further changes required if your application needed to make outbound requests. As we don’t make outbound requests from our application, I’m going to skip over these. An example of a project built for earlier App Engine releases is available here: <a href="https://github.com/billglover/ae_greetings/blob/master/greeting/svc.go">github.com/billglover/ae_greetings</a>.</p><h3>Deployment</h3><p>The latest release of App Engine gets rid of these platform specifics. We are now able to write standard Go applications and deploy them to App Engine.</p><p>Create a file called app.yaml in the same folder as main.go.</p><pre>runtime: go111</pre><p>You can now deploy your App with the following command.</p><pre>gcloud app deploy</pre><p>If you want to avoid having to respond to the prompt you can use the --quiet flag.</p><pre>gcloud app deploy --quiet</pre><p>When the deployment is complete, the client will display the URL to your application. You can open it in your default browser with the following command.</p><pre>gcloud app browse</pre><p>If everything worked, you should see a browser tab open showing your external IP address.</p><p>That is all we need to do to deploy a standard Application to App Engine. There are further configuration options to control the behaviour of your application. None of these are essential, but I chose to limit the number of instances I run. Documentation for the full set of options is available here: <a href="https://cloud.google.com/appengine/docs/standard/go111/config/appref">app.yaml Configuration File</a></p><pre>runtime: go111<br>automatic_scaling:<br>min_instances: 0<br>max_instances: 1</pre><p>In this post we have covered the following:</p><ul><li>Created a Go application that displays the IP address of the requestor</li><li>Ran the application on our local machine and tested it with cURL</li><li>Deployed the application on App Engine</li><li>Tested the public deployment by making a request in our browser</li></ul><p>This example shows how to deploy a standard Go application to Google App Engine. It shows that this is possible without writing specific App Engine code. This is a significant improvement and makes the platform an attractive deployment option for Go services.</p><p>Full code is available on GitHub: <a href="https://github.com/billglover/localname/tree/0553ea4ffbd2fd116a2a222fa3c2e7c660ff61d1/cmd/server">github.com/billglover/localname</a>. Issues and Pull Requests are welcome.</p><p><em>Originally published at </em><a href="https://billglover.me/2018/10/22/go-app-engine/"><em>billglover.me</em></a><em> on October 22, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b0e124bbce25" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Expensive Is a Go Function Call?]]></title>
            <link>https://medium.com/@BillGlover/how-expensive-is-a-go-function-call-4350337b3803?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/4350337b3803</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[developer]]></category>
            <category><![CDATA[learning-to-code]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[golang]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Mon, 17 Sep 2018 13:30:00 GMT</pubDate>
            <atom:updated>2018-09-18T15:27:42.225Z</atom:updated>
            <content:encoded><![CDATA[<p>I was recently benchmarking various implementations of an algorithm and noticed that the recursive implementation of an algorithm performed worse than its inline equivalent. I didn’t know if it made sense to attribute this overhead to the cost of the additional function calls in the recursive implementation. I set out to see if I could see behind the scenes of a Go function call and determine just how expensive each function call is.</p><blockquote><em>TLDR: The cost of a function call? I still don’t know. The best I can offer is that it depends, well that and an exploration of Go assembler.</em></blockquote><p>In this post, I take you through the analysis I did and highlight what I learned along the way.</p><h3>A simple program</h3><p>Rather than use a complex algorithm, I sought to use the simplest possible program that demonstrated the behaviour I wanted to investigate. This Go program takes an int, in this case 1000, and increments it by 1 inside a loop that executes 1000 iterations. The result, 2000, is printed to the screen.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2d406bdc393740d8c4ad787b148d5ca9/href">https://medium.com/media/2d406bdc393740d8c4ad787b148d5ca9/href</a></iframe><p>I included the loop to slow down the function and allow me to measure benchmarks. Without the loop, benchmark results are in the low single digit nanoseconds which fall within the margin of error of simple benchmarks.</p><pre>$ go test -run=X -bench=.<br>goos: darwin <br>oarch: amd64<br>pkg: github.com/billglover/recursion/inline<br>BenchmarkInc-8		5000000			331 ns/op<br>PASS<br>ok	github.com/billglover/recursion/inline	1.971s</pre><h3>Adding function calls</h3><p>Now that I had a baseline, I wanted to see how the addition of a single function call would change the benchmark. I modified the inc(n int) int function as show below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/465e42fcbc81bd23dca8a9cdec4ebb15/href">https://medium.com/media/465e42fcbc81bd23dca8a9cdec4ebb15/href</a></iframe><p>Instead of increment it the value of n directly, we do so via an additional function call to add(). With the additional function call, I expected to see an increase in execution time. The benchmark comparison between the approaches can be seen below.</p><pre>$ go test -run=X -bench=. ./... | grep -i bench BenchmarkIncFunction-8		5000000			289 ns/op<br>BenchmarkIncInline-8		5000000			289 ns/op</pre><p>I ran the benchmark again several more times and results were consistent to within a couple of nanoseconds. Given the differences in recursive function performance I’d been seeing, I was expecting the additional function call to result in a noticeable change in performance. In these examples it appeared to make no difference at all. It was time to dig deeper.</p><h3>Behind the scenes</h3><p>I had a suspicion that the compiler was performing some optimisation on my example code and the resulting code was similar if not identical between the two examples. Luckily, the Go toolchain provides an easy way to view the output of the compiler.</p><pre>$ go tool compile -S inline.go</pre><p><strong><em>Note:</em></strong> <em>this results in an intermediary assembler and is not the code that actually ends up running on the CPU. I ignored this technicality as I continued to explore.</em></p><p>Looking at the assembly output for the first example, it was fairly easy to trace through the function execution. Helpfully, the assembler output includes references back to the file and line number for the original Go sourcecode.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5ff4a4f2e8fd371a7ec8a38819f9744c/href">https://medium.com/media/5ff4a4f2e8fd371a7ec8a38819f9744c/href</a></iframe><p>The FUNCDATA and PCDATA instructions <a href="https://golang.org/doc/asm#introduction">relate to garbage collection</a> and are inserted by the compiler. I’ve removed these from the following (and future) listings to aid readability. The TEXT directives define the function names and indicate the stack allocation required for the function to execute. This one line took the most time to analyse. I’ve broken out an explanation for each of the components below.</p><pre>00000	TEXT	&quot;&quot;.inc(SB), NOSPLIT, $0-16</pre><ul><li>&quot;&quot; is a placeholder that will be filled in later by the linker.</li><li>incInline is a symbol used to reference the function location in memory.</li><li>NOSPLIT I don’t understand this fully, but I think this means that some stack management code can be left out.</li><li>$0 indicates that no additional space on the stack is required for this function.</li><li>$16 indicates that 16-bytes are required for the function parameters and return values (in this case; one 64-bit integer parameter, and one 64-bit integer return value).</li></ul><p>A simplified view of the assembly language version of our first example is shown below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/06e0654aaa58516ab149682101290eed/href">https://medium.com/media/06e0654aaa58516ab149682101290eed/href</a></iframe><p>A quick glance at the documentation (<a href="https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture">X86 Architecture</a>) for Intel CPUs tells us a little more about the registers being used here.</p><ul><li>AX: Accumulator register (AX) – used in arithmetic operations.</li><li>CX: Counter register (CX) – used in shift/rotate instructions and loops.</li></ul><p>The Go documentation (<a href="https://golang.org/doc/asm">A Quick Guide to Go’s Assembler</a>) defines the following psuedo-registers. These aren’t hardware registers but are virtual registers maintained by the Go tool chain. I’ll treat them as if they were hardware registers for this investigation.</p><ul><li>SB: Static base pointer – used to reference global symbols.</li><li>SP: Stack pointer – used to reference the top of stack.</li></ul><p>This shows that register CX is being used to hold the value of our loop variable i. And register AX is being used to hold the value of our local variable n.</p><p>Now that I understood the inline version of our programme, I took a look at the output of the compiler for our second example. Again, I’ve removed the FUNCDATA and PCDATA instructions and added comments to each line.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d28f10a85bb44e02d6228c1f8f828194/href">https://medium.com/media/d28f10a85bb44e02d6228c1f8f828194/href</a></iframe><p>In this listing we can clearly see the two function definitions. I’ll leave you to walk through the implementation of the add(n int) int function. Far more interesting is that the listing for the inc(n int) int is identical to the listing in our first example. Although we have defined a new function, this function is never called. The end result is that the code path followed in the two examples is identical. This explains the benchmark comparisons seen earlier.</p><p>The compiler has determined that it can move the function add() inline and save on a function call. This is known as “inlining” and is a common compiler trick used to improve performance at the expense of binary size (see <a href="https://en.wikipedia.org/wiki/Inline_expansion">Wikipedia</a>). This explains why I was seeing identical benchmarks for both approaches, but not why my recursive functions were seeing slower performance than inline counterparts.</p><p>It turns out that the compiler doesn’t move functionality like this inline for all cases. One example where it is unable to do so is recursion as it doesn’t know at compile time how many times the recursive function is going to be called.</p><p>I was now convinced that these function calls would demonstrate a performance overhead (otherwise why would they be optimised out), but I was no closer to being able to measure what that overhead was. I needed a way to view the cost of function calls without the compiler optimising them away.</p><p>After some digging, I discovered a little known Go pragma directive to disable inlining for particular functions. This should never be used in normal code, but is very useful here for demonstrating the cost of function calls.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/82dcfef0361e5b552da26e6bee140cfc/href">https://medium.com/media/82dcfef0361e5b552da26e6bee140cfc/href</a></iframe><p>With the addition of the noinline pragma directive, I re-ran the benchmark.</p><pre>$ go test -run=X -bench=. -benchmem ./... | grep -i bench BenchmarkIncFunction-8		1000000			2289 ns/op<br>BenchmarkIncInline-8		5000000			286 ns/op</pre><p>This now shows the inline call is ten times faster than the approach using function calls. But, before you panic and start avoiding function calls altogether, remember I’m making 1,000 function calls during each benchmark operation. The overhead of any individual function call is tiny.</p><p>Now that I was able to reproduce the overhead associated with function calls, I went back to the assembly to see what was going on behind the scenes.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bdc88d5579d822a09a3f579119e8cb7e/href">https://medium.com/media/bdc88d5579d822a09a3f579119e8cb7e/href</a></iframe><p>The good news is that the inc() function now makes a call to add() on line 47. However I was not expecting the additional complexity before and after the main function body. There appears to be a lot more going on than a simple CALL to add(). Much of this additional complexity comes from managing the values on the stack and it was becoming increasingly difficult to trace how values were moving between registers as the functions were called. I turned to pen and paper to work this through.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Y9yH4pGXyXM7QMU_.gif" /></figure><p>If you want to follow this through yourself, grab a large mug of your favourite brew and several sheets of paper, it’ll take a while.</p><p>I traced through the execution of the programme and learned the following:</p><ul><li>Values are passed to functions via the stack.</li><li>Values are returned from functions via the stack.</li><li>Calling functions are responsible for reserving the required stack space.</li><li>Function calls increase the stack size by one to accommodate the return address.</li><li>Functions make calls to the runtime to grow the stack as necessary.</li></ul><p>I now understood a little more about why function calls incur a cost. Allocating stack space and passing parameter and return values around all costs CPU cycles. But I still wasn’t able to articulate how big that cost was.</p><h3>So how expensive is a function call</h3><p>Comparing our two approaches through benchmarking we can see that our solution involving a function call took 2289 ns/op whereas our inline solution took only 286 ns/op. You could make the argument that adding the function call takes an additional 2003 ns/op. For 1,000 function calls in each benchmarking operation, this equates to 2 ns/function call.</p><p>An alternate way of looking at it would be to look at the increase in the number of instructions executed to make our function call. Our inline method requires 10 lines of assembler. Our method involving a function call requires the execution of an additional 20 instructions. Figuring out how long each instruction takes to execute is non-trivial and so I decided to stop digging further. If you have any thoughts on how I might be able to approximate this I’d love to hear from you.</p><h3>In conclusion</h3><p>Whilst I can’t definitively articulate the cost of a function call in Go, I do have a better understanding of why it incurs a cost. To some, this may be obvious, but the journey above thought me a lot more about the Go, the associated toolchain, and general programme execution.</p><p>I’m still of the opinion that the best way to determine the difference between two approaches is to benchmark them. If you find yourself questioning the results of your benchmarks, it’s time to dig further. The Go toolchain provides a number of options for further investigation.</p><p><em>Originally published at </em><a href="https://billglover.me/2018/09/17/how-expensive-is-a-go-function-call/"><em>billglover.me</em></a><em> on September 17, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4350337b3803" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Art in The Age of Computers]]></title>
            <link>https://medium.com/@BillGlover/art-in-the-age-of-computers-e5294dee30bb?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/e5294dee30bb</guid>
            <category><![CDATA[code]]></category>
            <category><![CDATA[learning-to-code]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[art]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Sat, 25 Aug 2018 00:00:00 GMT</pubDate>
            <atom:updated>2018-08-27T10:50:28.305Z</atom:updated>
            <content:encoded><![CDATA[<p>My wife and I recently visited the V&amp;A and stumbled across <a href="https://www.vam.ac.uk/exhibitions/chance-and-control-art-in-the-age-of-computers">Chance and Control: Art in the age of computers</a>, a small display celebrating 50 years of computer generated art. Tucked away in the corner of the second room was <a href="http://collections.vam.ac.uk/item/O221321/schotter-print-nees-georg/">Schotter</a>, a piece by Georg Nees.</p><blockquote><em>“Nees was fascinated by the relationship between order and disorder in picture composition. To create this work, he introduced random variables into the computer program, causing orderly squares to descend into a heap.”</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1-7Tb04tlzKi6vLV.jpg" /><figcaption>Schotter, Georg Nees © Victoria and Albert Museum, London</figcaption></figure><p>I was toying with the idea of recreating the image when I came across the <a href="https://www.michaelfogleman.com/#go-graphics">Go Graphics</a> package by Michael Fogleman (<a href="https://github.com/fogleman/gg">GitHub</a>, <a href="https://godoc.org/github.com/fogleman/gg">GoDoc</a>), a beautifully simple way to get started with drawing graphics in Go. The basics involve creating a context, drawing various shapes, stroking (or filling) them and then rendering the result as a PNG image.</p><pre>package main import &quot;github.com/fogleman/gg&quot; func main() { ctx := gg.NewContext(640, 640) ctx.DrawRectangle(270, 270, 100, 100) ctx.SetRGBA(0, 0, 0, 1) ctx.Stroke() ctx.SavePNG(&quot;out.png&quot;) }</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*K_UnIdEfFTjhXty2.png" /></figure><p>This code snippet creates a 640 by 640 image and draws a square of 100 by 100px in the center. To generate the image, I needed a few loops, some randomness and the ability to offset each square from it’s regular position. The algorithm seemed fairly straightforward:</p><ul><li>Use a for loop to iterate over the columns</li><li>Use a for loop to iterate over the rows</li><li>Offset each square from its mid-point based on a function of current row</li><li>Rotate each square based on a function of the current row</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*46CrPv-1uDYZvXmh.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3YUBqEzrvtJPtZq7.png" /></figure><p>Tweaking the dimensions and weighting the offset and rotation gives us something remarkably similar to Georg’s original.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vc_9w7SbVHVQIgWJ.png" /></figure><p>With the ability to produce images I quickly found myself spending hours tweaking parameters, padding, colours, etc. I settled on using an exponentially increasing random offset to generate the images below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qVfM0ts02Ib772SQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uR0CXo1fahr4nacy.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JJMMbjeAIJeZ2z8A.png" /></figure><p>I’d love to try plotting some of these on paper (using a real plotter of course) but for now I’ll have to settle for generating wallpapers instead. A zip folder containing a collection of phone wallpapers is available for download below. Full access to the source code is available here: <a href="https://github.com/billglover/aitaoc">github.com/billglover/aitaoc</a></p><p><strong>Download:</strong> <a href="https://billglover.me/download/aitaoc_iPhone.zip">iPhone</a>, <a href="https://billglover.me/download/aitaoc_iPad.zip">iPad</a></p><p><em>Originally published at </em><a href="https://billglover.me/2018/08/25/art-in-the-age-of-computers/"><em>billglover.me</em></a><em> on August 25, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e5294dee30bb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creative AI]]></title>
            <link>https://medium.com/@BillGlover/creative-ai-891fcaac3ccb?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/891fcaac3ccb</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[creative]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[art]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Mon, 13 Nov 2017 21:42:23 GMT</pubDate>
            <atom:updated>2017-11-14T23:00:26.368Z</atom:updated>
            <content:encoded><![CDATA[<p>Luba Elliott opened the Cloud &amp; Machine Learning track at the recent Google Developer Group event at UCL in London with a talk on the Creative Applications of Machine Learning. It was her closing question that motivated me to look into some of the artists she mentions.</p><blockquote><em>“What is the best way for an AI to dream?”</em></blockquote><blockquote><em>– Luba Elliott, </em><a href="http://www.gdg-london.com/"><em>#GDGLondon</em></a><em> 2017</em></blockquote><p>Ask someone to explain why a reasonably complex neural network arrived at a particular decision and you will likely be met with a shrug of the shoulders. If you are lucky, you will be met with a generic explanation of how neural networks work. Rarely will you hear anything specific to the outcome in question. The same could be said about dreams. There is the theory, but nothing that allows you to determine why an individual dream took the turn it did.</p><p>Mathematics can explain the theory behind the artificial intelligence. Engineering advances have given us the compute resources necessary to consume vast troves of data to train these models. As a result, the individual decisions made by machines are now a function of data sets so vast that we struggle to comprehend the information they contain. It is practically impossible to explain why an individual decision was made.</p><p>This very challenge has led to some interesting debate around the European Union regulations on algorithmic decision-making and a <a href="https://arxiv.org/abs/1606.08813">“right to explanation”</a> where a user can ask for an explanation of an algorithmic decision that was made about them.</p><p>What struck me about Luba’s talk was that rather than trying to understand and explain these systems, there was a community of people dedicated to exploring their creativity. But is there was more than creativity to their work. Could art hold the key to explaining these machines? Much of the creativity we see in their work results from peering inside these models, exploring them as they develop, from seeing them dream.</p><p>Luba’s question got me thinking: In addition to looking for mathematical explanations for the decisions our models make should we also be looking into the way they dream? Even if a deterministic outcome eludes us, we arrive at some incredibly intriguing art and with it an insight into the models that we have not previously had. I wanted to share a selection of artists mentioned during the talk, not because they are the first to explore the field of Creative AI, but because their work represents an usual look inside the machines that many of us have come to depend on.</p><h3>Anna Ridler</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F217670143&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F217670143&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F634946048_640.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="640" height="352" frameborder="0" scrolling="no"><a href="https://medium.com/media/2aeddc9c80fcb32728ceee2e46f12cca/href">https://medium.com/media/2aeddc9c80fcb32728ceee2e46f12cca/href</a></iframe><p><a href="https://vimeo.com/217670143">Fall of the House of Usher</a> from <a href="https://vimeo.com/user51755374">Anna Ridler</a> on <a href="https://vimeo.com/">Vimeo</a>.</p><blockquote><em>Fall of the House of Usher is a 12-minute animation based on the short story by Edgar Allan Poe, where each still is generated by artificial intelligence. This is done by using a neural net (pix2pix) trained on the artist’s ink drawings made of stills from the 1929 version of the film. Each still shown in the animation is not merely a filter that is applied to an existing image, but an entirely new image by a neural net.</em></blockquote><ul><li>Portfolio: <a href="https://annaridler.com/">anaridler.com</a></li><li>Vimeo: <a href="https://vimeo.com/user51755374">vimeo.com</a></li><li>Twitter: <a href="https://twitter.com/annaridler">@annaridler</a></li></ul><h3>Gene Kogan</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F169493266&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F169493266&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F574231961_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1280" height="720" frameborder="0" scrolling="no"><a href="https://medium.com/media/044e245fea218288d2f56bdedc990eeb/href">https://medium.com/media/044e245fea218288d2f56bdedc990eeb/href</a></iframe><p><a href="https://vimeo.com/169493266">J-train style transfer</a> from <a href="https://vimeo.com/genekogan">Gene Kogan</a> on <a href="https://vimeo.com/">Vimeo</a>.</p><blockquote><em>Gene Kogan is an artist and a programmer who is interested in generative systems, computer science, and software for creativity and self-expression.</em></blockquote><p>What I particularly like about Gene’s work is how raw it fees. This rawness only serves to highlight the non-deterministic nature of the artificial intelligence algorithms. Gene’s more recent work starts to explore the idea of what it might mean for an AI to dream.</p><p>For those interested in exploring this field Gene has been working on a collection of free educational resources, <a href="https://ml4a.github.io/">ml4a</a>.</p><h3>Memo Akten</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F213295825&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F213295825&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F629681268_200x150.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="256" height="256" frameborder="0" scrolling="no"><a href="https://medium.com/media/02135b1a36e9ee9b37dae9a844a62ccb/href">https://medium.com/media/02135b1a36e9ee9b37dae9a844a62ccb/href</a></iframe><p><a href="https://vimeo.com/213295825">Learning to see: Hello World! [WIP R&amp;D 1]</a> from <a href="https://vimeo.com/memotv">Memo Akten</a> on <a href="https://vimeo.com/">Vimeo</a>.</p><blockquote><em>Artist working with computation as a medium, exploring collisions between nature, science, technology, ethics, ritual, tradition and religion.</em></blockquote><p>I can’t remember what it was like to see for the first time. Sight is something the majority of us take for granted. This video does an incredible job of portraying what it might be like to experience those first few moments of sight. Incredibly, this is not an artists interpretation, this is an algorithm figuring out how to discern objects within its field of vision.</p><ul><li>Portfolio: <a href="http://www.memo.tv/category/work/by-type/">memo.tv</a></li><li>Vimeo: <a href="https://vimeo.com/memotv">memotv</a></li><li>Twitter: <a href="https://twitter.com/memotv">@memotv</a></li></ul><h3>Lawrence Lek</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F210494259&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F210494259&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F626489234_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/cc3678e43b6c72213c38b5cb176796e5/href">https://medium.com/media/cc3678e43b6c72213c38b5cb176796e5/href</a></iframe><p><a href="https://vimeo.com/210494259">Geomancer (2017) — Trailer</a> from <a href="https://vimeo.com/lek">Lawrence Lek</a> on <a href="https://vimeo.com/">Vimeo</a>.</p><blockquote><em>Geomancer is a CGI film by Lawrence Lek about the creative awakening of artificial intelligence. Feauturing a neural network-generated dream sequence, and a synthesised vocal soundtrack, Geomancer explores the aesthetics of post-human consciousness.</em></blockquote><ul><li>Portfolio: <a href="http://lawrencelek.com/">lawrencelek.com</a></li><li>Vimeo: <a href="https://vimeo.com/lek">lek</a></li><li>Twitter: <a href="https://twitter.com/prosthetic">@prosthetic</a></li></ul><h3>Luba Elliot</h3><blockquote><em>Luba Elliott is a curator, artist and researcher specialising in artificial intelligence in the creative industries. She is currently working to educate and engage the broader public about the latest developments in creative AI through monthly meetups, talks and tech demonstrations.</em></blockquote><p>I’d like to thank Luba for taking the time to speaking at #GDGLondon and for introducing me to a new dimension to the field of Artificial Intelligence. If you are interested further, take a look at her overview of the <a href="https://medium.com/@elluba/creative-ai-landscape-1c7988a26c32">Creative AI Landscape</a>.</p><p><em>Originally published at </em><a href="https://billglover.me/2017/11/13/creative-ai/"><em>billglover.me</em></a><em> on November 13, 2017.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=891fcaac3ccb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What I learned by hosting a micro:bit meet-up]]></title>
            <link>https://medium.com/@BillGlover/what-i-learned-by-hosting-a-micro-bit-meet-up-dae0c67bf936?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/dae0c67bf936</guid>
            <category><![CDATA[learning-to-code]]></category>
            <category><![CDATA[personal-development]]></category>
            <category><![CDATA[microbit]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Fri, 12 Aug 2016 15:31:35 GMT</pubDate>
            <atom:updated>2016-08-12T15:31:35.528Z</atom:updated>
            <content:encoded><![CDATA[<p>On Tuesday evening I booked a meeting room in the office and hosted a meet-up for some of our recent joiners. The evening was open to anyone (internally) who was interested in finding out about the BBC micro:bit, but specifically targeted at our community of recent hires. As this was the first time I’d hosted an event like this, I wanted to share my experiences. In short though, it definitely won’t be the last time we do this.</p><h3>The BBC micro:bit</h3><p>Whilst many of you will have heard of the <a href="https://www.raspberrypi.org/">Raspberry Pi</a> and the <a href="https://www.arduino.cc/">Arduino</a>, there will be few, particularly those outside the UK, who have heard of the BBC <a href="https://www.microbit.co.uk/">micro:bit</a>, a new development board targetted at the next generation of creative thinkers.</p><p>In an effort to re-kindle the educational spirit behind the original <a href="https://en.wikipedia.org/wiki/BBC_Micro">BBC Micro</a> from the early 1980s, the BBC teamed up with 29 <a href="https://www.microbit.co.uk/partners">industry partners</a> to launch the BBC micro:bit. The device itself is tiny. It is over 18 times faster and 70 times smaller than it’s predecessor and includes an array of sensors (light, compass, accelerometer, buttons, temperature), a 5x5 array of LEDs, a Bluetooth radio, and a range of general purpose I/O pins.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/973/1*I0Vsi5T5CV-IIfF9BCAMwg.png" /><figcaption>The BBC <a href="https://www.microbit.co.uk/device">micro:bit</a> device</figcaption></figure><p>The target market for this device is clear. Every year 7 (12 years old) student in the UK is being issued with one of these devices for free. Teachers are being provided with training material, tutorials that can be followed in class and a platform on which to share the resulting projects.</p><p>During the brief time that I have had to spend with the micro:bit, I have been amazed at some of the other things that have been done to make the micro:bit classroom friendly. I encourage you to explore for yourselves, but here are a few of my favourites;</p><ul><li>5 programming languages from the Block Editor through to JavaScript, Python and C ensure that those of all levels can be creative from the start.</li><li>Key I/O pins are exposed with pads large enough that crocodile clips (or even fingers) can be used to attach external accessories.</li><li>No software installation required at all. The development environment is browser based (and yes, it works offline). You program the device by copying a file to a USB drive.</li><li>Privacy is considered from the outset with no sign-up at all required for students. To share creations on the gallery, it is the teachers that register on the site.</li><li>The gallery of projects encourages sharing of creative ideas and provides as inspiration for those just getting started.</li></ul><p>If you are looking for more advanced micro controllers, there are plenty out there but I have yet to come across anything that comes close to the micro:bit in terms of accessibility at the entry level. And, for those who have long since graduated from Year 7, the devices are available for purchase from a number of approved retailers. I picked up mine from <a href="https://shop.pimoroni.com/products/microbit">Pimoroni</a> for £13.</p><h3>The meet-up</h3><p>With three micro:bits in my possession, I figured I’d ask and see if anyone else was interested in exploring the devices after work one evening. An experienced colleague got the ball rolling by saying he’d support me in hosting an event and pushed me to schedule a date. Our meet-up was arranged.</p><p>The level of interest surprised me. Despite it being the height of the summer holiday season, we had 7 people turn up and many more express an interest. What impressed me most though was the level of interest from those with no technical background. All who turned up had little to no coding or technical experience. This was an event driven purely by interest and a desire to learn.</p><ul><li>a desire to learn more about technology and the industry we work in</li><li>a desire to keep ahead of younger relatives who are already involved</li><li>a curiosity that arose after seeing my request for interest</li></ul><p>I gave a quick background to the micro:bit, not much more than you have just read but probably more than was needed, and then we let people get stuck in. Some stayed to the end, others didn’t, but all bar one (more on that later) managed to get a little project running on the micro:bit with the device responding in some way to user input.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ivmR4V9HEC37LrZrbuR2MQ.jpeg" /><figcaption>Not your typical office meeting</figcaption></figure><h4>Ideas can be challenging</h4><p>With all the sensors available on the micro:bit the creative possibilities are incredible. I hadn’t anticipated that coming up with ideas for starter projects would be challenging, but it was.</p><ul><li>No one wanted to copy someone else&#39;s idea or just do something simple</li><li>Some wanted to follow a tutorial, others just wanted to explore</li><li>Balancing the complexity of an idea with something that is accessible as a starter project requires a little experience</li></ul><p>I was hesitant to suggest ideas as I really didn’t want to be prescriptive or risk influencing what people would come up with. But in this scenario, starting with the basics seemed to help build confidence. Displaying an image on screen, changing that image in response to user input was a quick way to get people seeing the micro:bit respond to code that they had written.</p><p><strong><em>Note for next time: </em></strong><em>I noticed that people wanted to work individually rather than in groups. But it was only when people spoke to each other that ideas for projects started to form. Next time I might try spending 10 minutes as a group brainstorming a list of ideas before we start on coding. This would allow groups to form around popular ideas or individuals to pick up ideas and run with them without me worrying that I was being to prescriptive about what people should make.</em></p><h4>Problems can always be broken down</h4><p>Once we had ideas for people to work on, I noticed that a common challenge was breaking those ideas down into small steps. This challenges was universal with all of us struggling with it at some point. But it is this skill that is invaluable not just for developers and engineers, but for project managers and business analysts as well.</p><p>Take one of the challenges we faced: How do I make an image flash on screen a number of times?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-UbL2tu6hByw_MrvGqq-nA.png" /><figcaption><strong>Left: </strong>common way to describe flashing an image <strong>Right:</strong> breaking this down to say how to flash an image.</figcaption></figure><p>When you discover there is no block like the one shown on the left (the image is a mock-up), the temptation is to stop and adapt your idea. In some cases the compromise is to go with a static image and abandon the notion of flashing. But challenging people to describe how they would flash an image yields thinking that is closer to the approach on the right which, in this case, is something that you can implement. Re-thinking concepts that we are familiar with, even something as basic as flashing an image, isn’t something that comes naturally to many. It’s like being asked to define a word you use regularly and finding yourself struggling to offer up a defintion. We’ve all been there.</p><p>In observing people go through this process of breaking down problems, I found it fascinating that people were starting to make connections between coding for the micro:bit and tasks they considered their day job. A business analyst responsible for mapping out a process is performing very similar tasks to a developer trying to piece together a program. The context may be different, but in both cases the goals are similar; to express a solution to a problem in a concise and logical way.</p><p><strong><em>Note for next time: </em></strong><em>Challenging people to break down problems is one of the things I found myself doing frequently throughout the evening. In all cases, getting people describing the steps they would go through to solve a problem gave people the insight required to implement it. Given how crucial this skill is, it might be an idea at future events to get people to describe their proposed implementations verbally, either to the group or to each other, before then jumping in to implement them on the device.</em></p><h4><strong>Physical devices over simulators</strong></h4><p>Many of the languages for the micro:bit come with a simulator (Python and C being the exceptions). Whilst the simulator is fun and makes for a quick way to test ideas, the most satisfying moments were when people could see their creations running on the physical devices. There was joy, there were smiles. It was very much a moment of pride. It may seem obvious to anyone who has enjoyed that moment of delight the first time their code runs on a device, but simulators are no substitute for a physical device.</p><p>I’m pleased to say that by and large the experience of the simulator was good and that code that ran in the simulator also ran on devices. We did have one case where the compilation of code seemed to result in an empty project file. This was a shame as we were unable to get a fairly simple first project running on the device. What was more frustrating is that I wasn’t able to explain why things weren’t working.</p><p><strong><em>Note for next time: </em></strong><em>The simplicity of the micro:bit is wonderful, but when things don’t go as expected it can be a little challenging to know where to turn. We had the added complexity of the fact that restrictions on corporate laptops prevent anyone from copying code to the micro:bit devices. This meant we resorted to emailing copies of compiled code for me to load onto the devices. Simplifying this process for next time would reduce the chance of things going wrong. Specifically:</em></p><ul><li><em>Encourage people to name their projects (the default names are repetitive).</em></li><li><em>Get access to USB ports that are writeable if at all possible. If not, create a shared folder that people can use to simulate loading the code onto their devices.</em></li><li><em>Encourage people to remove old files from the downloads folder to avoid confusion between project versions.</em></li><li><em>Encourage people to switch between code view and block view to see a different take on the language used to describe their project.</em></li></ul><h4>Next-time</h4><p>Initially I was worried that no one would turn up to the event. When people did turn up, I was worried they’d be bored and feel like they’d rather be elsewhere. I learned that these worries were unfounded.</p><p>The micro:bit isn’t just for children — we had great fun. And, whilst I love playing with the Raspberry Pi and the Arduino, I could never have hosted a room of non-techies with those platforms and had as much fun as we did with the micro:bit on Tuesday. I’d recommend this to anyone. If you have an interest, or even just a curiosity, open it up to others.</p><p>I’m personally delighted that we were asked to run the event again, both by attendees and those who were unable to make it. This is the best indication that the event was a success. The date is already in the diary and, now that the first one is out of the way, I’m looking forward to it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bbkYBTiISrBL-2Ek39-MFQ.jpeg" /><figcaption>The morning after — trying to get to the bottom of the one project that didn’t run on device. Stumped!</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dae0c67bf936" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Capacity of Slices in Go]]></title>
            <link>https://medium.com/@BillGlover/the-capacity-of-slices-in-go-542256c7ac2?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/542256c7ac2</guid>
            <category><![CDATA[learning-to-code]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Sun, 07 Aug 2016 11:27:25 GMT</pubDate>
            <atom:updated>2016-08-07T11:27:25.170Z</atom:updated>
            <content:encoded><![CDATA[<p>Working through the <a href="https://tour.golang.org/moretypes/13">Tour of Go</a> I came across the following code snippet designed to demonstrate how to create slices of a given length and capacity.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6e745f13c9af09617734601cb61da98a/href">https://medium.com/media/6e745f13c9af09617734601cb61da98a/href</a></iframe><p>Before running any code examples such as this, I attempt to work out what the programme might do. In this case, I expected to see the following</p><pre>a len=5 cap=5 [0 0 0 0 0]<br>b len=0 cap=5 []<br>c len=2 cap=5 [0 0]<br>d len=3 cap=5 [0 0 0]</pre><p>If you run this code you’ll see that this isn’t quite what happens and that the capacity of slice ‘d’ is actually 3 and not 5.</p><pre>a len=5 cap=5 [0 0 0 0 0]<br>b len=0 cap=5 []<br>c len=2 cap=5 [0 0]<br>d len=3 cap=<strong>3</strong> [0 0 0]</pre><p>I tried a couple of similar examples but couldn’t explain the reduced capacity of slice ‘d’. My understanding of slices is that they are a data structure that describes a subset of a fixed capacity array. Based on this understanding, I interpret this code snippet as follows:</p><ul><li>‘d’ is a slice of existing slice ‘c’</li><li>‘c’ is a slice of existing slice ‘b’</li><li>‘b’ is a slice of an underlying array that has a fixed capacity for 5 integers</li></ul><p>As ‘b’, ‘c’, and subsequently ‘d’ all represent subsets of the same underlying fixed capacity array I couldn’t understand two things about the result:</p><ol><li>that the capacity of ‘d’ was 3 and not 5, and</li><li>if we accept (1) as given, that the capacity of ‘c’ was not 2</li></ol><p>At this point, I turned to the this post on the Go Blog, <a href="https://blog.golang.org/slices">Arrays, slices (and strings): The mechanics of ‘append’</a>. It is extremely well written and well worth a read if you really want to understand the mechanics of slices. But it would have been easy to miss the following statement made mid-way through the post.</p><blockquote>“The Capacity field is equal to the length of the underlying array, minus the index in the array of the first element of the slice.”</blockquote><p>This sounds like it might explain the behaviour I was seeing, but to fully understand it we need to understand a little bit about the structure of a slice, often referred to as the slice header.</p><pre>// slice b</pre><pre>slice := sliceHeader{<br>    Length:        0,<br>    Capacity:      5,<br>    ZerothElement: *int,<br>}</pre><p>For the initial slice ‘b’ this should be fairly self explanatory, we are describing a subset of an array of integers that has capacity 5 but length 0. Now, bearing in mind the statement on slice capacity, we can attempt to create similar slice headers for our other slices, ‘c’ and ‘d’.</p><pre>// slice c := b[:2]</pre><pre>slice := sliceHeader{<br>    Length:        2,<br>    Capacity:      5,<br>    ZerothElement: &amp;b[0], // element [0] of b<br>}</pre><pre>// slice d := c[2:5]</pre><pre>slice := sliceHeader{<br>    Length:        5,<br>    Capacity:      3,     // see text for explanation<br>    ZerothElement: &amp;c[2], // element [2] of b<br>}</pre><p>In the case of ‘c’ the capacity of the underlying array is 5, the index of the first element in the slice is 0 and so the capacity is 5. In the case of ‘d’ the capacity of the underlying array is still 5, but the index of the first element in the slice is 2 and so the slice ‘d’ has capacity 3. And this is exactly what we see in the results of the example.</p><p>One last point to note is that in the results of the code snippet, the length of ‘b’ is zero. This refers to the length of the slice ‘b’ and not the length of the underlying array which is 5. It is easy to trip up and assume that the length of ‘b’ is refering to the length of the array, it isn’t.</p><pre>a len=5 cap=5 [0 0 0 0 0]<br>b len=0 cap=5 []<br>c len=2 cap=5 [0 0]<br>d len=3 cap=<strong>3</strong> [0 0 0]</pre><p>Mystery solved.</p><p>The thing I don’t yet understand is why this definition of slice capacity was designed this way, any hints would be most welcome.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=542256c7ac2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The success of Apple Pay in China]]></title>
            <link>https://medium.com/@BillGlover/the-success-of-apple-pay-in-china-97e96585468a?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/97e96585468a</guid>
            <category><![CDATA[payments]]></category>
            <category><![CDATA[apple-pay]]></category>
            <category><![CDATA[china]]></category>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Mon, 22 Feb 2016 10:31:01 GMT</pubDate>
            <atom:updated>2016-02-22T10:31:01.086Z</atom:updated>
            <content:encoded><![CDATA[<p>As I flew back from China earlier this week, Apple pulled the trigger and launched Apple Pay in China, initially partnering with China UnionPay (中国银联) and the Industrial and Commercial Bank of China, ICBC (中国工商银行). Other banks are sure to follow soon.</p><blockquote>“Apple Pay launching in China today. Can’t wait for you to try it and see how incredibly easy it is to use! Apple Pay 于今日正式登陆中国。欢迎大家踊跃尝试，体验它卓越的便捷性！” — Tim Cook, Apple CEO, via <a href="http://weibo.com/5524254784/Dim8ol1hd?from=page_1005055524254784_profile&amp;wvr=6&amp;mod=weibotime&amp;type=comment">Sina Weibo</a></blockquote><p>Much has been made of the launch in the technology press with many treating Apple’s chances of success with a healthy dose of scepticism. None of the mainstream technology press brought home the scale of the challenge presented by the incumbents WeChat Pay and Alipay. Not only are they on home turf, but they have also had several years to establish a position of strength in the market. Apple is clearly the outsider. As if this wasn’t enough, Xiaomi (小米科技) and Huawei (华为) are both rumoured to be readying payment platforms of their own.</p><p>During my recent trip to China, it became evident just how real the challenge ahead of Apple is. And, whilst many in the press have cast doubt on Apple’s chances of success, I wanted to present my take on what they need to do if Apple Pay is to stand any chance of being successful.</p><h3>Apple Pay in the UK</h3><p>I have yet to personally experience Apple Pay in the US and so have based my comparisons with China on my experience of the service here in the UK.</p><p>When Apple Pay launched in the UK back in July 2015 Apple were able to take advantage of the proliferation of contactless payment terminals in the majority of retailers. Whilst Apple launched with a number of prominent retail partners, the reality was that Apple Pay was more widely available than people initially realised. Many of the contactless payment terminals supported Apple Pay with no action required on behalf of retailers. In many cases, retailers didn’t even appear to know that they supported Apple Pay. Simply asking to pay by card and holding your phone to the terminal was all that was required to use the service. It wasn’t long before I found myself able to get through a typical week without using cash or even a bank card.</p><p>We must remember that prior to the launch, card payments were commonplace even for relatively small transactions. Some smaller retailers insisted on a minimum transaction value to ensure they recovered the card processing costs but you could generally expect to be able to pay for most common transactions by card. I mention this, because this isn’t true of many countries and it is often easy to forget that our norms don’t necessarily apply elsewhere. Apple was able to take advantage of this infrastructure, and both customer and retailer expectation to launch a new service and whilst I don’t wish to trivialise their achievements, it is important to remember their starting position.</p><h3>The Landscape in China</h3><p>When I first visited China back in 2006, I went to buy a pair of shoes in a mall in Beijing. With the Visa logo on prominent display next to the cashier’s desk, I handed over my Barclaycard expecting to be asked to type in a pin or to sign a payment authorisation slip. What I did not expect was for the clerk to pick up the phone and make a telephone call to Visa to process the payment. Card payments, at least those not with Union Pay were clearly not common place. Even with Union Pay, it was clear that cash was still the payment method of choice for many retailers.</p><p>Now, some ten years later, I’m blown away by the prevalance of the mobile payments platforms, with many retailers actively pushing them over card payments, and in some cases even cash. Following a meal with a friend, we offered to pick up the bill. Our friend had taken a couple of days away from family to show us around the city of Guangzhou (广州) so we thought it only fair to treat her to a meal. But there was a challenge, neither of us had WeChat Pay and the restaurant was offering a significant discount to those who paid using WeChat. Our friend would receive even more of a discount as she had booked the table using Dianping (大众点评), an App in which Tencent, WeChat’s parent company, has an investment. Not only that, the waitress preferred to receive a WeChat payment over cash as it made things easier as she didn’t have to hand over precious small change. I was so surprised by this that before you know it, the waitress had written down the authorisation code and our meal was paid for. We never did get to treat our friend to that meal.</p><p>I was curious so probed a little further. Our friend pays for utility bills, travel, meals, groceries, visa renewals and even doctors appointments through WeChat. To her, this was convenient, often cheaper but above all, it was normal.</p><p>I asked around to see whether this was the norm and to my surprise I found that not only is it the norm, but it doesn’t seem confined to the younger generations. My in-laws also use WeChat Wallet. And, whilst I don’t want to generalise based on my short time in China and the limited number of people I have spoken to, but the impression I was left with was that if people didn’t use WeChat Pay themselves, they at least knew what it was.</p><p>At this point I should highlight that WeChat Pay isn’t the only prominent mobile payments service in China. AliPay has been around longer and is accepted by a wide range of retailers. Whilst in China I didn’t experience of AliPay first hand and they require the use of a Chinese bank card for registration so I was unable to try myself. However, for an indication of how common it is, I refer you to this article by Steven Millward of Tech In Asia when he <a href="https://www.techinasia.com/day-with-wechat-payments-in-stores">tried to survive an entire day using WeChat to pay for stuff</a>. There are almost certainly other players in the market but WeChat Pay and AliPay are the two most dominant.</p><h3>Apple’s Challenge</h3><p>Whilst the individuals I spoke to cannot possibly be regarded as a representative sample of the Chinese population, I found it interesting that none were familiar with Apple Pay or how it worked. With the launch imminent, the Apple marketing machine would surely address this. But I could see something more than just a lack of knowledge, the people I spoke to just weren’t that interested. They could already pay for things on their phones, why would they want to use a service provided by Apple? Just what was it that Apple was offering them?</p><h3>Bill / 葛威 on Twitter</h3><p>If you think Apple Pay breaks new ground, you have to see how WeChat payments dominate China. Household bills, food &amp; transport. #fintech</p><p>It would be easy to write Apple’s efforts off. Indeed, many in the industry already have. But here is what I think Apple needs to do in order to be successful with mobile payments in China.</p><p>1. Identify a clear value proposition</p><p>2. Incentivise retailers</p><p>3. Allow peer to peer payments</p><p>4. Play nice with the regulatory bodies</p><p>Whether or not Apple Pay is successful will depend on how well Apple can address these four essential objectives.</p><h4>Identify a clear value proposition</h4><p>When Apple Pay launched in the US, the proposition was clear; convenience and security. By the time they launched in the UK, the value proposition was less clear. We already had the convenience of contactless payments, so perhaps it was just simply security. We don’t yet know how successful these launches have been, but the promise is there. It isn’t yet clear to me that these values alone are enough to entice Chinese consumers away from established offerings. Concerns around data privacy, security, etc. all seem secondary to price and convenience. Apple has to compete on price in order to be successful, even if it means forgoing its usual transaction fees and persuading the banks to do the same. Whilst I don’t see Apple being able to offering the discounts that the other major players are offer, there are signs that Apple and its partners are already starting to offer users some incentives. Bank of China offering everything from loyalty points, discounts, and cash back to customers who use Apple Pay. Being able to compete on price is going to be key to success in China.</p><p>But price alone may not be enough. Apple needs to offer the consumer convenience and, in China, that may be at odds with Apple’s desire to control the full end to end user experience. Apple needs to accept that consumers demand services and functionality that exist outside the controlled Apple ecosystem and open up the wallet to its partners.</p><p>Concerns around security and privacy may be secondary to price, but this is one area where Apple is able to offer a unique proposition. It may be an American company, and there will undoubtedly be concerns around using an American company to handle payments, but Apple’s brand reputation is strong and their recent <a href="https://www.apple.com/customer-letter/">decision to fight a court order to assist the FBI</a> in gaining access to an iPhone has gone down well with the Chinese.</p><p>Whatever Apple settles on as their value proposition, they need to get the message out to consumers and to do so quickly. With WeChat and AliPay are installed on the vast majority of phones in China (not just those made by Apple), both players have a significant head start.</p><h4>Incentivise Retailers</h4><p>Apple’s main competitors offer retailers prominent placement within their Apps, enabling retailers to offer additional services right alongside the interface customers use to make payment. Apple’s desire to control the complete experience around Apple Pay has limited what others can do inside the wallet. Whilst loyalty cards are offered, they are limited in functionality and in reality have retailers have been slow to adopt them even in Apple’s more established geographies. Being able to enhance the wallet experience will be key to encouraging retailers to invest in the platform.</p><p>When it comes to infrastructure, Apple needs to incentivise the roll-out of the contactless terminals required to make a payment. Contactless payment terminals are not cheap and unless there is a clear incentive for retailers the Apple Pay platform will be held back by the inability for consumers to use it. If it becomes known as the payment platform that no retailers accept, consumers will quickly lose interest and Apple will struggle to regain the initiative. In some ways, Apple risks falling into the same trap as American Express who missed the boat on the contactless roll-out in the UK and, with their higher transaction fees, provide little incentive for retailers to upgrade their payment terminals to support AMEX contactless payments.</p><p>By partnering with Union Pay (Apple didn’t have a choice here), there is hope that Union Pay will support the roll out of the required terminals to retailers. However, both WeChat and Alipay are rumoured to be working on their own point of sale (POS) systems and both have demonstrated a cut-throat ability to muscle their way into a new market.</p><h4>Allow peer to peer payments</h4><p>Apple has yet to enable customers in any geography to use Apple Pay to transfer money between consumers. I suspect one of the key limitations behind this is the existing arrangement that Apple has with their partner banks. However the success of competitor peer to peer payment offerings will present a challenge.</p><p>During the recent New Year celebrations in China, WeChat users sent 32.1 billion Red Packets (红包) to each other, 8 billion of these on New Year’s Eve alone. These are monetary gifts of anywhere up to RMB200 (GBP20). When sent to groups of friends, the amount received by individuals in the group was randomised to add a sense of luck or chance to the event. These numbers are staggering and Apple Pay offers no alternative. If Apple is going to establish itself as a serious player in the mobile payments market in China, it is going to have to enable the low value, high volume consumer to consumer transactions that drove adoption of both AliPay and WeChat Pay over the last couple of years.</p><h4>Play nice with the regulator and the party</h4><p>Apple is a foreign company and it must remember that. The experience of foreign companies in China has shown that Apple is unlikely to be allowed to dominate the industry. If local competitors don’t get there first, you can be sure the <a href="https://twitter.com/relevantorgans">relevant organs</a> will help them out. I suspect that along the journey Apple will be required to make difficult decisions that may not endear it to it’s consumers back home. Striking a balance between keeping the Party and the various regulatory factions happy whilst not compromising on its core values will be key to the success of Apple Pay in China.</p><h3>The Outlook</h3><p>Foreign technology companies don’t have a great track record when it comes to China. But despite the bearish attitude in the technology press to the recent Apple Pay launch, I suspect Apple is uniquely placed among Western technology companies to give it a go. Brand familiarity is high and that is not something that can be said for many of Apple’s Western competitors. But, investors would do well not to get carried away.</p><blockquote>“We think China could be our largest Apple Pay market,” — Jennifer Bailey, vice president of Apple Pay, via <a href="http://www.reuters.com/article/us-apple-payment-china-idUSKCN0VQ302">Reuters</a></blockquote><p>Apple have been deliberate in their choice of words. And, whilst they are unlikely to be the largest player in China, even small market penetration in China could easily make China the poster child for Apple Pay success. Whatever the outcome, the future of mobile payments in China will be interesting to watch.</p><h4>Thanks</h4><p>I’d like to offer my thanks to both <a href="https://twitter.com/alicialiu">@alicialiu</a>, and <a href="https://twitter.com/The3rocks">@the3rocks</a> for help in research and proofreading for this post. I’d also like to extend my thanks to Cassie for the meal in Guangzhou that inspired me to write.</p><p><em>Note: This post has been cross-posted from my </em><a href="http://billglover.me/2016/02/21/the-success-of-apple-pay-in-china.html"><em>blog</em></a><em> where it originally appeared on 21st February, 2016.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97e96585468a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The art of ‘just good enough’]]></title>
            <link>https://medium.com/@BillGlover/the-art-of-just-good-enough-f0a5775a52ba?source=rss-194cbbb37a2------2</link>
            <guid isPermaLink="false">https://medium.com/p/f0a5775a52ba</guid>
            <dc:creator><![CDATA[Bill / 葛威]]></dc:creator>
            <pubDate>Sun, 12 Jan 2014 13:26:14 GMT</pubDate>
            <atom:updated>2014-01-12T13:33:10.831Z</atom:updated>
            <content:encoded><![CDATA[<h4>- or why I no longer finish anything</h4><p>It doesn’t seem all that long ago when I used to delight in being able to call myself a perfectionist. I was proud of the fact that I could never finish anything because it was never finished until it was perfect. However, 9 years into my career and I am increasingly conscious of the fact that I am still no better at being a <a href="http://en.wikipedia.org/wiki/Team_Role_Inventories">finisher</a>. The difference is, I can no longer justify my inability to finish anything on a never ending search for perfection. I am a part of a generation for whom ‘just good enough’ is the end goal.</p><h4>The Age of Beta</h4><p>What started as the Gmail Beta has now become standard practice across many industries. In an age where being first matters more than being right, we consider it acceptable to release unfinished applications and software, or to publish unfinished blog posts and articles. We expect 24 hour news networks to deliver credible retrospective analysis on events which are still unfolding as we watch.</p><blockquote>It is more important to be first than it is to be right.</blockquote><p>Don’t blame the publishers. Collectively we demand to be kept more informed faster than ever before. We demand early access to the next big thing. Who cares, if it is not finished yet? All we demand is that it is ‘just good enough’. We have grown accustomed to watching inane speculation as dramatic world events unfold, reading unfinished articles, and working with incomplete products. Why? We put up with it as a price for early access to tools and information.</p><p>For the Twitter generation, even the news networks are not fast enough. By now, we are all too aware that this pace comes at the expense of accuracy. Many a journalist has opined the loss of accuracy in reporting today and so I don’t intend to further that particular debate. But for me we have also lost something else in this rush to be first; we have lost a sense of what it means to finish something. This is true for software development, just as it is true for journalism and publishing.</p><h4>Release Early and Often</h4><p>There is a school of thought that says we should release early and release often. Without doubt, this approaches carry a number of benefits.</p><ul><li>Early feedback from your customers/readers</li><li>Validation of a product before significant investment</li><li>First mover advantage</li></ul><p>However there is a fine balance between releasing early and releasing prematurely. All too often, the question of when something is actually finished is largely ignored in favour of getting something out there fast. Releasing a product with a carefully selected subset of features that are complete is very different from releasing a product that teases the user with many features, none of which are quite finished.</p><p>The distinction may be obvious but it is important. We don’t have to be feature complete to finish something. There is no need to release an article or blog post that is a work in progress. It is possible to complete an article without waiting to see if circumstances change or new events unfold. By all means release early, but release when the article is finished, not before.</p><h4>Just Good Enough</h4><p>Releasing early and often should be a concept familiar to those involved in the hip world of start-ups and their associated funding schemes. However it is not just start-ups that are losing the ability to finish anything. We all are, including those of us working in large enterprises. Whether an internal system or a new customer facing product, the goal should always be to finish. At every level from developer, development team, project team or project sponsor it should be clear what it means to be finished. You’d be surprised how often this isn’t clear.</p><ul><li><strong>Developer:</strong> Is this code finished when written? When tested? Or when in production? When it performs well?</li><li><strong>Project:</strong> Is the release finished when it has features A, B, and C? Or does it need feature D as well?</li><li><strong>Sponsor:</strong> Is the project finished when some code makes it through to production? When the first customers use it? When the investment case is realised?</li></ul><p>What may also surprise you is that every interested party will have a different definition of what it means to be finished. If no one can agree on what it means to be finished, is it ever possible to finish?</p><blockquote>Take any project you are working on now and ask the same question, “When will this be finished?”</blockquote><p>Outside of software delivery we can ask similar questions.</p><ul><li><strong>Hacker: </strong>Is the hack complete when the you can see how to solve a problem? When you have a proof of concept solution? Or when you have a solution that you are proud to show to others?</li><li><strong>Writer:</strong> Is the article finished when drafted? When proof read? When published? When first read by someone else?</li><li><strong>Photographer: </strong>Is the picture finished when the shutter closes? When the negative is developed (or edited image saved to disk)? Or when the photograph is framed and hanging on a wall?</li><li><strong>Cook:</strong> Is dinner finished when it comes out of the oven? When the last morsel has been consumed? Or when the dishes are washed up and put away?</li></ul><p>All to often though, the focus is on “just good enough”. We don’t know what finished means and so we settle for just good enough to move on. As we strive to be first, we have lost the ability to finish anything.</p><p>I count myself as one of the biggest offenders. I don’t challenge myself or my team enough with this question, when are we finished? How will we know? Just prior to Christmas I asked a couple of the team at work and was unsurprised to find that they all had different answers.</p><h4>In Conclusion (Finishing)</h4><p>The art of ‘just good enough’ is just that, not good enough. As we rush through life, making sacrifices in order to be first (or at least fast), we need to stop and ask ourselves what it means to finish something. When something is finished we have an opportunity to feel pride, an emotion all too often lacking in the workforce of today. But above all, finishing means we can do just that, finish. We can move on to the next thing free from the lingering thoughts and distractions that stem from unfinished work. For me, I’m going to start by finishing this article.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f0a5775a52ba" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>