Cristian Adam

Octopress on Windows

I have started using Octopress last year for my other blog (in Romanian)

Octopress is advertised as “a blogging framework for hackers”. As a hacker one “should be comfortable running shell commands and familiar with the basics of Git”. But it all comes down to ruby.

If you’re a Windows hacker what do you do? My first idea was to install Cygwin.

For I’ve used Cygwin to run Octopress. I had a laptop with an Intel Core i7 CPU, didn’t notice any slowdowns.

By the time I’ve moved this blog to Octopress I didn’t have access to that Intel Core i7 powered laptop, but instead I had an Intel Core 2 Duo powered laptop. Then I’ve noticed that Octopress was rather slow on Cygwin.

That’s when I’ve started looking for alternatives to Cygwin.

The other options for running Ruby on Windows are:

  • RubyInstaller - The easy way to install Ruby on Windows
  • Running Ruby on Linux in a virtual machine
  • Compiling Ruby by myself smile

Octopress requires a few ruby libraries (gems) which require a native C/C++ compiler.

RubyInstaller provides a development kit, which is a “MSYS/MinGW based toolkit than enables you to build many of the native C/C++ extensions available for Ruby”.

Since Visual Studio 2013 Professional has been offered freely as the Community Edition, I decided to use Visual C++ to compile Ruby.

In my experience Visual C++ generates faster and smaller binaries than MinGW (GCC for Windows).

Before starting to compile Ruby one needs to compile some dependencies:

  • OpenSSL - requires Perl for building
  • Zlib - has a CMake configuration, very nice!
  • libffi - the worst of the lot, very unfriendly on Windows

Ruby can be compiled with Visual C++. Ruby provides a NMake makefile script. Building with NMake can be very slow because it runs only on a CPU core. I’ve tried to use Jom - the parallel NMake clone, unfortunately the Ruby makefile is not parse-able by Jom, it was complaining about some recursive declarations.

I haven’t mentioned the Ruby version I was trying to compile and when I’ve done this exercise. I’ve compiled ruby 2.0.0p598 at the beginning of March, I’ve chosen ruby 2.0 because at that time the newer version was crashing at runtime. Also that’s the same ruby version that Cygwin was providing back then.

Ruby 2.0.0.p598 doesn’t compile successfully out of the box with Visual C++ 2013 64bit.

The following patch does the trick:

diff -Naur old/ruby-2.0.0-p598/ext/extmk.rb new/ruby-2.0.0-p598/ext/extmk.rb
--- old/ruby-2.0.0-p598/ext/extmk.rb    2013-08-03 16:08:18.000000000 +0200
+++ new/ruby-2.0.0-p598/ext/extmk.rb    2015-07-19 14:26:08.240716600 +0200
@@ -664,7 +664,7 @@
       mf.print "#{tgt}:\n\t$(Q)$(MAKE) "
       mf.print "$(MFLAGS) "
       if enable_config("shared", $enable_shared)
-        mf.print %[DLDOBJS="$(EXTOBJS) $(ENCOBJS)" EXTSOLIBS="$(EXTLIBS)" ]
+        mf.print %[DLDOBJS="$(ENCOBJS)" EXTSOLIBS="$(EXTLIBS)" ]
         mf.print 'LIBRUBY_SO_UPDATE=$(LIBRUBY_EXTS) '
         mf.print %[EXTOBJS="$(EXTOBJS) $(ENCOBJS)" EXTLIBS="$(EXTLIBS)" ]
diff -Naur old/ruby-2.0.0-p598/win32/Makefile.sub new/ruby-2.0.0-p598/win32/Makefile.sub
--- old/ruby-2.0.0-p598/win32/Makefile.sub    2014-10-15 08:58:02.000000000 +0200
+++ new/ruby-2.0.0-p598/win32/Makefile.sub    2015-07-19 14:27:15.918484900 +0200
@@ -212,7 +212,7 @@
 !if !defined(LDFLAGS)
-LDFLAGS = -incremental:no -debug -opt:ref -opt:icf
+LDFLAGS = -incremental:no -debug -opt:ref -opt:icf -force:multiple
 !if !defined(XLDFLAGS)
 XLDFLAGS = -stack:$(STACK) 

Compiling Ruby is not enough, one needs to compile the required gems for Octopress.

Bundler will try to compile the missing gems when you issue bundle install. This is why one needs to run all these commands from VS2013 x64 Native Tools Command Prompt.

The following gems required patches:

Now I had a running Octopress setup. But after a quick benchmark I’ve noticed that the RubyInstaller had a faster binary build with MinGW 4.7.2 x64 disappointed

Then I decided to do a Profile Guided Optimization (PGO) build. This required a few modifications for Makefile.sub: OPTFLAGS = -O2sy- -GL and LDFLAGS = -incremental:no -debug -opt:ref -opt:icf -force:multiple -LTCG:$(LTCG).

I had LTCG as a shell variable because one needs to compile the binaries twice and editing Makefile.sub would trigger a rebuild. The value for $(LTCG) is set first as /LTCG:PGINSTRUMENT and after instrumentation set as /LTCG:PGOPTIMIZE or /LTCG:PGUPDATE.

I also had to hack mkexports.rb to export all final symbols instead of progressively gather them from the shared objects.

At the end I had an PGO optimized build for Octopress which was faster than RubyInstaller metal

And now for some numbers. I have tested RubyInstaller x64, ruby 2.0 installed on a Kubuntu 14.10 x86 on Virtual Box and VMware Player, ruby 2.0 installed on Kubuntu 14.10 x64 on the same machine, Cygwin x64. The Core 2 Duo laptop was not able to run x64 virtual machines, that’s due to a hardware limitation.

I used MSys Git (because it had bash and no ruby) to run this script for

for i in {1..2}; do rake generate; done
time for i in {1..10}; do rake generate; done 

This means that the times below are for ten rake generate commands:

Ruby Time Time with antivirus enabled
ruby 2.0.0p598 [x64-mingw32]
2m7.343s 2m32.256s
ruby 2.0.0p598 [x64-mswin64_120]
Visual C++ 2013
2m17.998s 2m43.675s
ruby 2.0.0p598 [x64-mswin64_120]
Visual C++ 2013 PGO Optimized
2m2.117s 2m27.561s
ruby 2.0.0p598 [x86_64-cygwin]
7m12.776s 7m35.724s
ruby 2.0.0p457 [i386-linux-gnu]
VMware Player 7.1.0
1m31.512s -
ruby 2.0.0p457 [i386-linux-gnu]
VirtualBox 4.3.24 (ICH9, I/O APIC Enabled)
2m15.348s -
ruby 2.0.0p457 [i386-linux-gnu]
VirtualBox 4.3.24 (PIIX3, I/O APIC Disabled)
2m32.534s -
ruby 2.0.0p457 [x86_64-linux-gnu]
Kubuntu 14.10
1m9.711s -

From these numbers we can see that running ruby on Cygwin is a bad idea. Running ruby in a virtual machine with VMware Player was the fastest option on Windows. Running ruby on Linux natively produced the fastest results.

The antivirus penalty seems to be similar for all Windows options. had like ten articles, therefore I decided to test my old blog entries imported into Octopress. My old blog had like one hundred articles.

Ruby Time Time with antivirus enabled
ruby 2.0.0p598 [x64-mingw32]
5m52.670s 6m19.283s
ruby 2.0.0p598 [x64-mswin64_120]
Visual C++ 2013
6m50.686s 7m18.096s
ruby 2.0.0p598 [x64-mswin64_120]
Visual C++ 2013 PGO Optimized
5m23.810s 5m48.801s
ruby 2.0.0p598 [x86_64-cygwin]
11m14.888s 11m42.282s
ruby 2.0.0p457 [i386-linux-gnu]
VMware Player 7.1.0
6m10.477s -
ruby 2.0.0p457 [i386-linux-gnu]
VirtualBox 4.3.24 (ICH9, I/O APIC Enabled)
7m29.291s -
ruby 2.0.0p457 [i386-linux-gnu]
VirtualBox 4.3.24 (PIIX3, I/O APIC Disabled)
7m32.479s -
ruby 2.0.0p457 [x86_64-linux-gnu]
Kubuntu 14.10
4m58.092s -

The results are a bit different. Cygwin is still slow, but now the fastest option on Windows is no longer VMware Player but instead the PGO Visual C++ build. Ruby on Linux is still the fastest option for that Core 2 Duo laptop. The difference from Linux and Windows PGO Visual C++ is not that dramatic.

Below you have the Visual C++ 2013 ruby 2.0.0p598 x64 binaries:

In order to use them you need to install Visual Studio Community Edition. Install the patched ruby gems gem install --local path_to_gem/filename.gem from the VS2013 x64 Native Tools Command Prompt window.

After a successful bundle install one just needs to run Ruby-x64-pgo\setrbvars.cmd from a command prompt and then the usual rake new_post[""], rake generate, or rake preview commands!

If you use Octopress on Windows, give this build a try wink