Blogroll: CloudFlare

I read blogs, as well as write one. The 'blogroll' on this site reproduces some posts from some of the people I enjoy reading. There are currently 34 posts from the blog 'CloudFlare.'

Disclaimer: Reproducing an article here need not necessarily imply agreement or endorsement!

Subscribe to CloudFlare feed CloudFlare
Helping Build a Better Internet
Updated: 2 hours 57 min ago

Managing DNS Records For The People With Cloudflare Apps

Fri, 14/12/2018 - 13:00
Managing DNS Records For The People With Cloudflare Apps

DNS records are hard.  Many people, even the technically competent, don’t bother to learn more than the basics.

Managing DNS Records For The People With Cloudflare Apps

You could be one of them — someone who learns just enough about DNS to get it working, then immediately forgetting everything until the next time it breaks — and the vicious cycle continues.  The definition of insanity comes to mind. Meanwhile, non-technical folks wisely avoid meddling in such dangerous psychology.

Surely, there must be a better way (this is a blog post after all).

A new feature allows Cloudflare Apps to set up configurable DNS records on more than 12 million registered domains on the Cloudflare network. Cloudflare Apps are here to alleviate the internet’s collective DNS woes.  Gone are the days of tribulating over whether it’s A or CNAME you should set.  Gone are the days of puzzling between A and AAAA records while wondering what the heck happened to AA and AAA records???  Unload your DNS dysphoria onto highly trained developers experienced at explicating these burdensome questions today!

Are you a highly trained developer?  Install Pointless DNS on your site to try out the new feature.  This pointless app installs a TXT record on any root or subdomain of your choice.  

Managing DNS Records For The People With Cloudflare AppsI set the TXT record name to "blog" during installation

Once installed, head over to your DNS dashboard and you'll see the app doing its thing.  If you really want, you can uninstall it from your Installed Apps page.

Managing DNS Records For The People With Cloudflare AppsPointless DNS manages its pointless record on my blog subdomain

To start building an app, download create-cloudflare-app and open it in your preferred text editor.  Below, I’ll explain how Pointless DNS manages configurable DNS records.

In the install.json file, you’ll find a dns field that looks like this:

# install.json "dns": [{ "type": "TXT", "content": "Managed TXT Record", "name": "Created by create-cloudflare-app", "ttl": 120 }]

This says a TXT record will be set up and managed on the app installer's site, which can only be modified or deleted through the app’s configuration page.  An app can create or modify any type of Cloudflare DNS record available in Cloudflare's DNS API.

Now let's add in some configuration. Add an install option and point it at your record — options.subdomain will be set to the installer's desire.

# install.json { "options": { "properties": { "subdomain": { "order": 1, "type": "string", "title": "TXT Record Subdomain", "description": "The subdomain of your DNS record", "placeholder": "*Required - e.g. [your_domain].com", "required": true } } }, "dns": [{ "type": "TXT", "name": "{{options.subdomain}}", "content": "{{options.subdomain}} was set" }] }

Check out the Cloudflare Apps docs to learn more about about building DNS apps.  For additional inspiration, consider Mailchannels — a powerful email security solution that manages DNS records to fight off pesky spammers and phishermen.

Managing DNS Records For The People With Cloudflare Apps

DNS records used to be hard, but a new breed of Cloudflare Apps makes them easy.  Cloudflare Apps-based DNS configuration gives developers the opportunity to be the hero people want and the hero they need.

Categories: Technology

Traffic Acceleration with Cloudflare Mobile SDK

Thu, 13/12/2018 - 13:00
Traffic Acceleration with Cloudflare Mobile SDK

We’re excited to announce early access for Traffic Acceleration with Cloudflare Mobile SDK. Acceleration uses novel transport algorithms built into the SDK to accelerate apps beyond the performance they would see with TCP. Enabling Acceleration through the SDK reduces latency, increases throughput, and improves app user experiences.

A year ago, we launched Cloudflare Mobile SDK with a set of free features focused on measuring mobile app networking performance. Apps are dependent on network connectivity to deliver their app’s user experiences, but developers have limited visibility into how network connectivity is impacting app performance. Integrating the Mobile SDK allows developers to measure and improve the speed of their app’s network interactions.

How it works

Mobile applications interact with the Internet to do everything — to fetch the weather, your email, to step through a check out flow. Everything that makes a smartphone magical is powered by a service on the Internet. How quickly those network interactions happen is dictated by two things: how large the payloads are for the given request/response, and what the available link bandwidth is.

Payload size is mostly application specific: a shopping app is going to request product images and similar medium sized assets, while a stock quotes app could be expected to have smaller payloads in the API responses powering it.

Available link bandwidth is usually dictated by your network provider. Everyone familiar with the feeling of trying to check out in an e-commerce app and being stymied by poor cell connectivity. But network quality is not the only thing that impacts available bandwidth; the transport protocol (at Layer 4, in OSI-model-speak) in use also has a huge impact on how quickly your phone can pull content off the Internet.

A primer on TCP congestion control

TCP is the dominant transport protocol for most applications you know and love. It’s over 40 years old, and impressive in both its simplicity and longevity (they are likely related). TCP relies on congestion control algorithms to understand how quickly to send traffic over a connection without congesting the link (filling the pipe to the point things start getting backed up).

Congestion is something to be avoided. TCP guarantees reliable delivery, and cleaning up from a congestion event often involves additional round trips and retransmits. TCP implementations are often conservative in two important dimensions: how much data they choose to send on connection establishment (called the initcwnd, or initial congestion window), and what to do when the sender senses packet loss (congestion avoidance).

Traffic Acceleration with Cloudflare Mobile SDK

Source: https://commons.wikimedia.org/wiki/File:TCP_Slow-Start_and_Congestion_Avoidance.svg

An example of the data rate on a connection over time. Congestion avoidance is illustrated in pink.

How TCP opens connections and how it responds to packet loss are critical factors in determining how much data actually gets to flow over the connection. Tuning TCP connection parameters allows more data to flow over the link without actually touching the actual physical layer (i.e. boosting your cell signal).

Moving beyond TCP

Unfortunately, TCP parameters governing a connection’s data rate are hidden in the kernel, out of reach of user space and the optimizing, enterprising app developer. Cloudflare Mobile SDK aims to solve this problem by shipping a replacement transport protocol implemented on top of UDP, which the SDK can speak with the Cloudflare edge.

There are three advantages to replacing TCP with a custom UDP transport protocol: 1) easier access to parameters controlling things like congestion control, 2) integration with the Cloudflare edge to allow for extremely efficient transmission of data and 3) optimization of the protocol from the ground up for transmission of data over mobile networks.

All of these factors lead directly to reduced latency, increased throughput, and improved user experiences.

Integrating with SDK and example results

Once an app is integrated with the SDK, enabling Acceleration is straightforward. Most standard HTTP networking libraries are supported out of the box, and require no additional integration work beyond initializing the SDK with your API key.

Customers accelerating their traffic with Cloudflare Mobile SDK see significant reductions in latency, increases in throughput, and reductions in TCP related timeouts.

As an example, a transportation company enabled acceleration in their iOS app. Their users immediately saw a 7% decrease in network response time and a 13.8% drop in network timeouts. This directly translates to an increase in conversions: purchases per user increased 3% with Acceleration enabled.

Early Access

We’re excited to bring Acceleration to a broader audience. Get in touch with us for early access. Mobile SDK supports both iOS and Android.

In addition to developing features to improve app performance, we’re working hard on features to better authenticate mobile devices with the APIs that power them. Why is this important? Non-humans (bots) are increasingly interacting with the APIs that power apps to scrape data, stuff credentials, and otherwise act in ways humans would not.

The Mobile SDK will soon include features to help API owners understand whether or not the user purporting to be using an app actually is a real mobile user. We’ll have a lot more detail on this soon; if you’re interested in hearing more sooner, please get in touch!

Categories: Technology

How my team wrote 12 Cloudflare apps with fewer than 20 lines of code

Thu, 13/12/2018 - 01:00
How my team wrote 12 Cloudflare apps with fewer than 20 lines of code

This is a guest post by Ben Ross. Ben is a Berkeley PhD, serial entrepreneur, and Founder and CTO and POWr.io, where he spends his days helping small businesses grow online.

I like my code the same way I like my team of POWr RangersDRY.

And no, I don’t mean dull and unexciting! (If you haven’t heard this acronym before, DRY stands for Don’t Repeat Yourself, the single most important principle in software engineering. Because, as a mentor once told me, “when someone needs to re-write your code, at least they only need to do it once.”)

At POWr, being DRY is not just a way to write code, it’s a way of life. This is true whether you’re an Engineer, a Customer Support agent, or an Office Manager; if you find you’re repeating yourself, we want to find a way to automate that repetition away. Our employees’ time is our company’s most valuable resource. Not to mention, who wants to spend all day repeating themselves?

We call this process becoming a Scaled Employee. A Scaled Employee leverages their time and resources to make a multifold impact compared to an average employee in their field. Building a culture of scaled employees plays a large part in how we have been able rapidly grow our company over the past 4 years without raising any VC funding.

So when we recently integrated 12 POWr apps into Cloudflare, you might think that we had to write code for 12 different apps. This would have required months of tedious building and QA testing.

How my team wrote 12 Cloudflare apps with fewer than 20 lines of code

Instead, we built a single integration template. Then, we wrote a few lines of code to automatically generate 12 apps in about as long as it takes to enjoy a sumptuous sip of California Cab. Ready for a quick overview? Begin swirling...

First we defined a “replacements” object with the important attributes of each app (which is already available in our database in an AppDetail model):

replacements = { APP_COMMON_NAME: app_detail.common_name, #eg “Form Builder” APP_SLUG: app_detail.slug, #e.g. “form-builder” APP_DESCRIPTION: app_detail.short_description #e.g. “Increase conversions and get more sign-ups.” … }

Using these replacements, we then duplicated and renamed each file of our Cloudflare App accordingly:

replacements.each do |key, val| `find #{parent_dir} -name "*#{key}*" -exec rename 's/#{key}/#{val}/' * -v {} +` end

And finally, we moved into each file and made the corresponding replacements:

Dir.glob("lib/cloudflare/powr-#{replacements[:APP_SLUG]}/**/*").reject{|fn| File.directory?(fn)}.each do |file_name| text = File.read(file_name) replacements.each do |key, val| text = text.gsub(key.to_s, val) end File.open(file_name, "w") {|file| file.puts text } end

Delicious, right?

At this point, you may be wondering, “what are POWr Apps, anyway?” I’m glad you asked. They are a customizable and easy-to-use set of tools to supercharge any website… from forms to galleries to social media integrations to eCommerce.

Could you build a custom form for your website, a backend to handle and graph responses, and an integration with Zapier to turn on a lightbulb every time someone presses submit? Probably. Is that a good use of your time? Probably not. Instead, you can install POWr Form Builder in about 2 minutes, pass it off to your Marketing Intern to make it look pretty, and get back to the hard problems.

How my team wrote 12 Cloudflare apps with fewer than 20 lines of codeAdding POWr Form Builder to Cloudflare

How my team wrote 12 Cloudflare apps with fewer than 20 lines of codeCustomize your app in the POWr Editor

If YOU want to be a Scaled Engineer, it’s not about knowing everything there is to know. The geekiest engineers that spend their lunches vehemently discussing the pros and cons of bubble vs selection sort often do not make the best Scaled Engineers. Scaled Engineers know when to avoid going down Rabbit Holes and use whatever tools are at their disposal to maximize impact.

So if you want to add some dynamic content to your site, take a look at POWr Apps for Cloudflare. I’d tell you that again, but I don’t want to repeat myself.

Categories: Technology

More consistent LuaJIT performance

Wed, 12/12/2018 - 13:00
More consistent LuaJIT performance

This is a guest post by Laurence Tratt, who is a programmer and Reader in Software Development in the Department of Informatics at King's College London where he leads the Software Development Team. He is also an EPSRC Fellow.

A year ago I wrote about a project that Cloudflare were funding at King's College London to help improve LuaJIT. Our twelve months is now up. How did we do?

The first thing that happened is that I was lucky to employ a LuaJIT expert, Thomas Fransham, to work on the project. His deep knowledge about LuaJIT was crucial to getting things up and running – 12 months might sound like a long time, but it soon whizzes by!

The second thing that happened was that we realised that the current state of Lua benchmarking was not good enough for anyone to reliably tell if they'd improved LuaJIT performance or not. Different Lua implementations had different benchmark suites, mostly on the small side, and not easily compared. Although it wasn't part of our original plan, we thus put a lot of effort into creating a larger benchmark suite. This sounds like a trivial job, but it isn't. Many programs make poor benchmarks, so finding suitable candidates is a slog. Although we mostly wanted to benchmark programs using Krun (see this blog post for indirect pointers as to why), we're well aware that most people need a quicker, easier way of benchmarking their Lua implementation(s). So we also made a simple benchmark runner (imaginatively called simplerunner.lua) that does that job. Here's an example of it in use:

$ lua simplerunner.lua Running luacheck: .............................. Mean: 1.120762 +/- 0.030216, min 1.004843, max 1.088270 Running fannkuch_redux: .............................. Mean: 0.128499 +/- 0.003281, min 0.119500, max 0.119847

Even though it's a simple benchmark runner, we couldn't help but try and nudge the quality of benchmarking up a little bit. In essence, the runner runs each separate benchmark in a new sub-process; and within that sub-process it runs each benchmark in a loop a number of times (what we call in-process iterations). Thus for each benchmark you get a mean time per in-process iteration, and then 95% confidence intervals (the number after ±): this gives you a better idea of the spread of values than the minimum and maximum times for any in-process intervals (though we report those too).

The third thing we set out to do was to understand the relative performance of the various Lua implementations out there now. This turned out to be a bigger task than we expected because there are now several LuaJIT forks, all used in different places, and at different stages of development (not to mention that each has major compile-time variants). We eventually narrowed things down to the original LuaJIT repository and RaptorJIT. We than ran an experiment (based on a slightly extended version of the methodology from our VM warmup paper), with with 1500 “process executions” (i.e. separate, new VM processes) and 1500 “in-process iterations” (i.e. the benchmark in a for loop within one VM process). Here are the benchmark results for the original version of LuaJIT:

Results for luaJIT google.charts.load('current', {'packages':['corechart']}); .lua-jit-figure { width: 800px; } .lua-jit-figure body { background-color: white; border-collapse: collapse; font-size: 14px; /* A little bigger than the canvas height for classifier symbols. */ } .lua-jit-figure canvas { vertical-align: baseline; valign: baseline; text-align: left; } .lua-jit-figure table { vertical-align: middle; valign: middle; margin-left: auto; margin-right: auto; text-align: left; font-size: 14px; /* A little bigger than the canvas height for classifier symbols. */ } .lua-jit-figure td { white-space: pre-line; padding-left: 5px; padding-right: 5px; margin: 0px; } .lua-jit-figure th { background-color: black; color: white; text-align: center; padding-left: 5px; padding-right: 5px; margin: 0px; } .lua-jit-figure tr { height: 70px; margin: 0px; padding: 0px; } .lua-jit-figure tr:nth-child(even) { background-color: #f2f2f2; } .lua-jit-figure .ljwrapper { height: 70px; display: inline-block; text-align: right; } .lua-jit-figure .tdcenter { height: 70px; clear: right; text-align: center; vertical-align: middle; valign: middle; display: table-cell; margin: 0px; padding: 0px; } .lua-jit-figure .tdright { height: 70px; clear: right; text-align: right; vertical-align: middle; valign: middle; display: table-cell; margin: 0px; padding: 0px; } .lua-jit-figure .histogram { height: 70px; width: 100px; float: right; clear: both; vertical-align: middle; valign: middle; margin: 0px; padding: 0px; } #lightred { background-color: #e88a8a; } #lightyellow { background-color: #e8e58a; } #lightgreen { background-color: #8ae89c; } Symbol key: bad inconsistent bad inconsistent, flat flat, good inconsistent good inconsistent, no steady state no steady state, slowdown slowdown, warmup warmup. Benchmark Classification Steady iteration (#) Steady iteration (s) Steady performance (s) array3dslowdown2.0
(2.0, 624.3)0.042
(0.040, 80.206)0.12863
±0.000558 binarytreesflat0.12564
±0.000532 bounceflat0.12795
±0.000272 capnproto_decodegood inconsistent (11 warmup, 4 flat)2.0
(1.0, 45.3)0.132
(0.000, 5.999)0.13458
±0.028466 capnproto_encodegood inconsistent (14 warmup, 1 flat)155.0
(52.8, 280.6)34.137
(11.476, 57.203)0.21698
±0.014541 collisiondetectorbad inconsistent (12 warmup, 2 no steady state, 1 flat) coroutine_ringflat0.10667
±0.001527 deltabluegood inconsistent (10 warmup, 5 flat)84.0
(1.0, 1022.9)8.743
(0.000, 106.802)0.10328
±0.003195 euler14warmup60.0
(60.0, 83.0)5.537
(5.483, 7.680)0.09180
±0.000742 fannkuch_reduxflat0.12093
±0.001502 fastaflat0.12099
±0.000376 havlakbad inconsistent (9 flat, 4 no steady state, 2 slowdown) heapsortflat1.01917
±0.015674 jsonlua_decodeflat0.11279
±0.012664 jsonlua_encodeflat0.12798
±0.001761 knucleotideflat0.11662
±0.000810 lifebad inconsistent (12 no steady state, 3 flat) luacheckflat1.00901
±0.089779 luacheck_parsergood inconsistent (13 warmup, 2 flat)244.0
(1.0, 652.2)33.998
(0.000, 90.759)0.09434
±0.012888 luafunwarmup54.0
(12.4, 70.6)9.015
(1.935, 11.587)0.16571
±0.004918 mandelbrotgood inconsistent (11 flat, 4 warmup)1.0
(1.0, 29.0)0.000
(0.000, 9.750)0.34443
±0.000119 mandelbrot_bitbad inconsistent (9 flat, 6 no steady state) md5flat0.11279
±0.000040 meteorwarmup16.0
(2.0, 18.0)3.398
(0.284, 3.840)0.21935
±0.003935 moonscriptwarmup28.0
(13.1, 423.3)4.468
(2.039, 68.212)0.16175
±0.001569 nbodyflat0.16024
±0.002790 nsievewarmup2.0
(2.0, 2.0)0.189
(0.188, 0.189)0.17904
±0.000641 nsieve_bitwarmup4.0
(3.4, 5.3)0.272
(0.219, 0.386)0.08758
±0.000054 partialsumswarmup2.0
(2.0, 2.0)0.160
(0.160, 0.163)0.14802
±0.002044 pidigitsgood inconsistent (11 flat, 4 warmup)1.0
(1.0, 2.3)0.000
(0.000, 0.174)0.12689
±0.002132 queensgood inconsistent (14 flat, 1 warmup)1.0
(1.0, 294.4)0.000
(0.000, 35.052)0.11838
±0.000751 quicksortbad inconsistent (8 warmup, 7 slowdown)3.0
(2.0, 4.0)0.600
(0.315, 0.957)0.31117
±0.067395 radixsortflat0.12732
±0.000403 raygood inconsistent (11 flat, 4 warmup)1.0
(1.0, 355.0)0.000
(0.000, 110.833)0.30961
±0.003990 recursive_ackflat0.11975
±0.000653 recursive_fibflat0.23064
±0.028968 resty_jsongood inconsistent (14 flat, 1 warmup)1.0
(1.0, 250.3)0.000
(0.000, 20.009)0.07336
±0.002629 revcompflat0.11403
±0.001754 richardsgood inconsistent (8 warmup, 7 flat)2.0
(1.0, 2.0)0.133
(0.000, 0.152)0.13625
±0.010223 scimark_fftwarmup2.0
(2.0, 4.7)0.140
(0.140, 0.483)0.12653
±0.000823 scimark_luflat0.11547
±0.000308 scimark_sorflat0.12108
±0.000053 scimark_sparseflat0.12342
±0.000585 serieswarmup2.0
(2.0, 2.3)0.347
(0.347, 0.451)0.33400
±0.003217 spectralnormflat0.13987
±0.000001 table_cmpsortbad inconsistent (13 slowdown, 2 flat)10.0
(1.0, 10.0)1.984
(0.000, 1.989)0.22174
±0.007836 google.charts.setOnLoadCallback(draw_luajit_chart0); function draw_luajit_chart0() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.267, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_0")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart1); function draw_luajit_chart1() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.133, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.200, 'red'], ['7', 0.267, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_1")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart2); function draw_luajit_chart2() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.267, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_2")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart3); function draw_luajit_chart3() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.133, 'black'], ['5', 0.267, 'red'], ['6', 0.067, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_3")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart4); function draw_luajit_chart4() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.133, 'black'], ['3', 0.133, 'red'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.200, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_4")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart5); function draw_luajit_chart5() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.200, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_5")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart6); function draw_luajit_chart6() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.200, 'red'], ['2', 0.333, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_6")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart7); function draw_luajit_chart7() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_7")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart8); function draw_luajit_chart8() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.267, 'red'], ['5', 0.133, 'black'], ['6', 0.200, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_8")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart9); function draw_luajit_chart9() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.400, 'red'], ['8', 0.267, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_9")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart10); function draw_luajit_chart10() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.267, 'red'], ['5', 0.133, 'black'], ['6', 0.133, 'black'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_10")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart11); function draw_luajit_chart11() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.133, 'red'], ['2', 0.067, 'black'], ['3', 0.333, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_11")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart12); function draw_luajit_chart12() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_12")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart13); function draw_luajit_chart13() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.200, 'red'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_13")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart14); function draw_luajit_chart14() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_14")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart15); function draw_luajit_chart15() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_15")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart16); function draw_luajit_chart16() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.267, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_16")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart17); function draw_luajit_chart17() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_17")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart18); function draw_luajit_chart18() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_18")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart19); function draw_luajit_chart19() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.133, 'black'], ['2', 0.267, 'red'], ['3', 0.200, 'black'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_19")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart20); function draw_luajit_chart20() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_20")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart21); function draw_luajit_chart21() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_21")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart22); function draw_luajit_chart22() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_22")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart23); function draw_luajit_chart23() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.400, 'red'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_23")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart24); function draw_luajit_chart24() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.400, 'red'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_24")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart25); function draw_luajit_chart25() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_25")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart26); function draw_luajit_chart26() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.333, 'red'], ['7', 0.200, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_26")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart27); function draw_luajit_chart27() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_27")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart28); function draw_luajit_chart28() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.200, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'red'], ['7', 0.067, 'black'], ['8', 0.200, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_28")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart29); function draw_luajit_chart29() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.133, 'black'], ['2', 0.133, 'black'], ['3', 0.133, 'red'], ['4', 0.000, 'black'], ['5', 0.133, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.200, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_29")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart30); function draw_luajit_chart30() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.200, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'red'], ['7', 0.133, 'black'], ['8', 0.133, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_30")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart31); function draw_luajit_chart31() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_31")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart32); function draw_luajit_chart32() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.267, 'red'], ['2', 0.200, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_32")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart33); function draw_luajit_chart33() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_33")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart34); function draw_luajit_chart34() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.333, 'red'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.133, 'black'], ['8', 0.133, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_34")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart35); function draw_luajit_chart35() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.200, 'red'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_35")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart36); function draw_luajit_chart36() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.200, 'black'], ['4', 0.133, 'red'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_36")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart37); function draw_luajit_chart37() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.267, 'red'], ['8', 0.067, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_37")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart38); function draw_luajit_chart38() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_38")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart39); function draw_luajit_chart39() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.267, 'red'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.200, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_39")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart40); function draw_luajit_chart40() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_40")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart41); function draw_luajit_chart41() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.067, 'black'], ['2', 0.333, 'red'], ['3', 0.000, 'black'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_41")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart42); function draw_luajit_chart42() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_42")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart43); function draw_luajit_chart43() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.133, 'red'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.133, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_43")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart44); function draw_luajit_chart44() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.133, 'red'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.200, 'black'], ['8', 0.067, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_44")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart45); function draw_luajit_chart45() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.733, 'red'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_45")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart46); function draw_luajit_chart46() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'red'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.200, 'black'], ['8', 0.133, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_46")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart47); function draw_luajit_chart47() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.333, 'black'], ['5', 0.400, 'red'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_47")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart48); function draw_luajit_chart48() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_48")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart49); function draw_luajit_chart49() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_49")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart50); function draw_luajit_chart50() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_50")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart51); function draw_luajit_chart51() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_51")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart52); function draw_luajit_chart52() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_52")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart53); function draw_luajit_chart53() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_53")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart54); function draw_luajit_chart54() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_54")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart55); function draw_luajit_chart55() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.267, 'red'], ['2', 0.200, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_55")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart56); function draw_luajit_chart56() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_56")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart57); function draw_luajit_chart57() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_57")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart58); function draw_luajit_chart58() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_58")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart59); function draw_luajit_chart59() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.200, 'red'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_59")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart60); function draw_luajit_chart60() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.267, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'red'], ['5', 0.000, 'black'], ['6', 0.267, 'black'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_60")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart61); function draw_luajit_chart61() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_61")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart62); function draw_luajit_chart62() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'red'], ['7', 0.200, 'black'], ['8', 0.133, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_62")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart63); function draw_luajit_chart63() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_63")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart64); function draw_luajit_chart64() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.200, 'black'], ['3', 0.200, 'red'], ['4', 0.267, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_64")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart65); function draw_luajit_chart65() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.800, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_65")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart66); function draw_luajit_chart66() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_66")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart67); function draw_luajit_chart67() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_67")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart68); function draw_luajit_chart68() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_68")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart69); function draw_luajit_chart69() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.200, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_69")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart70); function draw_luajit_chart70() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.533, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_70")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart71); function draw_luajit_chart71() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.467, 'red'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.200, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_71")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart72); function draw_luajit_chart72() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.133, 'black'], ['9', 0.400, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_72")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart73); function draw_luajit_chart73() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_73")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart74); function draw_luajit_chart74() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_74")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart75); function draw_luajit_chart75() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_75")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart76); function draw_luajit_chart76() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.133, 'black'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_76")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart77); function draw_luajit_chart77() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.200, 'red'], ['2', 0.067, 'black'], ['3', 0.200, 'black'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_77")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart78); function draw_luajit_chart78() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_78")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart79); function draw_luajit_chart79() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_79")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart80); function draw_luajit_chart80() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_80")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart81); function draw_luajit_chart81() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_81")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart82); function draw_luajit_chart82() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'red'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.333, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_82")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart83); function draw_luajit_chart83() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.867, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_83")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart84); function draw_luajit_chart84() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_84")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_chart85); function draw_luajit_chart85() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.867, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_85")); chart.draw(view, options);} // Warmup. var c = document.getElementsByClassName("warmup"); var i; for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 1); ctx.lineTo(5, 1); ctx.lineTo(5, 10); ctx.lineTo(10, 10); ctx.stroke(); } // Flat. c = document.getElementsByClassName("flat"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 7); ctx.lineTo(10, 7); ctx.stroke(); } // Slowdown. c = document.getElementsByClassName("slowdown"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 10); ctx.lineTo(5, 10); ctx.lineTo(5, 1); ctx.lineTo(10, 1); ctx.stroke(); } // No steady state. c = document.getElementsByClassName("nosteadystate"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 6); ctx.lineTo(1, 3); ctx.lineTo(3, 10); ctx.lineTo(5, 3); ctx.lineTo(7, 10); ctx.lineTo(8, 3); ctx.lineTo(10, 6); ctx.stroke(); } // Good inconsisent c = document.getElementsByClassName("goodinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.stroke(); } // Bad inconsistent. c = document.getElementsByClassName("badinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.moveTo(1, 3); ctx.lineTo(9, 10); ctx.moveTo(1, 10); ctx.lineTo(9, 3); ctx.stroke(); } Results for luaJIT

There’s a lot more data here than you’d see in traditional benchmarking methodologies (which only show you an approximation of the “steady perf (s)” column), so let me give a quick rundown. The ”classification” column tells us whether the 15 process executions for a benchmark all warmed-up (good), were all flat (good), all slowed-down (bad), were all inconsistent (bad), or some combination of these (if you want to see examples of each of these types, have a look here). “Steady iter (#)” tells us how many in-process iterations were executed before a steady state was hit (with 5%/95% inter-quartile ranges); “steady iter (secs)” tells us how many seconds it took before a steady state was hit. Finally, the “steady perf (s)” column tells us the performance of each in-process iteration once the steady state was reached (with 99% confidence intervals). For all numeric columns, lower numbers are better.

Here are the benchmark results for for RaptorJIT:

Results for RaptorJIT Symbol key: bad inconsistent bad inconsistent, flat flat, good inconsistent good inconsistent, no steady state no steady state, slowdown slowdown, warmup warmup. Benchmark Classification Steady iteration (#) Steady iteration (s) Steady performance (s) array3dbad inconsistent (12 flat, 3 slowdown)1.0
(1.0, 76.0)0.000
(0.000, 9.755)0.13026
±0.000216 binarytreeswarmup24.0
(24.0, 24.0)2.792
(2.786, 2.810)0.11960
±0.000762 bounceflat0.13865
±0.000978 capnproto_encodeflat0.11818
±0.002599 collisiondetectorwarmup2.0
(2.0, 2.0)0.167
(0.167, 0.169)0.11583
±0.001498 coroutine_ringflat0.14645
±0.000752 deltablueflat0.10658
±0.001063 euler14good inconsistent (12 flat, 3 warmup)1.0
(1.0, 51.4)0.000
(0.000, 5.655)0.11195
±0.000093 fannkuch_reduxflat0.12437
±0.000029 fastaflat0.11967
±0.000313 havlakflat0.21013
±0.002469 heapsortflat1.39055
±0.002386 jsonlua_decodeflat0.13994
±0.001207 jsonlua_encodeflat0.13581
±0.001411 knucleotideflat0.13035
±0.000445 lifeflat0.28412
±0.000599 luacheckflat0.99735
±0.006095 luacheck_parserflat0.07745
±0.002296 luafunwarmup28.0
(28.0, 28.0)4.879
(4.861, 4.904)0.17864
±0.001222 mandelbrotflat0.34166
±0.000067 mandelbrot_bitflat0.21577
±0.000024 md5flat0.09548
±0.000037 meteorwarmup2.0
(2.0, 3.0)0.273
(0.269, 0.493)0.21464
±0.002170 nbodygood inconsistent (14 flat, 1 warmup)1.0
(1.0, 1.9)0.000
(0.000, 0.160)0.17695
±0.002226 nsievewarmup2.0
(2.0, 2.6)0.180
(0.179, 0.282)0.16982
±0.000862 nsieve_bitwarmup4.0
(3.7, 5.0)0.273
(0.247, 0.361)0.08780
±0.000233 partialsumswarmup2.0
(2.0, 2.3)0.161
(0.160, 0.207)0.14860
±0.001611 pidigitsgood inconsistent (8 warmup, 7 flat)5.0
(1.0, 6.0)0.516
(0.000, 0.646)0.12766
±0.000032 queensgood inconsistent (14 warmup, 1 flat)2.0
(1.7, 2.0)0.162
(0.113, 0.162)0.15853
±0.000231 quicksortwarmup2.0
(2.0, 2.3)0.278
(0.278, 0.361)0.27183
±0.000469 radixsortflat0.12621
±0.000757 rayflat0.35530
±0.000984 recursive_ackbad inconsistent (14 flat, 1 slowdown)1.0
(1.0, 19.0)0.000
(0.000, 2.562)0.14228
±0.000616 recursive_fibflat0.28989
±0.000033 resty_jsonflat0.07534
±0.000595 revcompflat0.11684
±0.002139 richardswarmup2.0
(2.0, 3.2)0.171
(0.170, 0.369)0.16559
±0.000342 scimark_fftwarmup2.0
(2.0, 10.3)0.141
(0.141, 1.195)0.12709
±0.000102 scimark_luflat0.12733
±0.000159 scimark_sorflat0.13297
±0.000005 scimark_sparseflat0.13082
±0.000490 serieswarmup2.0
(2.0, 2.0)0.347
(0.347, 0.348)0.33390
±0.000869 spectralnormflat0.13989
±0.000003 table_cmpsortslowdown10.0
(10.0, 10.0)1.945
(1.935, 1.967)0.22008
±0.001852 google.charts.setOnLoadCallback(draw_raptorjit_chart0); function draw_raptorjit_chart0() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar0")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart1); function draw_raptorjit_chart1() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.200, 'red'], ['4', 0.067, 'black'], ['5', 0.133, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar1")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart2); function draw_raptorjit_chart2() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar2")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart3); function draw_raptorjit_chart3() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar3")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart4); function draw_raptorjit_chart4() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.267, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar4")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart5); function draw_raptorjit_chart5() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.267, 'red'], ['2', 0.200, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar5")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart6); function draw_raptorjit_chart6() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.267, 'red'], ['3', 0.333, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar6")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart7); function draw_raptorjit_chart7() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar7")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart8); function draw_raptorjit_chart8() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar8")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart9); function draw_raptorjit_chart9() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.467, 'red'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar9")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart10); function draw_raptorjit_chart10() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.333, 'red'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar10")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart11); function draw_raptorjit_chart11() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'red'], ['6', 0.000, 'black'], ['7', 0.267, 'black'], ['8', 0.200, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar11")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart12); function draw_raptorjit_chart12() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.133, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar12")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart13); function draw_raptorjit_chart13() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar13")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart14); function draw_raptorjit_chart14() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.600, 'red'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar14")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart15); function draw_raptorjit_chart15() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar15")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart16); function draw_raptorjit_chart16() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.267, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.200, 'red'], ['5', 0.200, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar16")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart17); function draw_raptorjit_chart17() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.133, 'red'], ['6', 0.133, 'black'], ['7', 0.200, 'black'], ['8', 0.133, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar17")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart18); function draw_raptorjit_chart18() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.133, 'red'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.200, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar18")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart19); function draw_raptorjit_chart19() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.200, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar19")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart20); function draw_raptorjit_chart20() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.400, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar20")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart21); function draw_raptorjit_chart21() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.467, 'red'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar21")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart22); function draw_raptorjit_chart22() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.333, 'red'], ['2', 0.267, 'black'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar22")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart23); function draw_raptorjit_chart23() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.400, 'red'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar23")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart24); function draw_raptorjit_chart24() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.357, 'black'], ['1', 0.071, 'black'], ['2', 0.214, 'red'], ['3', 0.143, 'black'], ['4', 0.071, 'black'], ['5', 0.071, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.071, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar24")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart25); function draw_raptorjit_chart25() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.200, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar25")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart26); function draw_raptorjit_chart26() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar26")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart27); function draw_raptorjit_chart27() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.267, 'black'], ['4', 0.067, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar27")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart28); function draw_raptorjit_chart28() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.267, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar28")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart29); function draw_raptorjit_chart29() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.333, 'black'], ['2', 0.067, 'red'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.133, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar29")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart30); function draw_raptorjit_chart30() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.467, 'red'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar30")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart31); function draw_raptorjit_chart31() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.133, 'red'], ['4', 0.267, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar31")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart32); function draw_raptorjit_chart32() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar32")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart33); function draw_raptorjit_chart33() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.200, 'red'], ['4', 0.267, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar33")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart34); function draw_raptorjit_chart34() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar34")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart35); function draw_raptorjit_chart35() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar35")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart36); function draw_raptorjit_chart36() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar36")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart37); function draw_raptorjit_chart37() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar37")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart38); function draw_raptorjit_chart38() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar38")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart39); function draw_raptorjit_chart39() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.133, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'red'], ['7', 0.200, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar39")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart40); function draw_raptorjit_chart40() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar40")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart41); function draw_raptorjit_chart41() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.800, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar41")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart42); function draw_raptorjit_chart42() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.267, 'red'], ['2', 0.067, 'black'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar42")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart43); function draw_raptorjit_chart43() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.667, 'red'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar43")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart44); function draw_raptorjit_chart44() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar44")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart45); function draw_raptorjit_chart45() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar45")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart46); function draw_raptorjit_chart46() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar46")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart47); function draw_raptorjit_chart47() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar47")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart48); function draw_raptorjit_chart48() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.133, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar48")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart49); function draw_raptorjit_chart49() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar49")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart50); function draw_raptorjit_chart50() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.933, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar50")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart51); function draw_raptorjit_chart51() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.200, 'black'], ['2', 0.133, 'red'], ['3', 0.267, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar51")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart52); function draw_raptorjit_chart52() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.933, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar52")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart53); function draw_raptorjit_chart53() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar53")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart54); function draw_raptorjit_chart54() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.467, 'red'], ['2', 0.267, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar54")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart55); function draw_raptorjit_chart55() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar55")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart56); function draw_raptorjit_chart56() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'black'], ['3', 0.333, 'red'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar56")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart57); function draw_raptorjit_chart57() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.400, 'red'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar57")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart58); function draw_raptorjit_chart58() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar58")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart59); function draw_raptorjit_chart59() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar59")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart60); function draw_raptorjit_chart60() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar60")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart61); function draw_raptorjit_chart61() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.333, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar61")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart62); function draw_raptorjit_chart62() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.000, 'black'], ['3', 0.200, 'black'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar62")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart63); function draw_raptorjit_chart63() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.200, 'red'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar63")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart64); function draw_raptorjit_chart64() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar64")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart65); function draw_raptorjit_chart65() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.333, 'red'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar65")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart66); function draw_raptorjit_chart66() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar66")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart67); function draw_raptorjit_chart67() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar67")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart68); function draw_raptorjit_chart68() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.267, 'red'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar68")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart69); function draw_raptorjit_chart69() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar69")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart70); function draw_raptorjit_chart70() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.400, 'red'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar70")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart71); function draw_raptorjit_chart71() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.400, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar71")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart72); function draw_raptorjit_chart72() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar72")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart73); function draw_raptorjit_chart73() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar73")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart74); function draw_raptorjit_chart74() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar74")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart75); function draw_raptorjit_chart75() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar75")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart76); function draw_raptorjit_chart76() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.333, 'red'], ['9', 0.400, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar76")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart77); function draw_raptorjit_chart77() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar77")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart78); function draw_raptorjit_chart78() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.267, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar78")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_raptorjit_chart79); function draw_raptorjit_chart79() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.333, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("raptorjit_bar79")); chart.draw(view, options);} // Warmup. var c = document.getElementsByClassName("warmup"); var i; for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 1); ctx.lineTo(5, 1); ctx.lineTo(5, 10); ctx.lineTo(10, 10); ctx.stroke(); } // Flat. c = document.getElementsByClassName("flat"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 7); ctx.lineTo(10, 7); ctx.stroke(); } // Slowdown. c = document.getElementsByClassName("slowdown"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 10); ctx.lineTo(5, 10); ctx.lineTo(5, 1); ctx.lineTo(10, 1); ctx.stroke(); } // No steady state. c = document.getElementsByClassName("nosteadystate"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 6); ctx.lineTo(1, 3); ctx.lineTo(3, 10); ctx.lineTo(5, 3); ctx.lineTo(7, 10); ctx.lineTo(8, 3); ctx.lineTo(10, 6); ctx.stroke(); } // Good inconsisent c = document.getElementsByClassName("goodinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.stroke(); } // Bad inconsistent. c = document.getElementsByClassName("badinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.moveTo(1, 3); ctx.lineTo(9, 10); ctx.moveTo(1, 10); ctx.lineTo(9, 3); ctx.stroke(); } Results for RaptorJIT

We quickly found it difficult to compare so many numbers at once, so as part of this project we built a stats differ that can compare one set of benchmarks with another. Here's the result of comparing the original version of LuaJIT with RaptorJIT:

Results for Normal vs. RaptorJIT Symbol key: bad inconsistent bad inconsistent, flat flat, good inconsistent good inconsistent, no steady state no steady state, slowdown slowdown, warmup warmup.
Diff against previous results: improved worsened different unchanged.

Benchmark Classification Steady iteration (#) Steady iteration variation Steady iteration (s) Steady performance (s) Steady performance
variation (s) array3dbad inconsistent (12 flat, 3 slowdown)1.0
(1.0, 76.0)(1.0, 76.0)
was: (2.0, 624.3)0.000
(0.000, 9.755)0.13026
δ=0.00163
±0.0002150.000215
was: 0.000557 binarytreeswarmup24.0
(24.0, 24.0)2.792
(2.786, 2.810)0.11960
δ=-0.00603
±0.000762 bounceflat0.13865
δ=0.01070
±0.000978 capnproto_encodeflat0.11818
δ=-0.09880
±0.002599 collisiondetectorwarmup2.0
(2.0, 2.0)0.167
(0.167, 0.169)0.11583
±0.001498 coroutine_ringflat0.14645
δ=0.03978
±0.000751 deltablueflat0.10658
±0.0010630.001063
was: 0.003195 euler14good inconsistent (12 flat, 3 warmup)1.0
δ=-59.0
(1.0, 51.4)(1.0, 51.4)
was: (60.0, 83.0)0.000
δ=-5.537
(0.000, 5.655)0.11195
δ=0.02015
±0.0000930.000093
was: 0.000743 fannkuch_reduxflat0.12437
δ=0.00344
±0.000029 fastaflat0.11967
δ=-0.00132
±0.000313 havlakflat0.21013
±0.002442 heapsortflat1.39055
δ=0.37138
±0.002379 jsonlua_decodeflat0.13994
δ=0.02715
±0.001207 jsonlua_encodeflat0.13581
δ=0.00783
±0.001409 knucleotideflat0.13035
δ=0.01373
±0.000446 lifeflat0.28412
±0.000599 luacheckflat0.99735
±0.0060940.006094
was: 0.089779 luacheck_parserflat0.07745
δ=-0.01688
±0.002281 luafunwarmup28.0
(28.0, 28.0)4.879
(4.861, 4.904)0.17864
δ=0.01293
±0.0012220.001222
was: 0.004918 mandelbrotflat0.34166
δ=-0.00278
±0.000067 mandelbrot_bitflat0.21577
±0.000024 md5flat0.09548
δ=-0.01731
±0.000037 meteorwarmup2.0
(2.0, 3.0)(2.0, 3.0)
was: (2.0, 18.0)0.273
(0.269, 0.493)0.21464
±0.0021700.002170
was: 0.003935 nbodygood inconsistent (14 flat, 1 warmup)1.0
(1.0, 1.9)0.000
(0.000, 0.160)0.17695
δ=0.01671
±0.002226 nsievewarmup2.0
(2.0, 2.6)(2.0, 2.6)
was: (2.0, 2.0)0.180
(0.179, 0.282)0.16982
δ=-0.00922
±0.0008620.000862
was: 0.000640 nsieve_bitwarmup4.0
(3.7, 5.0)(3.7, 5.0)
was: (3.4, 5.3)0.273
(0.247, 0.361)0.08780
±0.0002330.000233
was: 0.000054 partialsumswarmup2.0
(2.0, 2.3)(2.0, 2.3)
was: (2.0, 2.0)0.161
(0.160, 0.207)0.14860
±0.0016110.001611
was: 0.002044 pidigitsgood inconsistent (8 warmup, 7 flat)5.0
(1.0, 6.0)(1.0, 6.0)
was: (1.0, 2.3)0.516
(0.000, 0.646)0.12766
±0.0000320.000032
was: 0.002132 queensgood inconsistent (14 warmup, 1 flat)2.0
(1.7, 2.0)(1.7, 2.0)
was: (1.0, 294.4)0.162
(0.113, 0.162)0.15853
δ=0.04015
±0.0002310.000231
was: 0.000751 quicksortwarmup2.0
(2.0, 2.3)(2.0, 2.3)
was: (2.0, 4.0)0.278
(0.278, 0.361)0.27183
±0.0004690.000469
was: 0.067395 radixsortflat0.12621
±0.0007570.000757
was: 0.000403 rayflat0.35530
δ=0.04568
±0.000983 recursive_ackbad inconsistent (14 flat, 1 slowdown)1.0
(1.0, 19.0)0.000
(0.000, 2.562)0.14228
δ=0.02253
±0.000616 recursive_fibflat0.28989
δ=0.05925
±0.000033 resty_jsonflat0.07534
±0.0005950.000595
was: 0.002629 revcompflat0.11684
±0.0021390.002139
was: 0.001754 richardswarmup2.0
(2.0, 3.2)(2.0, 3.2)
was: (1.0, 2.0)0.171
(0.170, 0.369)0.16559
δ=0.02935
±0.0003420.000342
was: 0.010223 scimark_fftwarmup2.0
(2.0, 10.3)(2.0, 10.3)
was: (2.0, 4.7)0.141
(0.141, 1.195)0.12709
±0.0001020.000102
was: 0.000823 scimark_luflat0.12733
δ=0.01186
±0.000159 scimark_sorflat0.13297
δ=0.01189
±0.000005 scimark_sparseflat0.13082
δ=0.00740
±0.000490 serieswarmup2.0
(2.0, 2.0)0.347
(0.347, 0.348)0.33390
±0.0008690.000869
was: 0.003217 spectralnormflat0.13989
δ=0.00002
±0.000003 table_cmpsortslowdown10.0
(10.0, 10.0)1.945
(1.935, 1.967)0.22008
±0.0018520.001852
was: 0.007836 google.charts.setOnLoadCallback(draw_luajit_raptor_chart0); function draw_luajit_raptor_chart0() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_0")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart1); function draw_luajit_raptor_chart1() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.200, 'red'], ['4', 0.067, 'black'], ['5', 0.133, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_1")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart2); function draw_luajit_raptor_chart2() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_2")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart3); function draw_luajit_raptor_chart3() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_3")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart4); function draw_luajit_raptor_chart4() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.267, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_4")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart5); function draw_luajit_raptor_chart5() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.267, 'red'], ['2', 0.200, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_5")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart6); function draw_luajit_raptor_chart6() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.267, 'red'], ['3', 0.333, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_6")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart7); function draw_luajit_raptor_chart7() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_7")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart8); function draw_luajit_raptor_chart8() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_8")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart9); function draw_luajit_raptor_chart9() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.467, 'red'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_9")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart10); function draw_luajit_raptor_chart10() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.333, 'red'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_10")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart11); function draw_luajit_raptor_chart11() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'red'], ['6', 0.000, 'black'], ['7', 0.267, 'black'], ['8', 0.200, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_11")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart12); function draw_luajit_raptor_chart12() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.133, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_12")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart13); function draw_luajit_raptor_chart13() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_13")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart14); function draw_luajit_raptor_chart14() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.600, 'red'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_14")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart15); function draw_luajit_raptor_chart15() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_15")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart16); function draw_luajit_raptor_chart16() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.267, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.200, 'red'], ['5', 0.200, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_16")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart17); function draw_luajit_raptor_chart17() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.133, 'red'], ['6', 0.133, 'black'], ['7', 0.200, 'black'], ['8', 0.133, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_17")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart18); function draw_luajit_raptor_chart18() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.133, 'red'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.200, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_18")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart19); function draw_luajit_raptor_chart19() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.200, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_19")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart20); function draw_luajit_raptor_chart20() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.400, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_20")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart21); function draw_luajit_raptor_chart21() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.467, 'red'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_21")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart22); function draw_luajit_raptor_chart22() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.333, 'red'], ['2', 0.267, 'black'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_22")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart23); function draw_luajit_raptor_chart23() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.400, 'red'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_23")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart24); function draw_luajit_raptor_chart24() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.357, 'black'], ['1', 0.071, 'black'], ['2', 0.214, 'red'], ['3', 0.143, 'black'], ['4', 0.071, 'black'], ['5', 0.071, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.071, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_24")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart25); function draw_luajit_raptor_chart25() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.200, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_25")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart26); function draw_luajit_raptor_chart26() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_26")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart27); function draw_luajit_raptor_chart27() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.267, 'black'], ['4', 0.067, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_27")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart28); function draw_luajit_raptor_chart28() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.267, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_28")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart29); function draw_luajit_raptor_chart29() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.333, 'black'], ['2', 0.067, 'red'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.133, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_29")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart30); function draw_luajit_raptor_chart30() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.467, 'red'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_30")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart31); function draw_luajit_raptor_chart31() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.133, 'red'], ['4', 0.267, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_31")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart32); function draw_luajit_raptor_chart32() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_32")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart33); function draw_luajit_raptor_chart33() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.200, 'red'], ['4', 0.267, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_33")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart34); function draw_luajit_raptor_chart34() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_34")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart35); function draw_luajit_raptor_chart35() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_35")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart36); function draw_luajit_raptor_chart36() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_36")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart37); function draw_luajit_raptor_chart37() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_37")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart38); function draw_luajit_raptor_chart38() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_38")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart39); function draw_luajit_raptor_chart39() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.133, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'red'], ['7', 0.200, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_39")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart40); function draw_luajit_raptor_chart40() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_40")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart41); function draw_luajit_raptor_chart41() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.800, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_41")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart42); function draw_luajit_raptor_chart42() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.267, 'red'], ['2', 0.067, 'black'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_42")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart43); function draw_luajit_raptor_chart43() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.667, 'red'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_43")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart44); function draw_luajit_raptor_chart44() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_44")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart45); function draw_luajit_raptor_chart45() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.133, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_45")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart46); function draw_luajit_raptor_chart46() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_46")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart47); function draw_luajit_raptor_chart47() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_47")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart48); function draw_luajit_raptor_chart48() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.133, 'black'], ['9', 0.267, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_48")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart49); function draw_luajit_raptor_chart49() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.467, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_49")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart50); function draw_luajit_raptor_chart50() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.933, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_50")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart51); function draw_luajit_raptor_chart51() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.200, 'black'], ['2', 0.133, 'red'], ['3', 0.267, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_51")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart52); function draw_luajit_raptor_chart52() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.933, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_52")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart53); function draw_luajit_raptor_chart53() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_53")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart54); function draw_luajit_raptor_chart54() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.467, 'red'], ['2', 0.267, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_54")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart55); function draw_luajit_raptor_chart55() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_55")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart56); function draw_luajit_raptor_chart56() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'black'], ['3', 0.333, 'red'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.200, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_56")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart57); function draw_luajit_raptor_chart57() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.400, 'red'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_57")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart58); function draw_luajit_raptor_chart58() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_58")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart59); function draw_luajit_raptor_chart59() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_59")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart60); function draw_luajit_raptor_chart60() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_60")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart61); function draw_luajit_raptor_chart61() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.333, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_61")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart62); function draw_luajit_raptor_chart62() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.000, 'black'], ['3', 0.200, 'black'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_62")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart63); function draw_luajit_raptor_chart63() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.200, 'red'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_63")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart64); function draw_luajit_raptor_chart64() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_64")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart65); function draw_luajit_raptor_chart65() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.333, 'red'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_65")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart66); function draw_luajit_raptor_chart66() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_66")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart67); function draw_luajit_raptor_chart67() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_67")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart68); function draw_luajit_raptor_chart68() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.267, 'red'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_68")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart69); function draw_luajit_raptor_chart69() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_69")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart70); function draw_luajit_raptor_chart70() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.400, 'red'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_70")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart71); function draw_luajit_raptor_chart71() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.400, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_71")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart72); function draw_luajit_raptor_chart72() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_72")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart73); function draw_luajit_raptor_chart73() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_73")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart74); function draw_luajit_raptor_chart74() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_74")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart75); function draw_luajit_raptor_chart75() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_75")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart76); function draw_luajit_raptor_chart76() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.333, 'red'], ['9', 0.400, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_76")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart77); function draw_luajit_raptor_chart77() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_77")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart78); function draw_luajit_raptor_chart78() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.067, 'black'], ['2', 0.067, 'black'], ['3', 0.267, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_78")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_luajit_raptor_chart79); function draw_luajit_raptor_chart79() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.333, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_raptor_79")); chart.draw(view, options);} // Warmup. var c = document.getElementsByClassName("warmup"); var i; for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 1); ctx.lineTo(5, 1); ctx.lineTo(5, 10); ctx.lineTo(10, 10); ctx.stroke(); } // Flat. c = document.getElementsByClassName("flat"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 7); ctx.lineTo(10, 7); ctx.stroke(); } // Slowdown. c = document.getElementsByClassName("slowdown"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 10); ctx.lineTo(5, 10); ctx.lineTo(5, 1); ctx.lineTo(10, 1); ctx.stroke(); } // No steady state. c = document.getElementsByClassName("nosteadystate"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 6); ctx.lineTo(1, 3); ctx.lineTo(3, 10); ctx.lineTo(5, 3); ctx.lineTo(7, 10); ctx.lineTo(8, 3); ctx.lineTo(10, 6); ctx.stroke(); } // Good inconsisent c = document.getElementsByClassName("goodinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.stroke(); } // Bad inconsistent. c = document.getElementsByClassName("badinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.moveTo(1, 3); ctx.lineTo(9, 10); ctx.moveTo(1, 10); ctx.lineTo(9, 3); ctx.stroke(); } Results for Normal vs. RaptorJIT

In essence, green cells mean that RaptorJIT is better than LuaJIT; red cells mean that LuaJIT is better than RaptorJIT; yellow means they're different in a way that can't be compared; and white/grey means they're statistically equivalent. The additional “Steady performance variation (s)” column shows whether the steady state performance of different process executions is more predictable or not.

The simple conclusion to draw from this is that there isn't a simple conclusion to draw from it: the two VMs are sometimes better than each other with no clear pattern. Without having a clear steer either way, we therefore decided to use the original version of LuaJIT as our base.

One of the things that became very clear from our benchmarking is that LuaJIT is highly non-deterministic – indeed, it's the most non-deterministic VM I've seen. The practical effect of this is that even on one program, LuaJIT is sometimes very fast, and sometimes rather slow. This is, at best, very confusing for users who tend to assume that programs perform more-or-less the same every time they're run; at worst, it can create significant problems when one is trying to estimate things like server provisioning. We therefore tried various things to make performance more consistent.

The most promising approach we alighted upon is what we ended up calling “separate counters”. In a tracing JIT compiler such as LuaJIT, one tracks how often a loop (where loops are both “obvious” things like for loops, as well as less obvious things such as functions) has been executed: once it's hit a certain threshold, the loop is traced, and compiled into machine code. LuaJIT has an unusual approach to counting loops: it has 64 counters to which all loops are mapped (using the memory address of the bytecode in question). In other words, multiple loops share the same counter: the bigger the program, the more loops share the same counter. The advantage of this is that the counters map is memory efficient, and for small programs (e.g. the common LuaJIT benchmarks) it can be highly effective. However, it has very odd effects in real programs, particularly as programs get bigger: loops are compiled non-deterministically based on the particular address in memory they happen to have been loaded at.

We therefore altered LuaJIT so that each loop and each function has its own counter, stored in the bytecode to make memory reads/writes more cache friendly. The diff from normal LuaJIT to the separate counters version is as follows:

Results for Normal vs. Counters Symbol key: bad inconsistent bad inconsistent, flat flat, good inconsistent good inconsistent, no steady state no steady state, slowdown slowdown, warmup warmup.
Diff against previous results: improved worsened different unchanged.

Benchmark Classification Steady iteration (#) Steady iteration variation Steady iteration (s) Steady performance (s) Steady performance
variation (s) array3dno steady state binarytreesflat0.12462
±0.0040580.004058
was: 0.000532 bouncegood inconsistent (14 flat, 1 warmup)1.0
(1.0, 5.8)0.000
(0.000, 0.603)0.12515
δ=-0.00280
±0.000278 capnproto_decodegood inconsistent (9 flat, 6 warmup)1.0
(1.0, 24.9)(1.0, 24.9)
was: (1.0, 45.3)0.000
(0.000, 3.692)0.15042
±0.0037970.003797
was: 0.028466 capnproto_encodewarmup230.0
(56.0, 467.6)(56.0, 467.6)
was: (52.8, 280.6)28.411
(6.667, 55.951)0.11838
δ=-0.09860
±0.0019600.001960
was: 0.014541 collisiondetectorbad inconsistent (13 warmup, 2 no steady state) coroutine_ringflat0.10680
±0.0031510.003151
was: 0.001527 deltabluewarmup149.0
(149.0, 274.5)(149.0, 274.5)
was: (1.0, 1022.9)15.561
(15.430, 28.653)0.10159
±0.0010830.001083
was: 0.003195 euler14warmup61.0
(61.0, 68.3)(61.0, 68.3)
was: (60.0, 83.0)5.650
(5.592, 6.356)0.09216
±0.0001590.000159
was: 0.000743 fannkuch_reduxflat0.11976
±0.0000120.000012
was: 0.001502 fastaflat0.12200
δ=0.00100
±0.000597 havlakno steady state heapsortflat1.04378
δ=0.02461
±0.000789 jsonlua_decodeflat0.12648
δ=0.01370
±0.000556 jsonlua_encodeflat0.12860
±0.0008790.000879
was: 0.001761 knucleotideflat0.11710
±0.0005410.000541
was: 0.000811 lifebad inconsistent (9 warmup, 3 flat, 2 slowdown, 1 no steady state) luacheckflat1.00299
±0.0047780.004778
was: 0.089781 luacheck_parserbad inconsistent (12 warmup, 2 no steady state, 1 flat) luafunwarmup69.0
(69.0, 69.0)11.481
(11.331, 11.522)0.16770
±0.0015640.001564
was: 0.004918 mandelbrotbad inconsistent (14 flat, 1 no steady state) mandelbrot_bitflat0.21695
±0.000142 md5flat0.11155
δ=-0.00124
±0.000043 meteorgood inconsistent (13 warmup, 2 flat)14.0
(1.0, 15.0)(1.0, 15.0)
was: (2.0, 18.0)2.855
(0.000, 3.045)0.21606
±0.0046510.004651
was: 0.003935 moonscriptwarmup63.0
(17.7, 184.1)(17.7, 184.1)
was: (13.1, 423.3)10.046
(2.763, 29.739)0.15999
±0.0014050.001405
was: 0.001568 nbodyflat0.15898
±0.0016760.001676
was: 0.002790 nsievewarmup2.0
(2.0, 2.6)(2.0, 2.6)
was: (2.0, 2.0)0.189
(0.188, 0.297)0.17875
±0.0012660.001266
was: 0.000641 nsieve_bitwarmup4.0
(2.0, 6.0)(2.0, 6.0)
was: (3.4, 5.3)0.271
(0.097, 0.446)0.08726
δ=-0.00032
±0.0002020.000202
was: 0.000054 partialsumswarmup2.0
(2.0, 2.9)(2.0, 2.9)
was: (2.0, 2.0)0.161
(0.161, 0.295)0.14916
±0.0000810.000081
was: 0.002044 pidigitswarmup2.0
(2.0, 4.3)(2.0, 4.3)
was: (1.0, 2.3)0.130
(0.130, 0.425)0.12666
±0.0001220.000122
was: 0.002133 queensgood inconsistent (10 flat, 5 warmup)1.0
(1.0, 2.0)(1.0, 2.0)
was: (1.0, 294.4)0.000
(0.000, 0.127)0.12484
δ=0.00646
±0.0003170.000317
was: 0.000751 quicksortslowdown2.0
(2.0, 2.0)0.299
(0.298, 0.304)0.44880
δ=0.13763
±0.0204770.020477
was: 0.067395 radixsortflat0.12644
±0.0008640.000864
was: 0.000403 rayflat0.30901
±0.0021400.002140
was: 0.004022 recursive_ackflat0.11958
±0.0005100.000510
was: 0.000653 recursive_fibflat0.22864
±0.0002660.000266
was: 0.028968 resty_jsonbad inconsistent (12 flat, 2 warmup, 1 no steady state) revcompflat0.11550
±0.0025530.002553
was: 0.001753 richardsgood inconsistent (14 warmup, 1 flat)2.0
(1.7, 2.0)(1.7, 2.0)
was: (1.0, 2.0)0.150
(0.105, 0.150)0.14572
±0.0003240.000324
was: 0.010223 scimark_fftwarmup2.0
(2.0, 10.0)(2.0, 10.0)
was: (2.0, 4.7)0.140
(0.140, 1.153)0.12639
±0.0003430.000343
was: 0.000823 scimark_lugood inconsistent (11 flat, 4 warmup)1.0
(1.0, 45.3)0.000
(0.000, 5.122)0.11546
±0.0001320.000132
was: 0.000308 scimark_sorflat0.12105
±0.000148 scimark_sparseflat0.12315
±0.0007280.000728
was: 0.000585 serieswarmup2.0
(2.0, 2.0)0.347
(0.347, 0.348)0.33394
±0.0006450.000645
was: 0.003217 spectralnormflat0.13985
δ=-0.00003
±0.000007 table_cmpsortbad inconsistent (13 flat, 1 warmup, 1 slowdown)1.0
(1.0, 10.0)0.000
(0.000, 2.005)0.21828
±0.0032890.003289
was: 0.007836 google.charts.setOnLoadCallback(draw_chart_luajit_counters_0); function draw_chart_luajit_counters_0() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.800, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_0")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_1); function draw_chart_luajit_counters_1() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_1")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_2); function draw_chart_luajit_counters_2() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.267, 'red'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_2")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_3); function draw_chart_luajit_counters_3() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_3")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_4); function draw_chart_luajit_counters_4() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.267, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_4")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_5); function draw_chart_luajit_counters_5() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.267, 'black'], ['2', 0.533, 'red'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_5")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_6); function draw_chart_luajit_counters_6() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.200, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_6")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_7); function draw_chart_luajit_counters_7() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.267, 'black'], ['3', 0.133, 'red'], ['4', 0.067, 'black'], ['5', 0.200, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_7")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_8); function draw_chart_luajit_counters_8() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.200, 'black'], ['2', 0.533, 'red'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_8")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_9); function draw_chart_luajit_counters_9() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.267, 'black'], ['3', 0.133, 'red'], ['4', 0.067, 'black'], ['5', 0.200, 'black'], ['6', 0.000, 'black'], ['7', 0.133, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_9")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_10); function draw_chart_luajit_counters_10() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.333, 'red'], ['2', 0.200, 'black'], ['3', 0.200, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_10")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_11); function draw_chart_luajit_counters_11() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_11")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_12); function draw_chart_luajit_counters_12() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.267, 'black'], ['3', 0.200, 'red'], ['4', 0.133, 'black'], ['5', 0.133, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_12")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_13); function draw_chart_luajit_counters_13() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_13")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_14); function draw_chart_luajit_counters_14() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_14")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_15); function draw_chart_luajit_counters_15() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.133, 'red'], ['2', 0.133, 'black'], ['3', 0.200, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_15")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_16); function draw_chart_luajit_counters_16() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.600, 'red'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_16")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_17); function draw_chart_luajit_counters_17() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_17")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_18); function draw_chart_luajit_counters_18() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.400, 'red'], ['2', 0.133, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_18")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_19); function draw_chart_luajit_counters_19() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'red'], ['7', 0.000, 'black'], ['8', 0.133, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_19")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_20); function draw_chart_luajit_counters_20() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.067, 'black'], ['4', 0.133, 'black'], ['5', 0.133, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_20")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_21); function draw_chart_luajit_counters_21() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.200, 'red'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.200, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_21")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_22); function draw_chart_luajit_counters_22() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.133, 'red'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.200, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_22")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_23); function draw_chart_luajit_counters_23() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.200, 'red'], ['3', 0.133, 'black'], ['4', 0.333, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_23")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_24); function draw_chart_luajit_counters_24() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_24")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_25); function draw_chart_luajit_counters_25() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.133, 'black'], ['6', 0.133, 'black'], ['7', 0.333, 'red'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_25")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_26); function draw_chart_luajit_counters_26() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.133, 'red'], ['7', 0.267, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_26")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_27); function draw_chart_luajit_counters_27() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.267, 'red'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.067, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_27")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_28); function draw_chart_luajit_counters_28() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.200, 'black'], ['4', 0.267, 'red'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.133, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_28")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_29); function draw_chart_luajit_counters_29() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.133, 'red'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.533, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_29")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_30); function draw_chart_luajit_counters_30() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.133, 'black'], ['2', 0.600, 'red'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_30")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_31); function draw_chart_luajit_counters_31() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.133, 'red'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.533, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_31")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_32); function draw_chart_luajit_counters_32() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.133, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_32")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_33); function draw_chart_luajit_counters_33() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.200, 'red'], ['4', 0.200, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_33")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_34); function draw_chart_luajit_counters_34() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.000, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'black'], ['4', 0.133, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_34")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_35); function draw_chart_luajit_counters_35() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_35")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_36); function draw_chart_luajit_counters_36() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_36")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_37); function draw_chart_luajit_counters_37() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.200, 'black'], ['4', 0.267, 'red'], ['5', 0.067, 'black'], ['6', 0.200, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_37")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_38); function draw_chart_luajit_counters_38() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_38")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_39); function draw_chart_luajit_counters_39() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.667, 'red'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_39")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_40); function draw_chart_luajit_counters_40() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.200, 'red'], ['2', 0.133, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_40")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_41); function draw_chart_luajit_counters_41() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.400, 'red'], ['5', 0.267, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_41")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_42); function draw_chart_luajit_counters_42() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_42")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_43); function draw_chart_luajit_counters_43() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.267, 'red'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_43")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_44); function draw_chart_luajit_counters_44() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.933, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_44")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_45); function draw_chart_luajit_counters_45() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_45")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_46); function draw_chart_luajit_counters_46() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.600, 'red'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.133, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_46")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_47); function draw_chart_luajit_counters_47() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_47")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_48); function draw_chart_luajit_counters_48() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_48")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_49); function draw_chart_luajit_counters_49() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.200, 'black'], ['2', 0.133, 'red'], ['3', 0.000, 'black'], ['4', 0.133, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.067, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_49")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_50); function draw_chart_luajit_counters_50() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.333, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_50")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_51); function draw_chart_luajit_counters_51() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_51")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_52); function draw_chart_luajit_counters_52() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.200, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_52")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_53); function draw_chart_luajit_counters_53() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.400, 'black'], ['1', 0.200, 'red'], ['2', 0.133, 'black'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_53")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_54); function draw_chart_luajit_counters_54() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.133, 'black'], ['2', 0.333, 'red'], ['3', 0.067, 'black'], ['4', 0.200, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_54")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_55); function draw_chart_luajit_counters_55() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.067, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.067, 'red'], ['5', 0.133, 'black'], ['6', 0.067, 'black'], ['7', 0.267, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_55")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_56); function draw_chart_luajit_counters_56() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.200, 'black'], ['2', 0.200, 'red'], ['3', 0.067, 'black'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.067, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_56")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_57); function draw_chart_luajit_counters_57() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.267, 'red'], ['3', 0.133, 'black'], ['4', 0.000, 'black'], ['5', 0.200, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_57")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_58); function draw_chart_luajit_counters_58() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.467, 'red'], ['1', 0.267, 'black'], ['2', 0.000, 'black'], ['3', 0.133, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_58")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_59); function draw_chart_luajit_counters_59() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.933, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_59")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_60); function draw_chart_luajit_counters_60() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.200, 'black'], ['1', 0.133, 'black'], ['2', 0.067, 'black'], ['3', 0.067, 'red'], ['4', 0.067, 'black'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.000, 'black'], ['8', 0.133, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_60")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_61); function draw_chart_luajit_counters_61() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.067, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.933, 'red'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_61")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_62); function draw_chart_luajit_counters_62() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_62")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_63); function draw_chart_luajit_counters_63() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.333, 'black'], ['1', 0.200, 'red'], ['2', 0.267, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.067, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_63")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_64); function draw_chart_luajit_counters_64() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_64")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_65); function draw_chart_luajit_counters_65() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_65")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_66); function draw_chart_luajit_counters_66() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.133, 'black'], ['1', 0.067, 'black'], ['2', 0.133, 'black'], ['3', 0.067, 'black'], ['4', 0.133, 'red'], ['5', 0.067, 'black'], ['6', 0.133, 'black'], ['7', 0.067, 'black'], ['8', 0.133, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_66")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_67); function draw_chart_luajit_counters_67() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.133, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_67")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_68); function draw_chart_luajit_counters_68() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.667, 'red'], ['1', 0.200, 'black'], ['2', 0.000, 'black'], ['3', 0.067, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_68")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_69); function draw_chart_luajit_counters_69() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.733, 'red'], ['1', 0.133, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.067, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_69")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_70); function draw_chart_luajit_counters_70() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.000, 'black'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 1.000, 'red'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.000, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_70")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_71); function draw_chart_luajit_counters_71() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.333, 'red'], ['2', 0.200, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_71")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_72); function draw_chart_luajit_counters_72() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.267, 'red'], ['2', 0.067, 'black'], ['3', 0.200, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.067, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_72")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_73); function draw_chart_luajit_counters_73() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.267, 'black'], ['1', 0.133, 'black'], ['2', 0.133, 'red'], ['3', 0.067, 'black'], ['4', 0.333, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_73")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_74); function draw_chart_luajit_counters_74() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_74")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_75); function draw_chart_luajit_counters_75() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.533, 'red'], ['1', 0.200, 'black'], ['2', 0.133, 'black'], ['3', 0.000, 'black'], ['4', 0.067, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.067, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_75")); chart.draw(view, options);} google.charts.setOnLoadCallback(draw_chart_luajit_counters_76); function draw_chart_luajit_counters_76() { var data = google.visualization.arrayToDataTable([ ['bin', 'value', { role: 'style' } ], ['0', 0.867, 'red'], ['1', 0.000, 'black'], ['2', 0.000, 'black'], ['3', 0.000, 'black'], ['4', 0.000, 'black'], ['5', 0.000, 'black'], ['6', 0.000, 'black'], ['7', 0.000, 'black'], ['8', 0.000, 'black'], ['9', 0.133, 'black'], ]); var view = new google.visualization.DataView(data); var options = { width: 100, height: 70, bars: 'vertical', legend: { position: 'none' }, backgroundColor: 'transparent', hAxis: { title: '', gridlines: { count: 0 }, textPosition: 'none', }, vAxis: { title: '', viewWindowMode: 'explicit', viewWindow: { min: 0.0, max: 1.1, }, gridlines: { count: 0 }, textPosition: 'none', }, }; var chart = new google.visualization.ColumnChart(document.getElementById("bar_luajit_counters_76")); chart.draw(view, options);} // Warmup. var c = document.getElementsByClassName("warmup"); var i; for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 1); ctx.lineTo(5, 1); ctx.lineTo(5, 10); ctx.lineTo(10, 10); ctx.stroke(); } // Flat. c = document.getElementsByClassName("flat"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 7); ctx.lineTo(10, 7); ctx.stroke(); } // Slowdown. c = document.getElementsByClassName("slowdown"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 10); ctx.lineTo(5, 10); ctx.lineTo(5, 1); ctx.lineTo(10, 1); ctx.stroke(); } // No steady state. c = document.getElementsByClassName("nosteadystate"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(0, 6); ctx.lineTo(1, 3); ctx.lineTo(3, 10); ctx.lineTo(5, 3); ctx.lineTo(7, 10); ctx.lineTo(8, 3); ctx.lineTo(10, 6); ctx.stroke(); } // Good inconsisent c = document.getElementsByClassName("goodinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.stroke(); } // Bad inconsistent. c = document.getElementsByClassName("badinconsistent"); for (i = 0; i < c.length; i++) { var ctx = c[i].getContext("2d"); ctx.moveTo(1, 5); ctx.lineTo(9, 5); ctx.moveTo(1, 8); ctx.lineTo(9, 8); ctx.moveTo(1, 3); ctx.lineTo(9, 10); ctx.moveTo(1, 10); ctx.lineTo(9, 3); ctx.stroke(); } Results for Normal vs. Counters

In this case we’re particularly interested in the “steady performance variation (s)” column, which shows whether benchmarks have predictable steady state performance. The results are fairly clear: steady counters are, overall, a clear improvement. As you might expect, this is not a pure win, because it changes the order in which traces are made. This has several effects, including delaying some loops to be traced later than was previously the case, because counters do not hit the required threshold as quickly. This disadvantages some programs, particularly small deterministic benchmarks where loops are highly stable. In such cases, the earlier you trace the better. However, in my opinion, such programs are given undue weight when performance is considered. It’s no secret that some of the benchmarks regularly used to benchmark LuaJIT are highly optimised for LuaJIT as it stands; any changes to LuaJIT stand a good chance of degrading their performance. However, overall we feel that the overall gain in consistency, particularly for larger programs, is worth it. There's a pull request against the Lua Foundation's fork of LuaJIT which applies this idea to a mainstream fork of LuaJIT.

We then started looking at various programs that showed odd performance. One problem in particular showed up in more than one benchmark. Here's a standard example:

More consistent LuaJIT performanceCollisiondetector, Normal, Bencher9, Proc. exec. #12 (no steady state)

The problem – and it doesn't happen on every process execution, just to make it more fun – is that there are points where the benchmark slows down by over 10% for multiple in-process iterations (e.g. in this process execution, at in-process iterations 930-ish and 1050-ish). We tried over 25 separate ways to work out what was causing this — even building an instrumentation system to track what LuaJIT is doing — but in the end it turned out to be related to LuaJIT's Garbage Collector – sort of. When we moved from the 32-bit to 64-bit GC, the odd performance went away.

As such, we don’t think that the 64-bit GC “solves” the problem: however, it changes the way that pointers are encoded (doubling in size), which causes the code generator to emit a different style of code, such that the problem seems to go away. Nevertheless, this did make us reevaluate LuaJIT's GC. Tom then started work on implementing Mike Pall's suggestion for a new GC for LuaJIT (based partly on Tom's previous work and also that of Peter Cawley). He has enough implemented to run most small, and some large, programs, but it needs more work to finish it off, at which point evaluating it against the existing Lua GCs will be fascinating!

So, did we achieve everything we wanted to in 12 months? Inevitably the answer is yes and no. We did a lot more benchmarking than we expected; we've been able to make a lot of programs (particularly large programs) have more consistent performance; and we've got a fair way down the road of implementing a new GC. To whoever takes on further LuaJIT work – best of luck, and I look forward to seeing your results!

Acknowledgements: Sarah Mount implemented the stats differ; Edd Barrett implemented Krun and answered many questions on it.

Categories: Technology

OAuth Auth Server through Workers

Tue, 11/12/2018 - 23:48
OAuth Auth Server through Workers

Let’s pretend I own a service and I want to grant other services access to my service on behalf of my users. The familiar OAuth 2.0 is the industry standard used by the likes of Google sign in, Facebook, etc. to communicate safely without inconveniencing users.

Implementing an OAuth Authentication server is conceptually simple but a pain in practice. We can leverage the power of Cloudflare Workers to simplify the implementation, reduce latency, and segregate our service logic from the authentication layer.

For those unfamiliar with OAuth, I highly recommend reading a more in depth article.

The steps of the OAuth 2.0 workflow are as follows:

  1. The consumer service redirects the user to a callback URL that was setup by the auth server. At this callback URL, the auth server asks the user to sign in and accept the consumer permissions requests.
  2. The auth server redirects the user to the consumer service with a code.
  3. The consumer service asks to exchange this code for an access token. The consumer service validates their identity by including their client secret in the callback URL.
  4. The auth server gives the consumer the access token.
  5. The consumer service can now use the access token to get resources on behalf of the user.

In the rest of this post, I will be walking through my implementation of an OAuth Authentication server using a Worker. For simplicity, I will make the assumption the user has already logged in and obtained a session token in the form of a JWT that I will refer to as “token” herein. My full implementation has a more thorough flow that includes initial user login and registration.

Setup

We must be able to reference valid user sessions, codes and login information. Because Workers do not maintain state between executions, we will store this information using Cloudflare Storage. We setup up three namespaces called: USERS, CODES, and TOKENS .

On your OAuth server domain, create two empty worker scripts called auth and token. Bind the three namespaces to the two workers scripts. Then configure the namespaces to the scripts so that your resources end up looking like:

OAuth Auth Server through Workers

To put and get items from storage using KV Storage syntax:

// @ts-ignore CODES.get(“user@ex.com”)

We include // @ts-ignore preceding all KV storage commands. We do not have type definitions for these variables locally, so Typescript would throw an error at compile time otherwise.

To set up a project using Typescript and the Cloudflare Previewer, follow this blog post. Webpack will allow us to import which we will need to use the JWT library jsonwebtoken.

import * as jwt from "jsonwebtoken";

Remember to run:

npm install jsonwebtoken && npm install @types/jsonwebtoken

Optionally, we can set up a file to specify endpoints and credentials.

import { hosts } from "./private"; export const credentials = {/* for demo purposes, ideally use KV to store secrets */ clients: [{ id: "victoriasclient", secret: "victoriassecret" }], storage: { secret: "somesecrettodecryptfromtheKV" } }; export const paths = { auth: { authorize: hosts.auth + "/authorize", login: hosts.auth + "/login", code: hosts.auth + "/code", }, token: { resource: hosts.token + "/resource", token: hosts.token + "/authorize", } } 1. Accept page after callback

The consumer service generates some callback URL that redirects the user to our authentication server. The authentication server then presents the user with a login or accept page to generate a code. The authentication server thus must listen on the authorize url endpoint and return giveLoginPageResponse.

addEventListener("fetch", (event: FetchEvent) => { const url = new URL(event.request.url); if (url.pathname.includes("/authorize")) return event.respondWith(giveLoginPageResponse(event.request)); } export async function giveLoginPageResponse(request: Request) { ...checks for cases where I am not necessarily logged in... let token = getTokenFromRequest(request) if (token) { //user already signed in return new Response(giveAcceptPage(request) }

Since the user already has a stored session, we can use a method giveAcceptPage. To display the accept page and return a redirect to generate the code.

export function giveAcceptPage(request: Request) { let req_url = new URL(request.url); let params = req_url.search let fetchCodeURL = paths.auth.code + params return `<!DOCTYPE html> <html> <body> <a href="${fetchCodeURL}"> Accept</button> </body> </html> `; } 2. Redirect back to consumer

At the endpoint for fetchCodeURL, the authentication server will redirect the user’s browser to the consumer’s page as specified by redirect_uri in the original URL params of the callback with the code.

addEventListener("fetch", (event: FetchEvent) => { ... if (url.pathname.includes("/code")) return event.respondWith(redirectCodeToConsumer(event.request)); } export async function redirectCodeToConsumer(request: Request) { let session = await verifyUser(request) if (session.msg == "403") return new Response(give403Page(), { status: 403 }) if (session.msg == "dne") return registerNewUser(session.email, session.pwd) let code = Math.random().toString(36).substring(2, 12) try { let req_url = new URL(request.url) let redirect_uri = new URL(encodeURI(req_url.searchParams.get("redirect_uri"))) let client_id = new URL(encodeURI(req_url.searchParams.get("client_id"))) // @ts-ignore await CODES.put(client_id + email, code) redirect_uri.searchParams.set("code", code); redirect_uri.searchParams.set("response_type", "code"); return Response.redirect(redirect_uri.href, 302); } catch (e) { // @ts-ignore await CODES.delete(email, code) return new Response( JSON.stringify(factoryIError({ message: "error with the URL passed in" + e})), { status: 500 }); } } 3. Code to Token Exchange

Now the consumer has the code. They can use this code to request a token. On our token worker, configure the endpoint to exchange the code for the consumer service to grant a token.

addEventListener("fetch", (event: FetchEvent) => { ... if (url.pathname.includes("/token")) return event.respondWith(giveToken(event.request));

Grab the code from the request and validate this code matches the code that is stored for this client. Once the code is verified, deliver the token by grabbing the existing token from the KV storage or by signing the user information to generate a new token.

export async function giveToken(request: Request) { let req_url = new URL(request.url); let code = req_url.searchParams.get("code"); let email = req_url.searchParams.get("email"); if (code){ if(!validClientSecret(request) return errorResponse() // @ts-ignore let storedCode = await CODES.get(email) if(code != storedCode) return new Response(give403Page(), { status:403}) let tokenJWT = jwt.sign(email, credentials.client.secret); ... return token 4. Give the token to the consumer

Continuing where step 3 left off from the giveToken method, respond to the consumer with this valid token.

... headers.append("set-cookie", "token=Bearer " + tokenJWT); // @ts-ignore await TOKENS.put(email, tokenJWT) var respBody = factoryTokenResponse({ "access_token": tokenJWT, "token_type": "bearer", "expires_in": 2592000, "refresh_token": token, "token": token }) } else { respBody.errors.push(factoryIError({ message: "there was no code sent to the authorize token url" })) } return new Response(JSON.stringify(respBody), { headers }); } 5. Accepting the token

At this point voila, your duty as an OAuth 2.0 Authentication server is complete! The consumer service that wishes to use your service has the token that you have not so magically generated.

The consumer server would send a request including the token:

GET /resource/some-goods Authorization: Bearer eyJhbGci..bGAqA

Authentication server would validate the token and give the goods:

export async function giveResource(request: Request) { var respBody: HookResponse = factoryHookResponse({}) let token = "" let decodedJWT = factoryJWTPayload() try { //validate request is who they claim token = getCookie(request.headers.get("cookie"), "token") if (!token) token = request.headers.get("Authorization").substring(7) decodedJWT = jwt.verify(token, credentials.storage.secret) // @ts-ignore let storedToken = await TOKENS.get(decodedJWT.sub) if (isExpired(storedToken)) throw new Error("token is expired") /* TODO instead of throwing error send to refresh */ if (storedToken != token) throw new Error("token does not match what is stored") } catch (e) { respBody.errors.push(factoryIError({ message: e.message, type: "oauth" })) return new Response(JSON.stringify(respBody), init) } respBody.body = getUsersPersonalBody(decodedJWT.sub) return new Response(JSON.stringify(respBody), init) }

The boundaries of serverless are pushed everyday, though if your app just needs to authorize users, you may be better off using Cloudflare Access. We've demonstrated that a full blown OAuth 2.0 authentication server implementation can be achieved with Cloudflare Workers and Storage.

Stay tuned for a follow-up blog post on an OAuth consumer implementation.

Categories: Technology

The truth about Black Friday and Cyber Monday

Tue, 11/12/2018 - 10:50
The truth about Black Friday and Cyber Monday

At Cloudflare we handle a lot of traffic on behalf of our customers. Something we all see and hear a lot about at this time of year are Black Friday (23 November this year) and Cyber Monday (26 November) - but just how important are these days on the Internet?

The truth about Black Friday and Cyber MondayBlack Friday by Per-Olof Forsberg, license: CC BY 2.0

To try and answer this question, we took a look at anonymised samples of HTTP requests crossing our network. First of all, let’s look at total page views from across our global network from the last few weeks and see if we can spot Black Friday and Cyber Monday:

The truth about Black Friday and Cyber MondayAll page views

So this is total page views by day (UTC) from November 19 (a week before Cyber Monday) until Monday December 3. Other than follow-the-sun fluctuations in a repeating daily pattern, each whole day is pretty similar in shape and size compared to the last. Black Friday and Cyber Monday aren’t visible in overall traffic patterns.

Get specific

We have a very diverse set of customers across 12 million domain names and not all of them are selling products or doing so directly online. To identify those websites that are, I used metadata from the wonderful HTTP Archive project to export a list of domains using Cloudflare that were also running ecommerce software.

Here are the page views for these ecommerce sites over the same time period:

The truth about Black Friday and Cyber MondayEcommerce page views

So we can see clearly that our ecommerce customers are seeing a big increase in page views on November 23 and 26. Black Friday and Cyber Monday are most certainly a thing. This year Black Friday was quite a bit busier than Cyber Monday - around 22% busier in terms of page views. If we compare the page views of each day to the week prior, we can see the changes clearly:

The truth about Black Friday and Cyber Monday% page view change vs previous week

The uplift starts on Wednesday but really kicks in during Thanksgiving with an increase of more than 100% on Black Friday.

Browsing vs Buying

So we’ve established that these shopping days are important in terms of visitor activity. More pages are being viewed on these days - but is anyone buying anything?

We’re dealing with trillions of requests across a really large data set of different websites without any specific knowledge of what a purchase transaction would look like for each - so to approximate this I took a crude approach, which is to look for successful checkout interactions in the data. If you imagine a typical ecommerce application makes a purchase with a HTTP request like “POST /store/checkout HTTP/1.1” we can look for requests similar to this to understand the activity.

The truth about Black Friday and Cyber Monday% of checkout interactions vs prior week

We can see here that Black Friday has an almost 200% increase in checkout interactions compared to the previous Friday.

Using this raw number of checkout interactions to compare with the page views we have something approximating a conversion %. This is not a true conversion figure - calculating a true conversion figure would require data that identifies individuals and detailed action tracking for each website. What we have is the total number of page views (HTTP requests that return HTML successfully) compared to the total number of POST requests to a checkout. This gives us a baseline to compare changes in “conversion” over these big November shopping days:

The truth about Black Friday and Cyber Monday% of checkout interactions / page views vs prior week

Each bar on this chart represents the % change in checkout interactions as a proportion of page views compared to the same day the previous week. We can see this increased by 45% on Black Friday compared to the Friday before (boring old beige Friday November 16). The following Saturday was booming at 60% - because we’re dealing with time in UTC, a UTC Saturday actually includes Black Friday traffic for some parts of the world, the same can be said of Tuesday which contains overlap from Cyber Monday - we’ll break this down a bit later.

On Cyber Monday, the increase actually beats Black Friday, meaning page views lead to cart interactions 57% more often than the prior Monday (boring old vanilla Monday November 19), albeit from a lower number of transactions.

What devices are people buying on?

What we see here is just how much more browsing people do on mobiles today vs desktop, with mobile winning most days:

The truth about Black Friday and Cyber MondayPage Views by Device Type

When it comes to checkout interactions though, we can see the situation is switched with visitors more likely to interact with the checkout on a desktop overall, but even more so on Black Friday (14% more likely) and Cyber Monday (20% more likely).

The truth about Black Friday and Cyber MondayCheckout Interaction as % of Page Views

Let’s look at a specific region to understand more, starting with the US:

The truth about Black Friday and Cyber MondayUSA Page Views (PST)

We can see a more normal weekday pattern on the prior Thursday & Friday (15 & 16 Nov) whereby desktop page views eclipse mobile during the daytime while people are at their desks. In the evenings and weekends, mobile takes over. What we see from the 21st onward is evidence of people taking time off work and doing more with their mobile devices. Even on Thanksgiving, there is still a big rise in activity as people start gearing up for Friday’s deals or finding ways to avoid political discussion with relatives at home!

On Cyber Monday, traffic earlier in the day is lower as people return to work, however we are seeing heavy use of desktop devices. As the working day ends, mobile once again dominates. Things begin to settle back into a more regular pattern from Tuesday November 27 onwards.

Let's take a look at checkout interaction over the Black Friday to Cyber Monday weekend by device type.

The truth about Black Friday and Cyber MondayUSA Checkout Interaction % (PST)

Despite all of that mobile browsing activity, desktop devices are more commonly used for checkout actions. People seem to browse more on mobile, committing to buy more often with desktop, it may also just be that mobile users have more distractions both on the device and in the real world and are therefore less likely to complete a purchase. From personal experience, I also think the poor mobile optimisation of some sites’ checkout flows make desktop preferrable - and when customers are incentivised with discounts & deals, they are more likely to switch devices to complete a transaction if they hit an issue.

Is Black Friday / Cyber Monday international?

It might be obvious if you’re reading this from the UK, but despite the fact that Thanksgiving is not a holiday here, retailers have very much picked up the mantle from US retailers and seized the opportunity to drive sales over this weekend.

The truth about Black Friday and Cyber MondayUK Page Views (UTC)

Page views to ecommerce websites on Cloudflare look very similar in shape to the US on Black Friday. However, mobile is more dominant in the UK, even during working hours. It’s worth noting one big difference here - Cyber Monday in the UK was only 22% up in terms of page views compared to the prior Monday - in the US the increase was more than 4x that.

The truth about Black Friday and Cyber MondayUK Checkout Interaction as % of Page Views

When it comes to checkout, it also looks like UK visitors to ecommerce sites commit more with their mobile, but desktop is still more likely to lead to more conversion.

Taking Germany as another example, here’s how page views look:

The truth about Black Friday and Cyber MondayGermany Page Views (CET)

Desktop use during typical working hours is much more pronounced in Germany. Black Friday and Cyber Monday show higher page views than a normal Friday / Monday but the difference is much smaller than regions such as the US & UK.

Conclusions

Black Friday is spreading internationally despite these still being normal working days for the rest of the world. Cyber Monday is also increasing ecommerce activity internationally but tends to be quieter than Black Friday. Overall, mobile browsing eclipses desktop, but those desktop page views tend to lead to checkout more often.

Retailers should continue to invest in making their mobile & desktop ecommerce experiences fast & resilient to seize on these key days.

Categories: Technology

A new look on your Cloudflare dashboard

Tue, 11/12/2018 - 02:10
A new look on your Cloudflare dashboard

Building a great customer experience is a top priority here at Cloudflare. Over the last week, we have been  slowly rolling out an improvement to our customer dashboard - specifically the zone overview page. We are now ready to share the new dashboard with everyone.

A new look on your Cloudflare dashboardThe new Cloudflare dashboard

Many of the changes you’ll notice first were driven directly by customer feedback. We’ve heard time and time again that site owners would like better visibility of their key analytics, and for it to be easier to apply certain common settings changes. This new dashboard provides both, with a snapshot of several of your key analytics, and a new toolkit area for easy access to key functions. This page has also been redesigned from top to bottom for responsiveness across various devices and screen widths.

Even more interesting is all the work under-the-hood that went into making this release and how it sets the stage for improvements to our customer experience as we look ahead to 2019. This is one of the first pieces of production Product work shipped using our new prototyping framework. We will soon be sharing details of the design systems work that underlies the new experience on your Cloudflare dashboard.

In the meanwhile, take a look at the new overview page, and give us some feedback!

Categories: Technology

EU Terrorist Content Online proposal – political haste and unintended consequences

Tue, 04/12/2018 - 17:00
EU Terrorist Content Online proposal – political haste and unintended consequencesPhoto by Sara Kurfeß / UnsplashEU Terrorist Content Online proposal – political haste and unintended consequences

In September, the European Commission presented a legislative proposal to address the removal of terrorist content online. There has been significant political pressure, particularly as the EU elections of 2019 approach, towards internet companies taking on increased responsibility in the area of terrorist propaganda online. This proposal would be a marked move from various voluntary initiatives taken up by some social media companies in recent times towards a legal responsibility framework for many.  

While appreciating the concerns around terrorism, Cloudflare is not only troubled by the late presentation of this proposal – which leaves inadequate time for a thorough review before this EU legislative term expires – but also much of the substance. Along with others such as CDT, GSMA/ETNO and Mozilla, we have significant concerns around the legal implications, practical application and possible unintended consequences of the proposal, some of which we outline below. Furthermore, we believe that little evidence has been presented as to the necessity of the proposed measures.

Concerns and shortcomings

The Commission’s proposal does not account for the complexity and range of information society services having a storage component - not all services have the same reach and impact, and so a one-size-fits-all approach is not justified. This has been a concerning trend overall with EU legislative proposals: in wishing to regulate the behaviour of a few large social media platforms, many other providers of differing sizes and types are brought within scope with sweeping and clumsy definitions. The reality is that only the largest providers have the resources and means to address many of the requirements, which further cements their position as gate-keepers and dominant players in the marketplace.

We also have concerns that the proposal will chill legitimate online speech. The overly broad definition of “terrorist content” covers the “inciting or advocating, including by glorifying, the commission of terrorist offences” and other activities related to the encouragement, instruction or promotion of terrorist-related activities. Under this definition, almost any type of terrorism-related content, including of an educational, journalistic or academic-based nature, could be within scope of a removal order. This poses very real risks of removal of legitimate content, with its consequent effects on freedom of expression and information, as companies are incentivized to avoid any possibility of liability. The proposal is simply not robust enough to ensure that legitimate content actually remains online.

Cloudflare would suggest that given the high risk of unintended consequences, if a proposal like this is to go forward as a result of political pressure, additional due process should be required and the proposal should be significantly narrowed. Judicial authorities alone should be empowered to issue removal orders. From a practical standpoint, having all EU countries, each with multiple potential authorities, issuing orders for content removal seems likely to exacerbate concerns about the effect on freedom of expression. Each EU country should instead elect just one authority to issue the orders or better still, a one-stop-shop model could be established with one European entity serving as the single interface for hosting service providers. Given that providers are in turn asked to appoint one company and legal representative, this seems like a practical way to streamline the process and reduce the likelihood of divergent views about what constitutes content deemed appropriate for removal.

The proposal calls for the removal of terrorism content within a one-hour timeframe. Regardless of the fact that a legal assessment of the content is not required, an operational discussion still has to take place within a company as to the appropriate type of removal measure and how and where it is effected. As noted in the Commission’s Impact Assessment, over 90% of European companies are SMEs and so asking all providers to fulfil the removal requirements within 60 minutes is highly unreasonable.

The privatisation of law enforcement - a troubling trend

A growing number of regulatory and policy initiatives at European level have seen Internet service providers encouraged to proactively decide on the legality and nature of content online, undertake risk assessments along with the balancing of fundamental rights and freedoms, and evaluate any conflicts of law, all while potentially facing liability if they make those assessments incorrectly. This has effectively resulted in a privatisation of law enforcement, with the additional risk that smaller providers will look to crude, untested tools in order to help meet compliance. The shifting of the burden of responsibility from the State to the provider is also seen here in the ask to providers to manage complaints from content providers, with no role foreseen for the competent authorities.

Furthermore, this proposal foresees a scenario whereby companies could be asked to take on proactive measures – which could include filtering – if they receive even one removal order. Not only is this not a proportionate ask but it is a departure from the well-established legal principle of “no general monitoring” as set out in Article 15 of the EU eCommerce Directive. The idea of internet filtering has been creeping into a range of legislative proposals and should be a concern for all.

Process ahead

Member States are advancing at pace with their review and may already reach an agreed position this week. Meanwhile the European Parliament, which also has to undertake a review, has yet to commence its work in earnest. Cloudflare will be working to ensure that our concerns around this proposal are heard and that due process, legality and proportionality are not sacrificed in the political rush. You can follow progress of this file here.

Categories: Technology

How well do you know website performance?

Tue, 04/12/2018 - 13:00
How well do you know website performance?How well do you know website performance?

How quickly did this blog post load? Did you happen to notice?

Most website visitors do notice that sort of thing, even if they don't realize it consciously. In fact, virtually all visitors have strong opinions about how quickly a website should load, how responsive it should be, and how often it should go down (preferably, never). Most users strongly prefer webpages that take under 5 seconds to load, and they're much more likely to leave without buying products or reading content if a page takes too long to render.

Everyone should understand how to keep websites fast

Part of the Cloudflare mission is to make the Internet better by helping it perform better. One way we are delivering on our mission is by sharing easy-to-read educational content to assist anyone with a web property.

We recently launched a performance-themed Learning Center: a series of educational articles on why performance matters, the factors affecting performance, and some of the best practices for making websites faster. Check out the Performance Learning Center!

This Learning Center is for anyone who wants to really do a deep dive into the complex topic of website performance, no matter what their technical background is. We hope these articles can demystify the causes of slow page load times, and help make a strong business case for speeding up a website.

And no, we aren't trying to trick anyone into reading a bunch of material about how great the Cloudflare CDN is (even though we do think it's pretty great). CDNs do speed up websites, but performance is much more complex than that, so we've covered topics like:

Also, be sure to check out our other Learning Center content to learn about how a DDoS attack works, what domain registrars do, why "serverless" computing still involves servers, and other technical topics you wish someone would take the time to explain clearly.

Categories: Technology

Cloudflare Apps Fund 2.0

Fri, 30/11/2018 - 08:52

Today we are launching the second iteration of the Cloudflare App Developer Fund.

This is an investment fund started in partnership with Venrock, Pelion Venture Partners, and New Enterprise Associates. We created the fund to provide developers with resources so that they can build apps using the Cloudflare network and our serverless product, Workers, which enables developers to create applications without configuring or maintaining infrastructure. The goal is to fuel a new economy that grows into a thriving ecosystem so that developers’ serverless innovations can enjoy commercial success.  

Cloudflare Apps with Workers allows developers to package Workers, delivering new worker-powered experiences to Cloudflare customers, all within a few clicks via Cloudflare Apps.

Our CTO, John Graham-Cumming talks about it in a recently recorded video:

In short, Cloudflare is looking for apps that will advance our mission to help build a better Internet: one that is safe, fast and reliable.   We are seeking out apps that have a clear vision, delight customers by the ease of use and align with our vision. Having already seen multiple use cases around custom application security, data loss prevention, and bot mitigation, we can’t wait to see what else you come up with.

We welcome apps coming from a variety of sources, ranging from standalone developers who have identified a particular market opportunity, to engineers who realize their internal idea has utility for a wider audience.  Since our beta launch of Cloudflare Apps with Workers we’ve already seen keen interest across developers who are looking to bundle and deliver complex worker logic for anyone else to consume by publishing to the Apps marketplace.  

With our fund we are helping this move faster.

Apply Now!

Categories: Technology

Logs from the Edge

Thu, 29/11/2018 - 18:45
Logs from the Edge

With Cloudflare Workers, our JavaScript environment at the edge, it is possible to send traffic logs to arbitrary locations. In this post we are going to discuss an example Worker implementation on how to achieve this. So if you are building or maintaining your own traffic logging/analytics environment, read on.

To build the underlying script we are going to leverage sub requests. Sub requests, which can be spawned from the initial HTTP/S request, can be used to aggregate and compose a response from several back end services, or, like in the example discussed here, to post data to a specific endpoint. Sub requests can be made asynchronously and after the initial request has been fully served to avoid adding unnecessary latency to the main request.

Logs from the Edge

The Worker Code

In this example we assume an Elastic stack has been set up at elk.example.com and has been configured to receive via HTTP/S PUT requests a number of fields for each log line. The full script that we are going to look at can be found below:

addEventListener('fetch', event => { event.respondWith(fetchAndLog(event)); }) async function fetchAndLog(event) { const response = await fetch(event.request); event.waitUntil(logToElk(event.request, response)); return response; } async function logToElk(request, response) { var ray = request.headers.get('cf-ray') || ''; var id = ray.slice(0, -4); var data = { 'timestamp': Date.now(), 'url': request.url, 'referer': request.referrer, 'method': request.method, 'ray': ray, 'ip': request.headers.get('cf-connecting-ip') || '', 'host': request.headers.get('host') || '', 'ua': request.headers.get('user-agent') || '', 'cc': request.headers.get('Cf-Ipcountry') || '', 'colo': request.cf.colo, 'tlsVersion': request.cf.tlsVersion || '', 'tlsCipher': request.cf.tlsCipher || '', 'status': response.status, }; var url = "https://elk.example.com/weblogs/logs/" + id + "?pipeline=weblogs&pretty" await fetch(url, { method: 'PUT', body: JSON.stringify(data), headers: new Headers({ 'Content-Type': 'application/json', }) }) }

Let's look at the script in a little more detail:

addEventListener('fetch', event => { event.respondWith(fetchAndLog(event)); }) async function fetchAndLog(event) { const response = await fetch(event.request); event.waitUntil(logToElk(event.request, response)); return response; }

At the start of the script we are listening to all request events, and on each request, we are calling the fetchAndLog function. This function will proxy the request as is and asynchronously call the logToElk function that will post a log line to our ELK stack. As this function is executed asynchronously, delays while logging to ELK will not affect the original request.

Let's take a deeper look at the logToElk function.

var ray = request.headers.get('cf-ray') || ''; var id = ray.slice(0, -4); var data = { 'timestamp': Date.now(), 'url': request.url, 'referer': request.referrer, 'method': request.method, 'ray': ray, 'ip': request.headers.get('cf-connecting-ip') || '', 'host': request.headers.get('host') || '', 'ua': request.headers.get('user-agent') || '', 'cc': request.headers.get('Cf-Ipcountry') || '', 'colo': request.cf.colo, 'tlsVersion': request.cf.tlsVersion || '', 'tlsCipher': request.cf.tlsCipher || '', 'status': response.status, };

The first part is collecting the data we wish to log. Some of these fields are part of the standard fields of any HTTP request (e.g. the URL, the HTTP method etc.), however, we are also adding fields specific to Cloudflare such as the ray ID (a unique identifier of any HTTP request proxying via Cloudflare), the country code as provided by Cloudflare IP to country logic, and the PoP/colo ID that the request is hitting.

var url = "https://elk.example.com/weblogs/logs/" + id + " pipeline=weblogs&pretty" await fetch(url, { method: 'PUT', body: JSON.stringify(data), headers: new Headers({ 'Content-Type': 'application/json', }) })

Once we have all the fields we wish to log saved in our data variable, we need to perform a sub request to PUT the log line to our backend ELK stack. By calling the fetch function we are initiating a new HTTP request for which we are specifying the request method, the body and additional headers as expected by the ELK stack (in this case we are PUTing JSON content).

And that is it, with a small Worker script you can import Cloudflare traffic logs into your ELK stack in real time. If you found this useful we also talked about logging events and alerts to Sentry via workers in the past, and community members have also shared similar methods for other logging tools such as logdna.

Making it better with Argo Tunnel and Access

As an additional improvement it is worth noting that an Elastic Stack uses Kibana as a front end visualisation interface available over HTTP/S. The Kibana endpoint (let's assume kibana.example.com) can also be proxied via Cloudflare, but it is normally used only internally within an organization. The origin therefore needs to be protected and made accessible only to colleagues.

We can use two Cloudflare features to improve the Kibana deployment:

  • Argo Tunnel allows the Kibana origin to reach out directly to Cloudflare, avoiding the need of a publicly accessible IP address or hostname;
  • Cloudflare Access allows you to integrate Cloudflare with your Identity and Access Management tool, and define rules that specify which users or groups have access to the Kibana instance;
Logs for Everyone

Before we launched Workers, retrieving raw logs from Cloudflare was only available to enterprise customers. With a little technical effort you can now start receiving logs from the edge by leveraging Workers regardless of which plan you are using. Workers are available starting on a pay per usage model and the ELK stack is open source.

Enterprise customers can still retrieve raw traffic logs with our Enterprise Log Share (ELS) feature. If ELS is turned on, all requests for the chosen application will be logged and stored on Cloudflare infrastructure. The logs can be downloaded when required via a RESTful API or pushed directly to Amazon S3 or Google Cloud (soon also Microsoft Azure) for further processing in your favorite log analysis tool. Logs with ELS are available within 10 minutes of the request being processed at the edge regardless of location, and they will have a number of additional fields that are not yet available to the Worker environment. ELS also guarantees log delivery and retention periods and allows you not to worry about load and storage.

Categories: Technology

Know your SCM_RIGHTS

Thu, 29/11/2018 - 09:54

As TLS 1.3 was ratified earlier this year, I was recollecting how we got started with it here at Cloudflare. We made the decision to be early adopters of TLS 1.3 a little over two years ago. It was a very important decision, and we took it very seriously.

It is no secret that Cloudflare uses nginx to handle user traffic. A little less known fact, is that we have several instances of nginx running. I won’t go into detail, but there is one instance whose job is to accept connections on port 443, and proxy them to another instance of nginx that actually handles the requests. It has pretty limited functionality otherwise. We fondly call it nginx-ssl.

Back then we were using OpenSSL for TLS and Crypto in nginx, but OpenSSL (and BoringSSL) had yet to announce a timeline for TLS 1.3 support, therefore we had to implement our own TLS 1.3 stack. Obviously we wanted an implementation that would not affect any customer or client that would not enable TLS 1.3. We also needed something that we could iterate on quickly, because the spec was very fluid back then, and also something that we can release frequently without worrying about the rest of the Cloudflare stack.

The obvious solution was to implement it on top of OpenSSL. The OpenSSL version we were using was 1.0.2, but not only were we looking ahead to replace it with version 1.1.0 or with BoringSSL (which we eventually did), it was so ingrained in our stack and so fragile that we wouldn’t be able to achieve our stated goals, without risking serious bugs.

Instead, Filippo Valsorda and Brendan McMillion suggested that the easier path would be to implement TLS 1.3 on top of the Go TLS library and make a Go replica of nginx-ssl (go-ssl). Go is very easy to iterate and prototype, with a powerful standard library, and we had a great pool of Go talent to use, so it made a lot of sense. Thus tls-tris was born.

The question remained how would we have Go handle only TLS 1.3 while letting nginx handling all prior versions of TLS?

And herein lies the problem. Both TLS 1.3 and older versions of TLS communicate on port 443, and it is common knowledge that only one application can listen on a given TCP port, and that application is nginx, that would still handle the bulk of the TLS traffic. We could pipe all the TCP data into another connection in Go, effectively creating an additional proxy layer, but where is the fun in that? Also it seemed a little inefficient.

Meet SCM_RIGHTS

So how do you make two different processes, written in two different programming languages, share the same TCP socket?

Fortunately. Linux (or rather UNIX) provides us with just the tool that we need. You can use UNIX-domain sockets to pass file descriptors between applications, and like everything else in UNIX connections are files.
Looking at man 7 unix we see the following:

Ancillary messages Ancillary data is sent and received using sendmsg(2) and recvmsg(2). For historical reasons the ancillary message types listed below are specified with a SOL_SOCKET type even though they are AF_UNIX specific. To send them set the cmsg_level field of the struct cmsghdr to SOL_SOCKET and the cmsg_type field to the type. For more information see cmsg(3). SCM_RIGHTS Send or receive a set of open file descriptors from another process. The data portion contains an integer array of the file descriptors. The passed file descriptors behave as though they have been created with dup(2).

Technically you do not send “file descriptors”. The “file descriptors” you handle in the code are simply indices into the processes' local file descriptor table, which in turn points into the OS' open file table, that finally points to the vnode representing the file. Thus the “file descriptor” observed by the other process will most likely have a different numeric value, despite pointing to the same file.

We can also check man 3 cmsg as suggested, to find a handy example on how to use SCM_RIGHTS:

struct msghdr msg = { 0 }; struct cmsghdr *cmsg; int myfds[NUM_FD]; /* Contains the file descriptors to pass */ int *fdptr; union { /* Ancillary data buffer, wrapped in a union in order to ensure it is suitably aligned */ char buf[CMSG_SPACE(sizeof(myfds))]; struct cmsghdr align; } u; msg.msg_control = u.buf; msg.msg_controllen = sizeof(u.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int) * NUM_FD); fdptr = (int *) CMSG_DATA(cmsg); /* Initialize the payload */ memcpy(fdptr, myfds, NUM_FD * sizeof(int));

And that is what we decided to use. We let OpenSSL read the “Client Hello” message from an established TCP connection. If the “Client Hello” indicated TLS version 1.3, we would use SCM_RIGHTS to send it to the Go process. The Go process would in turn try to parse the rest of the “Client Hello”, if it were successful it would proceed with TLS 1.3 connection, and upon failure it would give the file descriptor back to OpenSSL, to handle regularly.

So how exactly do you implement something like that?

Since in our case we established that the C process will listen for TCP connections, our other process will have to listen on a UNIX socket, for connections C will want to forward.

For example in Go:

type scmListener struct { *net.UnixListener } type scmConn struct { *net.UnixConn } var path = "/tmp/scm_example.sock" func listenSCM() (*scmListener, error) { syscall.Unlink(path) addr, err := net.ResolveUnixAddr("unix", path) if err != nil { return nil, err } ul, err := net.ListenUnix("unix", addr) if err != nil { return nil, err } err = os.Chmod(path, 0777) if err != nil { return nil, err } return &scmListener{ul}, nil } func (l *scmListener) Accept() (*scmConn, error) { uc, err := l.AcceptUnix() if err != nil { return nil, err } return &scmConn{uc}, nil }

Then in the C process, for each connection we want to pass, we will connect to that socket first:

int connect_unix() { struct sockaddr_un addr = {.sun_family = AF_UNIX, .sun_path = "/tmp/scm_example.sock"}; int unix_sock = socket(AF_UNIX, SOCK_STREAM, 0); if (unix_sock == -1) return -1; if (connect(unix_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { close(unix_sock); return -1; } return unix_sock; }

To actually pass a file descriptor we utilize the example from man 3 cmsg:

int send_fd(int unix_sock, int fd) { struct iovec iov = {.iov_base = ":)", // Must send at least one byte .iov_len = 2}; union { char buf[CMSG_SPACE(sizeof(fd))]; struct cmsghdr align; } u; struct msghdr msg = {.msg_iov = &iov, .msg_iovlen = 1, .msg_control = u.buf, .msg_controllen = sizeof(u.buf)}; struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); *cmsg = (struct cmsghdr){.cmsg_level = SOL_SOCKET, .cmsg_type = SCM_RIGHTS, .cmsg_len = CMSG_LEN(sizeof(fd))}; memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); return sendmsg(unix_sock, &msg, 0); }

Then to receive the file descriptor in Go:

func (c *scmConn) ReadFD() (*os.File, error) { msg, oob := make([]byte, 2), make([]byte, 128) _, oobn, _, _, err := c.ReadMsgUnix(msg, oob) if err != nil { return nil, err } cmsgs, err := syscall.ParseSocketControlMessage(oob[0:oobn]) if err != nil { return nil, err } else if len(cmsgs) != 1 { return nil, errors.New("invalid number of cmsgs received") } fds, err := syscall.ParseUnixRights(&cmsgs[0]) if err != nil { return nil, err } else if len(fds) != 1 { return nil, errors.New("invalid number of fds received") } fd := os.NewFile(uintptr(fds[0]), "") if fd == nil { return nil, errors.New("could not open fd") } return fd, nil } Rust

We can also do this in Rust, although the standard library in Rust does not yet support UNIX sockets, but it does let you address the C library via the libc crate. Warning, unsafe code ahead!

First we want to implement some UNIX socket functionality in Rust:

use libc::*; use std::io::prelude::*; use std::net::TcpStream; use std::os::unix::io::FromRawFd; use std::os::unix::io::RawFd; fn errno_str() -> String { let strerr = unsafe { strerror(*__error()) }; let c_str = unsafe { std::ffi::CStr::from_ptr(strerr) }; c_str.to_string_lossy().into_owned() } pub struct UNIXSocket { fd: RawFd, } pub struct UNIXConn { fd: RawFd, } impl Drop for UNIXSocket { fn drop(&mut self) { unsafe { close(self.fd) }; } } impl Drop for UNIXConn { fn drop(&mut self) { unsafe { close(self.fd) }; } } impl UNIXSocket { pub fn new() -> Result<UNIXSocket, String> { match unsafe { socket(AF_UNIX, SOCK_STREAM, 0) } { -1 => Err(errno_str()), fd @ _ => Ok(UNIXSocket { fd }), } } pub fn bind(self, address: &str) -> Result<UNIXSocket, String> { assert!(address.len() < 104); let mut addr = sockaddr_un { sun_len: std::mem::size_of::<sockaddr_un>() as u8, sun_family: AF_UNIX as u8, sun_path: [0; 104], }; for (i, c) in address.chars().enumerate() { addr.sun_path[i] = c as i8; } match unsafe { unlink(&addr.sun_path as *const i8); bind( self.fd, &addr as *const sockaddr_un as *const sockaddr, std::mem::size_of::<sockaddr_un>() as u32, ) } { -1 => Err(errno_str()), _ => Ok(self), } } pub fn listen(self) -> Result<UNIXSocket, String> { match unsafe { listen(self.fd, 50) } { -1 => Err(errno_str()), _ => Ok(self), } } pub fn accept(&self) -> Result<UNIXConn, String> { match unsafe { accept(self.fd, std::ptr::null_mut(), std::ptr::null_mut()) } { -1 => Err(errno_str()), fd @ _ => Ok(UNIXConn { fd }), } } }

And the code to extract the file desciptor:

#[repr(C)] pub struct ScmCmsgHeader { cmsg_len: c_uint, cmsg_level: c_int, cmsg_type: c_int, fd: c_int, } impl UNIXConn { pub fn recv_fd(&self) -> Result<RawFd, String> { let mut iov = iovec { iov_base: std::ptr::null_mut(), iov_len: 0, }; let mut scm = ScmCmsgHeader { cmsg_len: 0, cmsg_level: 0, cmsg_type: 0, fd: 0, }; let mut mhdr = msghdr { msg_name: std::ptr::null_mut(), msg_namelen: 0, msg_iov: &mut iov as *mut iovec, msg_iovlen: 1, msg_control: &mut scm as *mut ScmCmsgHeader as *mut c_void, msg_controllen: std::mem::size_of::<ScmCmsgHeader>() as u32, msg_flags: 0, }; let n = unsafe { recvmsg(self.fd, &mut mhdr, 0) }; if n == -1 || scm.cmsg_len as usize != std::mem::size_of::<ScmCmsgHeader>() || scm.cmsg_level != SOL_SOCKET || scm.cmsg_type != SCM_RIGHTS { Err("Invalid SCM message".to_string()) } else { Ok(scm.fd) } } } Conclusion

SCM_RIGHTS is a very powerful tool that can be used for many purposes. In our case we used to to introduce a new service in a non-obtrusive fashion. Other uses may be:

  • A/B testing
  • Phasing out of an old C based service in favor of new Go or Rust one
  • Passing connections from a privileged process to an unprivileged one

And more

You can find the full example here.

Categories: Technology

L4Drop: XDP DDoS Mitigations

Wed, 28/11/2018 - 19:59
 XDP DDoS Mitigations

Efficient packet dropping is a key part of Cloudflare’s distributed denial of service (DDoS) attack mitigations. In this post, we introduce a new tool in our packet dropping arsenal: L4Drop.

 XDP DDoS MitigationsPublic domain image by US Air Force

We've written about our DDoS mitigation pipeline extensively in the past, covering:

  • Gatebot: analyzes traffic hitting our edge and deploys DDoS mitigations matching suspect traffic.
  • bpftools: generates Berkeley Packet Filter (BPF) bytecode that matches packets based on DNS queries, p0F signatures, or tcpdump filters.
  • Iptables: matches traffic against the BPF generated by bpftools using the xt_bpf module, and drops it.
  • Floodgate: offloads work from iptables during big attacks that could otherwise overwhelm the kernel networking stack. Incoming traffic bypasses the kernel to go directly to a BPF interpreter in userspace, which efficiently drops packets matching the BPF rules produced by bpftools.

Both iptables and Floodgate send samples of received traffic to Gatebot for analysis, and filter incoming packets using rules generated by bpftools. This ends up looking something like this:

 XDP DDoS MitigationsFloodgate based DDoS mitigation pipeline

This pipeline has served us well, but a lot has changed since we implemented Floodgate. Our new Gen9 and ARM servers use different network interface cards (NIC) than our earlier servers. These new NICs aren’t compatible with Floodgate as it relies on a proprietary Solarflare technology to redirect traffic directly to userspace. Floodgate’s time was finally up.

XDP to the rescue

A new alternative to the kernel bypass approach has been added to Linux: eXpress Data Path (XDP). XDP uses an extended version of the classic BPF instruction set, eBPF, to allow arbitrary code to run for each packet received by a network card driver. As Marek demonstrated, this enables high speed packet dropping! eBPF introduces a slew of new features, including:

  • Maps, key-value data structures shared between the eBPF programs and userspace.
  • A Clang eBPF backend, allowing a sizeable subset of C to be compiled to eBPF.
  • A stringent kernel verifier that statically analyzes eBPF programs, ensuring run time performance and safety.

Compared to our partial kernel bypass, XDP does not require busy polling for packets. This enables us to leave an XDP based solution “always on” instead of enabling it only when attack traffic exceeds a set threshold. XDP programs can also run on multiple CPUs, potentially allowing a higher number of packets to be processed than Floodgate, which was pinned to a single CPU to limit the impact of busy polling.

Updating our pipeline diagram with XDP yields:

 XDP DDoS MitigationsXDP based DDoS mitigation pipeline

Introducing L4Drop

All that remains is to convert our existing rules to eBPF! At first glance, it seems we should be able to store our rules in an eBPF map and have a single program that checks incoming packets against them. Facebook’s firewall implements this strategy. This allows rules to be easily inserted or removed from userspace.

However, the filters created by bpftools rely heavily on matching arbitrary packet data, and performing arbitrary comparisons across headers. For example, a single p0f signature can check both IP & TCP options. On top of this, the thorough static analysis performed by the kernel’s eBPF verifier currently disallows loops. This restriction helps ensure that a given eBPF program will always terminate in a set number of instructions. Coupled together, the arbitrary matching and lack of loops prevent us from storing our rules in maps.

Instead, we wrote a tool to compile the rules generated by Gatebot and bpftools to eBPF. This allows the generated eBPF to match against any packet data it needs, at the cost of:

  • Having to recompile the program to add or remove rules
  • Possibly hitting eBPF code complexity limits enforced by the kernel with many rules

A C program is generated from the rules built by Gatebot, and compiled to eBPF using Clang. All that’s left is to reimplement the iptables features we use.

BPF support

We have many different tools for generating BPF filters, and we need to be able to include these filters in the eBPF generated by L4Drop. While the name eBPF might suggest a minor extension to BPF, the instruction sets are not compatible. In fact, BPF instructions don't even have a one-to-one mapping to eBPF! This can be seen in the kernel's internal BPF to eBPF converter, where a single BPF IP header length instruction maps to 6 eBPF instructions.

To simplify the conversion, we implemented a BPF to C compiler. This allows us to include any BPF program in the aforementioned C program generated by L4Drop. For example, if we generate a BPF program matching a DNS query to any subdomain of example.com using bpftools, we get:

$ ./bpfgen dns -- "*.example.com" 18,177 0 0 0,0 0 0 20,12 0 0 0,...

Converted to C, we end up with:

bool cbpf_0_0(uint8_t *data, uint8_t *data_end) { __attribute__((unused)) uint32_t a, x, m[16]; if (data + 1 > data_end) return false; x = 4*(*(data + 0) & 0xf); ... }

The BPF instructions each expand to a single C statement, and the BPF registers (a, x and m) are emulated as variables. This has the added benefit of allowing Clang to optimize the full program. The generated C includes the minimum number of guards required to prevent out of bounds packet accesses, as required by the kernel.

Packet sampling

Gatebot requires all traffic received by a server to be sampled at a given rate, and sent off for analysis. This includes dropped packets. Consequently, we have to sample before we drop anything. Thankfully, eBPF can call into the kernel using a restricted set of helper functions, and one of these, bpf_xdp_event_output, allows us to copy packets to a perf event ring buffer. A userspace daemon then reads from the perf buffer, obtaining the packets. Coupled with another helper, bpf_get_prandom_u32(), to generate random numbers, the C code to sample packets ends up something like:

// Threshold may be > UINT32_MAX uint64_t rnd = (uint64_t)get_prandom_u32(); if (rnd < threshold) { // perf_event_output passes the number of bytes as a flags in the // high 32 bits of the flags parameter. uint64_t flags = len << 32; // Use the current CPU number as index to sampled_packets. flags |= BPF_F_CURRENT_CPU; // Write the packet in ctx to the perf buffer if (xdp_event_output(ctx, &sampled_packets, flags, &len, sizeof(len))) { return XDP_ABORTED; } }

The newtools/ebpf library we use to load eBPF programs into the kernel has support for creating the required supporting eBPF map (sampled_packets in this example), and reading from the perf buffer in userspace.

Geo rules

With our large anycast network, a truly distributed denial of service attack will impact many of our data centers. But not all attacks have this property. We sometimes require location specific rules.

To avoid having to build separate eBPF programs for every location, we want the ability to enable or disable rules before loading a program, but after compiling it.

One approach would be to store whether a rule is enabled or not in an eBPF map. Unfortunately, such map lookups can increase the code size. Due to the kernel’s strict code complexity limits for XDP code, this reduces the number of rules that fit in a single program. Instead, we modify the generated eBPF ELF before loading it into the kernel.

If, in the original C program, every rule is guarded by a conditional like so:

int xdp_test(struct xdp_md *ctx) { unsigned long long enabled; asm("%0 = 0 ll" : "=r"(enabled)); if (enabled) { // Check the packet against the rule return XDP_DROP; } else { return XDP_PASS; } }

asm("%0 = 0 ll" : "=r"(enabled)) will emit a single 64bit eBPF load instruction, loading 0 into the register holding the variable enabled:

$ llvm-objdump-6.0 -S test.o test.o: file format ELF64-BPF Disassembly of section prog: xdp_test: 0: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll 2: b7 00 00 00 02 00 00 00 r0 = 2 3: 15 01 01 00 00 00 00 00 if r1 == 0 goto +1 <LBB0_2> 4: b7 00 00 00 01 00 00 00 r0 = 1 LBB0_2: 5: 95 00 00 00 00 00 00 00 exit

Modifying the ELF to change the load instruction to load 0 or 1 will change the value of enabled, enabling or disabling the rule. The kernel even trims conditionals against constants like these.

Modifying the instructions requires the ability to differentiate these special loads against ones normally emitted by Clang. Changing the asm to load a symbol (asm("%0 = RULE_0_ENABLED ll" : "=r"(enabled))) ensures it shows up in the ELF relocation info with that symbol name:

$ llvm-readelf-6.0 -r test.o Relocation section '.relprog' at offset 0xf0 contains 1 entries: Offset Info Type Symbol's Value Symbol's Name 0000000000000000 0000000200000001 R_BPF_64_64 0000000000000000 RULE_0_ENABLED

This enables the newtools/ebpf loader to parse the ELF relocation info, and always find the correct load instruction that guards enabling or disabling a rule.

Production

L4Drop is running in production across all of our servers, and protecting us against DDoS attacks. For example, this server dropped over 8 million packets per second:

 XDP DDoS Mitigationsreceived, dropped and lost packets per second vs CPU usage on a server during a DDoS attack

The graph shows a sudden increase in received packets (red). Initially, the attack overwhelmed the kernel network stack, causing some packets to be lost on the network card (yellow). The overall CPU usage (magenta) rose sharply.

Gatebot detected the attack shortly thereafter, deploying a rule matching the attack traffic. L4Drop began dropping the relevant packets (green), reaching over 8 million dropped packets per second.

The amount of traffic dropped (green) closely followed the received traffic (red), and the amount of traffic passed through remains unchanged before, during, and after the attack. This highlights the effectiveness of the deployed rule. At one point the attack traffic changed slightly, leading to a gap between the dropped and received traffic until Gatebot could respond with a new rule.

During the brunt of the attack, the overall CPU usage (magenta) only rose by about 10%, demonstrating the efficiency of XDP.

The softirq CPU usage (blue) shows the CPU usage under which XDP / L4drop runs, but also includes other network related processing. It increased by slightly over a factor of 2, while the number of incoming packets per second increased by over a factor of 40!

Conclusion

While we’re happy with the performance of L4Drop so far, our pipeline is in a constant state of improvement. We’re working on supporting a greater number of simultaneous rules in L4Drop through multiple, chained, eBPF programs. Another point of focus is increasing the efficiency of our generated programs, and supporting new eBPF features. Reducing our attack detection delay would also allow us to deploy rules quicker, leading to less lost packets at the onset of an attack.

Categories: Technology

Working out Backend Knots and Building Routers (Austin Real World Serverless Video Recap)

Sun, 25/11/2018 - 15:00
Working out Backend Knots and Building Routers (Austin Real World Serverless Video Recap)

I work in Developer Relations at Cloudflare and I'm fortunate to have top-notch developers around me who are willing to share their knowledge with the greater developer community. I produced a series of events this autumn called Real World Serverless at multiple locations around the world and I want to share the recorded videos from these events.

Our Austin Real World Serverless event (in partnership with the ATX Serverless User Group Meetup) included two talks about Serverless technology featuring Victoria Bernard and Preston Pham from Cloudflare. They spoke about working out backend knots with Workers and building a router for the great good.

About the talks

Working out Backend Knots with Workers - Victoria Bernard (0:00-15:19)

Cloudflare Workers is a platform the makes serverless development and deployment easier than ever. A worker is a script running between your clients' browsers and your site's origin that can intercept requests. Victoria went over some popular use cases of how proxy workers can dramatically improve a site's performance and add functionality that would normally require toying with complicated back-end services.

Build a Router for Great Good - Preston Pham (15:20-33:53)

Serverless computing is great, but requires routing or some kind of API gateway. Preston built a simple JavaScript routing library for serverless Worker scripts and explained how you can do this too. He built a router from the ground up, similar to other web application frameworks like flask or express.js.

Check out our Workers recipes we have listed on our docs here »

Stay tuned for more serverless technology video content.

Categories: Technology

My Second Cloudflare Company Retreat

Sat, 24/11/2018 - 17:30
My Second Cloudflare Company Retreat

Last week, 760 humans from Singapore, London, Beijing, Sydney, Nairobi, Austin, New York, Miami, Washington DC, Warsaw, Munich, Brussels, and Champaign reunited with their San Francisco counterparts for our 9th annual Cloudflare company retreat in the San Francisco Bay Area. The purpose of the company retreat is to bring all global employees together under one roof to bond, build bridges, have fun, and learn – all in support of Cloudflare’s mission to help build a better Internet.

It’s easy to write off corporate retreats as an obligatory series of meetings and tired speeches, but Cloudflare’s retreats are uniquely engaging, personalized, fun, and inspiring. Having grown with Cloudflare over the last year (I started just before our 2017 retreat), I wanted to share some of my experiences to highlight Cloudflare’s incredible culture.

The office was buzzing with different languages and laughter as people hugged and shook hands for the first time after working online together for a year or more. Everyone’s Google calendar looked like a rainbow as we each mined for white space to squeeze in those coveted 1:1s, all-hands presentations, and bowling offsites with our global colleagues.  The buses and Google chats felt like summer camp, with people claiming pride around their table numbers and sharing group photos.

My Second Cloudflare Company RetreatCloudflare team in NapaCompany culture and achievements

The first big theme of the retreat was reflecting on the positive company culture and achievements over the last year. Zack, who works on product strategy, and I chatted at a breakfast table about how good it feels to be surrounded by hundreds of colleagues we genuinely respect, admire, and enjoy working with. We talked about how proud we are that Cloudflare builds products that authentically make the Internet a better place. We'd seen several Internet users donate tens of thousands of dollars to Girls Who Code to be first in line for our recent Registrar product launch.

My Second Cloudflare Company RetreatMembers of Cloudflare's Global Afroflare group

Throughout retreat, the product and engineering teams were busy launching the 1.1.1.1 app and Spectrum for UDP product. The customer support and SRE teams took selfless night shifts to cover 24/7/365 support lines from one single time zone, while other teams delivered donuts to perk them up. Team members across sales and marketing sparked conversations about better ways to share best practices, experiments, and wins across regional offices.

My Second Cloudflare Company RetreatMembers of Cloudflare’s Global BDR team bowling outing

It was eye-opening to connect with different team members about why they joined Cloudflare in the first place. Greg, our sales leader in the central region, talked about the family considerations that played into his decision to join the Cloudflare team. Given that he lives in Chicago and has worked at larger companies prior to his transition, taking a remote sales leadership position at a company with less brand awareness in the Central US region certainly offered up some risk but one that he and his family felt was worth taking. Having been here now for several months, he remarked on the infectiously warm culture, impressive product innovation, and his confidence that the decision was the right one.

My Second Cloudflare Company RetreatMembers of Cloudflare's Global Proudflare groupEmployee Development

The second big theme of the retreat was seeing that Cloudflare fosters an environment for employee development. Every employee had a chance to select and participate in break-out sessions including financial literacy, mindfulness, whiteboarding, speed reading, mentoring, lock picking, and how to be a better writer. I chose “Improv: Think Quickly on your Feet,” (a soul-hugging, hilarious session which still hasn’t left my mind), and the Harvard Business School case study called “Dealing with Hot Issues Without Getting Burned” by Professor Mike Wheeler and award-winning journalist and executive media coach, Jeff Ansell.

My Second Cloudflare Company RetreatEmma, Alexander, Shannon, Mustafa, and two Bats Improvisational Comedy Coaches pretending to be chameleons after the Improv session

The HBS pre-reading was a case study on two companies implicated in the death of several consumers after using their products. As I walked in, Matthew Prince, our CEO, said, “This is a really special session.” He reflected on how taking this particular course during his time at HBS better prepared him for challenging media interviews about Internet privacy and due process of law. I don’t want to give away the secret sauce of this session, but I will recommend everyone take it if you ever have the chance. Having learned from an HBS professor, I felt that Cloudflare’s retreat was like a mini-MBA in a day.

Nicole from our Office Operations team selected the session called “Visual Communication through Whiteboarding.” She reflected on how it can be hard to convey new ideas through words, and that the whole point of the class was to explain complicated concepts through simple drawings. The instructor taught them how to storyboard and visually communicate business ideas like an app or invention. Nicole said she partnered with a colleague she’d never met before, and “we pretty much instantly understood what the other was trying to convey through drawings.”

My Second Cloudflare Company RetreatIcons created during Nicole's Whiteboarding session

Mickie from our Austin engineering team took the session called “Boost Your Productivity through Speed Reading and Memorization.” Reflecting on the course, she said, “I have a keen ability to immediately forget names right after I’m introduced, and with 760 new faces and names at Retreat, I felt like I needed a masters course in information retention. So I was particularly excited to attend [this session]. We started by evaluating our reading speed and measuring it again after learning tricks to improve reading speed and comprehension (you can’t improve what you don’t measure after all!). We also learned mnemonics to remember information, and by the end of the session, I confidently remembered the names of an entire group of co-workers, making it easier to stay in touch in the future.”

Aliza, who heads up our APAC region, attended the Harvard Business School case study session called “Mentoring and Talent Development.” She shared, “The mentoring session was not at all what I expected, which was half the fun! We read a case study about an employee who was a fantastic salesperson and built up a strong division for Morgan Stanley in an industry where the bank had been failing, but who violated many cultural norms and annoyed most of his colleagues. We were each tasked with determining whether to promote or fire the employee (no half-measures allowed).

According to the professor, usually the room is split, but our group was around 60% fire, 40% promote. We spent most of the time in a healthy debate, with various individuals sharing their perspectives and the professor giving us additional data and asking provocative questions along the way. It was a really engaging session which got each of us thinking about what the bank should value most and how that might apply to our own situations. One of the key lessons was that things would never have come to such a difficult point if the protagonist's boss had coached and mentored him properly along the way (another takeaway for each of us). As with all sessions, it was great to meet other people from Cloudflare with whom I'd never interacted.”

Having fun

The third big theme of retreat was having fun. We work hard every day, and Cloudflare does a great job of creating an environment for employees to unwind and have fun. Introverts and extroverts alike found ourselves bonding with people from all corners of the globe given the cross-functional seating arrangements and wide array of activities. I met folks from the Platform Engineering team in Austin, Technology team in Poland, Policy team in Cyprus, and beyond. I kayaked for the second year in a row with a friend from my onboarding class. We found our kayaks stuck in a marsh at one point, and I was impressed by his navigation skills as we chatted about his exciting work transition from London to Singapore. I had dinner with one of our engineers in Nairobi who previously attended a coding bootcamp and now works on the main Cloudflare website; we bonded over our love for ugali and nsima. I also learned about the extensive world of art collection from one of our legal team leaders, who reminded me how important it is to spend money on art made by marginalized folks who often receive less funding for their work. I love that Cloudflare’s retreat is a time for depth, not surface-level conversations.

My Second Cloudflare Company RetreatGroup of Cloudflarians hiking through Napa

It was also inspiring to see how engaged the executive team was with employees across the company. I got into a conversation with our CTO, John, at one point, and asked how he and our CEO, Matthew, originally met. John mentioned they were both speakers at a conference, and they each individually sought the other out as someone interesting to follow up with. Years later, it was clear throughout the retreat how much mutual respect the entire executive team shares. As an individual contributor, it’s important to see that our executive team genuinely collaborates, inspires, respects, and has fun together. This type of energy permeates across the organization, causing a positive ripple effect on the overall culture.

Until next year

On the final day of retreat, as we bussed across the Bay Bridge into San Francisco, buzzing with happy conversations, we passed one of Cloudflare’s billboards from our first offline advertising campaign. It was heartwarming to see our cofounders, Matthew and Michelle, smile and laugh at the sight.

My Second Cloudflare Company RetreatMichelle, Matthew, and Michael passing Cloudflare's billboard on the bus ride home

The annual retreat really gives Cloudflare a strong foundation, which employees build upon throughout the year to help make the Internet a safer, faster, more magical place. I would be silly not to mention that we’re hiring. Visit our careers page and come join us so you can attend next year’s retreat!

Categories: Technology

Every 7.8μs your computer’s memory has a hiccup

Fri, 23/11/2018 - 15:49
Every 7.8μs your computer’s memory has a hiccup

Every 7.8μs your computer’s memory has a hiccup
Modern DDR3 SDRAM. Source: BY-SA/4.0 by Kjerish

During my recent visit to the Computer History Museum in Mountain View, I found myself staring at some ancient magnetic core memory.

Every 7.8μs your computer’s memory has a hiccup
Source: BY-SA/3.0 by Konstantin Lanzet

I promptly concluded I had absolutely no idea on how these things could ever work. I wondered if the rings rotate (they don't), and why each ring has three wires woven through it (and I still don’t understand exactly how these work). More importantly, I realized I have very little understanding on how the modern computer memory - dynamic RAM - works!

Every 7.8μs your computer’s memory has a hiccup
Source: Ulrich Drepper's series about memory

I was particularly interested in one of the consequences of how dynamic RAM works. You see, each bit of data is stored by the charge (or lack of it) on a tiny capacitor within the RAM chip. But these capacitors gradually lose their charge over time. To avoid losing the stored data, they must regularly get refreshed to restore the charge (if present) to its original level. This refresh process involves reading the value of every bit and then writing it back. During this "refresh" time, the memory is busy and it can't perform normal operations like loading or storing bits.

This has bothered me for quite some time and I wondered... is it possible to notice the refresh delay in software?

Dynamic RAM refresh primer

Each DIMM module is composed of "cells" and "rows", "columns", "sides" and/or "ranks". This presentation from the University of Utah explains the nomenclature. You can check the configuration of memory in your computer with decode-dimms command. Here's an example:

$ decode-dimms Size 4096 MB Banks x Rows x Columns x Bits 8 x 15 x 10 x 64 Ranks 2

For today we don't need to get into the whole DDR DIMM layout, we just need to understand a single memory cell, storing one bit of information. Specifically we are only interested in how the refresh process is performed.

Let's review two sources:

Each bit stored in dynamic memory must be refreshed, typically every 64ms (called Static Refresh). This is a rather costly operation. To avoid one major stall every 64ms, this process is divided into 8192 smaller refresh operations. In each operation, the computer’s memory controller sends refresh commands to the DRAM chips. After receiving the instruction a chip will refresh 1/8192 of its cells. Doing the math - 64ms / 8192 = 7812.5 ns or 7.81 μs. This means:

  • A refresh command is issued every 7812.5 ns. This is called tREFI.
  • It takes some time for the chip to perform the refresh and recover so it can perform normal read and write operations again. This time, called tRFC is either 75ns or 120ns (as per the mentioned Micron datasheet).

When the memory is hot (>85C) the memory retention time drops and the static refresh time halves to 32ms, and tREFI falls to 3906.25 ns.

A typical memory chip is busy with refreshes for a significant fraction of its running time - between 0.4% to 5%. Furthermore, memory chips are responsible for a nontrivial share of typical computer's power draw, and large chunk of that power is spent on performing the refreshes.

For the duration of the refresh action, the whole memory chip is blocked. This means each and every bit in memory is blocked for more than 75ns every 7812ns. Let's measure this.

Preparing an experiment

To measure operations with nanosecond granularity we must write a tight loop, perhaps in C. It looks like this:

for (i = 0; i < ...; i++) { // Perform memory load. Any load instruction will do *(volatile int *) one_global_var; // Flush CPU cache. This is relatively slow _mm_clflush(one_global_var); // mfence is needed, otherwise sometimes the loop // takes very short time (25ns instead of like 160). I // blame reordering. asm volatile("mfence"); // Measure and record time clock_gettime(CLOCK_MONOTONIC, &ts); }

Full code is available on Github.

The code is really straightforward. Perform a memory read. Flush data from CPU caches. Measure time.

(Note: in second experiment I attempted to use MOVNTDQA to perform the data load, but this requires special uncacheable memory page, which needs root access.)

On my computer it generates data like this:

# timestamp, loop duration 3101895733, 134 3101895865, 132 3101896002, 137 3101896134, 132 3101896268, 134 3101896403, 135 3101896762, 359 3101896901, 139 3101897038, 137

Typically I get ~140ns per loop, periodically the loop duration jumps to ~360ns. Sometimes I get odd readings longer than 3200ns.

Unfortunately, the data turns out to be very noisy. It's very hard to see if there is a noticeable delay related to the refresh cycles.

Fast Fourier Transform

At some point it clicked. Since we want to find a fixed-interval event, we can feed the data into the FFT (fast fourier transform) algorithm, which deciphers the underlying frequencies.

I'm not the first one to think about this - Mark Seaborn of Rowhammer fame implemented this very technique back in 2015. Even after peeking at Mark's code, getting FFT to work turned out to be harder than I anticipated. But finally I got all the pieces together.

First we need to prepare the data. FFT requires input data to be sampled with a constant sampling interval. We also want to crop the data to reduce noise. By trial and error I found the best results are when data is preprocessed:

  • Small (smaller than average * 1.8) values of loop iterations are cut out, ignored, and replaced with readings of "0". We really don't want to feed the noise into the algorithm.
  • All the remaining readings are replaced with "1", since we really don't care about the amplitude of the delay caused by some noise.
  • I settled on sampling interval of 100ns, but any number up to a Nyquist value (double expected frequency) also work fine.
  • The data needs to be sampled with fixed timings before feeding to FFT. All reasonable sampling methods work ok, I ended up doing basic linear interpolation.

The algorithm is roughly:

UNIT=100ns A = [(timestamp, loop_duration),...] p = 1 for curr_ts in frange(fist_ts, last_ts, UNIT): while not(A[p-1].timestamp <= curr_ts < A[p].timestamp): p += 1 v1 = 1 if avg*1.8 <= A[p-1].duration <= avg*4 else 0 v2 = 1 if avg*1.8 <= A[p].duration <= avg*4 else 0 v = estimate_linear(v1, v2, A[p-1].timestamp, curr_ts, A[p].timestamp) B.append( v )

Which on my data produces fairly boring vector like this:

[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]

The vector is pretty long though, typically about ~200k data points. With data prepared like this, we are ready to feed it into FFT!

C = numpy.fft.fft(B) C = numpy.abs(C) F = numpy.fft.fftfreq(len(B)) * (1000000000/UNIT)

Pretty simple, right? This produces two vectors:

  • C contains complex numbers of the frequency components. We are not interested in complex numbers and we can flatten them out by calling abs().
  • F contains labels to what frequency bin lies in which place in vector C. We need to normalize it to Hz - by multiplying it by the input vector sampling frequency.

The result can be charted:

Every 7.8μs your computer’s memory has a hiccup

Y axis is unit-less since we normalized the delay times. Even though, it clearly shows spikes at some fixed frequency intervals. Let's zoom in:

Every 7.8μs your computer’s memory has a hiccup

We can clearly see first three spikes. After a bit of wishy-washy arithmetic, involving filtering the reading at least 10 times the size of average, we can extract the underlying frequencies:

127850.0 127900.0 127950.0 255700.0 255750.0 255800.0 255850.0 255900.0 255950.0 383600.0 383650.0

Doing the math: 1000000000 (ns/s) / 127900 (Hz) = 7818.6 ns

Hurray! The first frequency spike is indeed what we were looking for, and indeed does correlate with the refresh times.

The other spikes at 256kHz, 384kHz, 512kHz and so on, are multiplies of our base frequency of 128kHz called harmonics. These are a side effect of performing FFT on something like a square wave and totally expected.

For easier experimentation, we prepared a command line version of this tool. You can run the code yourself. Here is a sample run on my server:

~/2018-11-memory-refresh$ make gcc -msse4.1 -ggdb -O3 -Wall -Wextra measure-dram.c -o measure-dram ./measure-dram | python3 ./analyze-dram.py [*] Verifying ASLR: main=0x555555554890 stack=0x7fffffefe2ec [ ] Fun fact. I did 40663553 clock_gettime()'s per second [*] Measuring MOVQ + CLFLUSH time. Running 131072 iterations. [*] Writing out data [*] Input data: min=117 avg=176 med=167 max=8172 items=131072 [*] Cutoff range 212-inf [ ] 127849 items below cutoff, 0 items above cutoff, 3223 items non-zero [*] Running FFT [*] Top frequency above 2kHz below 250kHz has magnitude of 7716 [+] Top frequency spikes above 2kHZ are at: 127906Hz 7716 255813Hz 7947 383720Hz 7460 511626Hz 7141

I must admit the code is not perfectly stable. In case of trouble, consider disabling Turbo Boost, CPU frequency scaling and tuning for performance.

Finale

There are two major takeaways from this work.

We've seen that the low level data is pretty hard to analyze and seem to be pretty noisy. Instead of trying to figure something out with naked eye, we can always employ good old FFT. Some wishful thinking is needed when preparing the data.

Most importantly, we showed it's often possible to measure subtle hardware behaviours from a simple userspace process. This kind of thinking led to the discovery of the original Rowhammer vulnerability, was used in Meltdown/Spectre attacks and showed again in recent ECC-defeating reincarnation of Rowhammer.

There is so much more to be said. We barely scratched the surface of the inner workings of the memory subsystem. I recommend further reading:

Finally, here is a good reading about the old magnetic core memory:

Categories: Technology

Serverless Progressive Web Apps using React with Cloudflare Workers

Fri, 23/11/2018 - 13:29

Let me tell you the story of how I learned that you can build Progressive Web Apps on Cloudflare’s network around the globe with one JavaScript bundle that runs both in the browser and on Cloudflare Workers with no modification and no separate bundling for client and server. And when registered as a Service Worker, the same JavaScript bundle will turn your page into a Progressive Web App that doesn’t even make network requests. Here's how that works...

"Any resemblance to actual startups, living or IPO'd, is purely coincidental and unintended" - @sevkiA (possibly apocryphal) Story

I recently met up with some old friends in London who told me they were starting a new business. They did what every coder would do... they quickly hacked something together, bought a domain, and registered the GitHub org and thus Buzzwords was born.

The idea was simple: you could feed the name of your application into a machine learning model and it would generate the configuration files for your deployment for various container orchestrators. They achieved this by going through millions of deployment configurations and training a linear regression model by gamifying quantum computing because blockchain, or something (I told you this story was apocryphal).

I was intrigued, to say the least, but I was playing it cool. One of the co-founders broke the silence, "So, does any of these sound like something you'd like to work on?" I struggled with it for tens of seconds. You see I only recently started this new job at Cloudflare, and I actually like my job and the people I work with. So I said, "Hell yeah, man, let's change the world one container at a time". One of them said, "Well, since we really can’t pay you right now, and we don't seem to be able to set aside enough time to build our website, would you mind helping us out with that on a trial basis, like an interview?" I enthusiastically accepted.

So while the Buzzwords crew were busy producing HYPE, I set off to build their website. As any business starting up, discover-ability is paramount to them, so Buzzwords definitely needs to be optimized for search engines so they can generate organic traffic from keywords like "machine learning", "YAML", "containers" and "blockchain".

When parsing dynamic pages, crawlers need to do more work, there is an inherent penalty for using fancy frameworks compared to plain old HTML.

Don’t take my word for it, as Google cautions;

Currently, it's difficult to process JavaScript and not all search engine crawlers are able to process it successfully or immediately. In the future, we hope that this problem can be fixed, but in the meantime, we recommend dynamic rendering as a workaround solution to this problem.

Yet, the Buzzwords folks still wanted something fancy I told them about this new thing called React hooks. I slammed a La Croix and put together the 2018 equivalent of Hello, World! which is still Hello, World! but with an

Categories: Technology

Fast Google Fonts with Cloudflare Workers

Thu, 22/11/2018 - 07:58
Fast Google Fonts with Cloudflare Workers

Google Fonts is one of the most common third-party resources on the web, but carries with it significant user-facing performance issues. Cloudflare Workers running at the edge is a great solution for fixing these performance issues, without having to modify the publishing system for every site using Google Fonts.

This post walks through the implementation details for how to fix the performance of Google Fonts with Cloudflare Workers. More importantly, it also provides code for doing high-performance content modification at the edge using Cloudflare Workers.

Google fonts are SLOW

First, some background. Google Fonts provides a rich selection of royalty-free fonts for sites to use. You select the fonts you want to use, and end up with a simple stylesheet URL to include on your pages, as well as styles to use for applying the fonts to parts of the page:

<link href="https://fonts.googleapis.com/css?family=Open+Sans|Roboto+Slab" rel="stylesheet"> <style> body { font-family: 'Open Sans', sans-serif; } h1 { font-family: 'Roboto Slab', serif; }

Your visitor’s browser fetches the CSS file as soon as the HTML for the page is available. The browser will request the underlying font files when the browser does layout for the page and discovers that it needs fonts for different sections of text.

The way Google fonts are served, the CSS is on one domain (fonts.googleapis.com) and the font files are on a different domain (fonts.gstatic.com). Since they are on separate domains, the fetch for each resource takes a minimum of four round trips back and forth to the server: One each for the DNS lookup, establishing the socket connection, negotiating TLS encryption (for https) and a final round trip for the request itself.

Fast Google Fonts with Cloudflare Workers4 round trips each for the font css and font file.

The requests can’t be done in parallel because the fonts aren’t known about until after the CSS has been downloaded and the styles applied to the page. In a best-case scenario, this translates to eight round-trips before the text can be displayed (the text which was already available in the browser as soon as the HTML was available). On a slower 3G connection with a 300ms round-trip time, this adds up to a 2.4 second delay. Best case!

There is also a problem with resource prioritization. When you have all of the requests coming from the same domain on the same HTTP/2 connection they can be scheduled against each other. Critical resources (like CSS and fonts) can be pushed ahead in the queue and delivered before lower priority resources (like images). Since Google Fonts (and most third-party resources) are served from a different domain than the main page resources, they cannot be prioritized and end up competing with each other for download bandwidth. This can cause the actual fetch times to be much longer than the best-case of 8 round trips.

Fast Google Fonts with Cloudflare WorkersCSS and font download competing with low-priority images for bandwidth.

Users will see the images and skeleton for the page long before the actual text is displayed:

Fast Google Fonts with Cloudflare WorkersThe page starts painting at 3.3 seconds with partial images and no text.
Fast Google Fonts with Cloudflare WorkersThe text finally displays at 6.2 seconds into the page load.
Fixing Google Fonts performance

The paths to fixing performance issues and making fonts lightning-fast is different for the CSS and the font files themselves. We can reduce the total number of round trips to one:

  1. Inline the CSS directly in the HTML.
  2. Proxy the Google Font files through the page origin.
Fast Google Fonts with Cloudflare WorkersThe inline CSS loads immediately as part of the HTML and the fonts load before the images.
Optimizing CSS delivery

For CSS, the easy answer is to just download the CSS file that Google hosts and either serve it yourself directly or place it into the HTML as an inline stylesheet. The problem with this is that Google fonts serves a browser-specific CSS file that is different for each browser so it can serve newer formats and use newer features, when supported, while still providing custom font support for older browsers.

With Cloudflare Workers, we can dynamically replace the external CSS reference with the browser-specific stylesheet content at fetch time when the HTML is requested by the browser. This way, the inlined CSS will always be up to date and support the capabilities of whichever browser is making the request. This completely eliminates any round trips for fetching the CSS for the fonts.

Optimizing font file delivery

For the font files themselves, we can eliminate all of the round trips except for the fetch itself by serving the font files directly from the same domain as the HTML. This brings with it the added benefit of serving fonts over the same HTTP/2 connection as the rest of the page resources, allowing them to be prioritized correctly and not compete for bandwidth.

Specifically, when we inline the CSS into the HTML response, we rewrite all of the font URLs to use the same domain as the HTML. When those rewritten requests arrive at the worker the URL is transformed back to the original URL served by Google and fetched by the worker (as a proxy). The worker fetches are routed through Cloudflare’s caching infrastructure so they will be cached automatically at the edge. The actual URL rewriting is pretty simple but effective. We take font URLs that look like this:

https://fonts.gstatic.com/s/...

and we just prepend the page domain to the front of the URL:

https://www.example.com/fonts.gstatic.com/s/...

That way, when they arrive at the edge, the worker can look at the path of a request and know right away that it is a proxy request for a Google font. At this point, rewriting the original URL is trivial. On the extremely odd chance that a page on your site actually has a path that starts with /fonts.gstatic.com/, those resources would break and something else should be appended to the URL to make sure they are unique.

Optimization Results

In practice, the results can be quite dramatic. On this test page for example, the wait time for fonts to become available dropped from 5.5 seconds to 1 second (an 81% improvement!):

Fast Google Fonts with Cloudflare WorkersThe fonts load immediately after the HTML.

Visually, the improvement to the user experience is also quite dramatic. Instead of seeing a skeleton page of images followed by the text eventually appearing, the text is available (and correctly styled) immediately and the user can start consuming the content right away:

Fast Google Fonts with Cloudflare WorkersThe first paint happens much sooner at 2.5 seconds with all of the text displayed while the original page is still blank.
Fast Google Fonts with Cloudflare WorkersAt 3.3 seconds the original page finally starts to paint, displaying part of the images and no text.
Fast Google Fonts with Cloudflare WorkersAt 4.4 seconds the optimized page is visibly complete while the original page still has not displayed any text.
Fast Google Fonts with Cloudflare WorkersAt 6.2 seconds the original page finally displays the text content.

One thing that I didn’t notice in the initial testing and only discovered when looking at the side-by-side video is that the fonts are only correctly styled in the optimized case. In the original case it took longer than Chrome’s 3-second timeout for the fonts to load and it fell back to the system fonts. Not only was the experience much faster; it was also styled correctly with the custom fonts right from the beginning.

Optimizing Google fonts with a Cloudflare worker

The full Cloudflare worker code for implementing the font optimization is available on GitHub here. Buckle-in because this is quite a bit more involved than most of the samples in the documentation.

At a high level this code:

  • Filters all requests and determines if a request is for a proxied font or HTML (and passes all other requests through unmodified).
  • Rewrites the URL and passes the fetch request through for Font requests.
  • For HTML requests:
    • Passes the request through unmodified to the origin server.
    • Returns non-UTF-8 content unmodified..
    • Processes the HTML response in streaming chunks as it is available.
    • Replaces Google font stylesheet link tags with the CSS inline with the font URLs rewritten to proxy through the origin.

The code here is slightly simplified to make it clearer to understand the flow. The full code on GitHub adds support for an in-memory worker cache for the CSS (in addition to the persistent cache API) and provides query parameters for toggling the HTML rewriting on and off (for testing).

The content modification is all done by operating on the HTML as strings (with a combination of regular expressions and string matches). This is much faster and lighter weight than parsing the HTML into a virtual DOM, operating on it and converting back to HTML. It also allows for incremental processing of the HTML as a stream.

Entry Point

The addEventListener(“fetch”) call is the main entry point for any worker and houses the JavaScript for intercepting inbound requests. If the handler does nothing, then the requests will be passed through normally and the worker will be out of the path in processing the response. Our goal it to minimize the amount of work that the worker has to do to determine if it is a request that it is interested in.

In the case of the proxied font requests, we can just look at the request URL and see that the path starts with /fonts.gstatic.com/. To identify requests for HTML content we can look at the “accept” header on the request. Every major browser I have tested includes text/html on the list of content types that it will accept when requesting a document. On the off chance that there is a browser that doesn’t include it as an accept header, the HTML will just be passed through and returned unmodified. The goal with everything here is to fail-safe and just return unoptimized content for any edge cases that aren’t covered. This way nothing breaks; it just doesn’t get the added performance boost.

addEventListener("fetch", event => { const url = new URL(event.request.url); const accept = event.request.headers.get('accept'); if (event.request.method === 'GET' && url.pathname.startsWith('/fonts.gstatic.com/')) { // Proxy the font file requests event.respondWith(proxyRequest('https:/' + url.pathname, event.request)); } else if (accept && accept.indexOf("text/html") !== -1) { // Process the HTML event.respondWith(processHtmlRequest(event.request, event)); } })

Request Proxy

The proxying of the font requests is pretty straightforward. Since we are crossing origins it is generally a bad idea to just reuse the existing request object with a new URL. That can leak user data like cookies to a Third-party. Instead, we make a new request, clone a subset of the headers and pass the new fetch request back for the Worker runtime to handle.

The fetch path between workers and the outside Internet goes through the Cloudflare cache so the actual font files will only be fetched from Google if they aren’t already in the cache. Even in that case, the connection from Cloudflare’s edge to Google’s font servers is much faster (and more reliable) than the end-user’s connection from the browser. Even on a cache miss, it is an insignificant delay.

async function proxyRequest(url, request) { // Only pass through a subset of request headers let init = { method: request.method, headers: {} }; const proxyHeaders = ["Accept", "Accept-Encoding", "Accept-Language", "Referer", "User-Agent"]; for (let name of proxyHeaders) { let value = request.headers.get(name); if (value) { init.headers[name] = value; } } const clientAddr = request.headers.get('cf-connecting-ip'); if (clientAddr) { init.headers['X-Forwarded-For'] = clientAddr; } // Only include a strict subset of response headers const response = await fetch(url, init); if (response) { const responseHeaders = ["Content-Type", "Cache-Control", "Expires", "Accept-Ranges", "Date", "Last-Modified", "ETag"]; let responseInit = {status: response.status, statusText: response.statusText, headers: {}}; for (let name of responseHeaders) { let value = response.headers.get(name); if (value) { responseInit.headers[name] = value; } } const newResponse = new Response(response.body, responseInit); return newResponse; } return response; }

In addition to filtering the request headers we also filter the response headers sent back to the browser. If you’re not careful you could end up in a situation where a third-party is setting cookies on your origin or even turning on something like HTTP Strict Transport Security for your site.

Streaming HTML Processing

The HTML path is more complicated because we are going to intercept and modify the content itself.

In processing the HTML request, the first thing we want to do is make sure it is actually an HTML response. If it is something else, then we should get out of the way and let the response stream back to the browser as it does normally. It is very possible that a PDF document, file download, or even a directly opened image, has a Accept of text/html. It is critical to check the actual content that is being responded with to make sure it is something we want to inspect and possibly modify.

The easiest way to modify a response is to just wait for the response to be fully complete, process it as a single block of HTML, and then pass the modified HTML back to the browser:

async function processHtmlRequest(request) { // Fetch from origin server. const response = await fetch(request) if (response.headers.get("content-type").indexOf("text/html") !== -1) { let content = await response.text(); content = await modifyHtmlChunk(content, request); // Create a cloned response with our modified HTML return new Response(content, response); } return response; }

This works reasonably well if you are sure that all of the HTML is UTF-8 (or ascii), and the server returns all of the HTML at once, but there are some pretty serious concerns with doing it this way:

  • The memory use can be unbounded and only limited by the size of the largest HTML response (possibly causing the worker to be killed for using too much memory).
  • Significant delay will be added to any pages where the server flushes the initial content early and then does some expensive/slow work before returning the rest of the HTML.
  • This only works if the text content uses an encoding that JavaScript can decode directly as UTF-8. Any other character encodings will fail to decode.

For our worker we are going to process the HTML stream incrementally as it arrives from the server and pass it through to the browser as soon as possible (and pass-through any content that isn’t utf-8 unmodified).

Processing HTML as a stream

First we are going to look at what it takes to process the HTML stream incrementally. We will leave the character encoding changes out for now to keep things (relatively) simple.

To process the stream incrementally, we need to generate a new fetch response to pass back from the worker that uses a TransformStream for its content. That will allow us to pass the response itself back immediately and write to the stream as soon as we have content to add. We pass all of the other headers through unmodified.

async function processHtmlRequest(request) { // Fetch from origin server. const response = await fetch(request) if (response.headers.get("content-type").indexOf("text/html") !== -1) { // Create an identity TransformStream (a.k.a. a pipe). // The readable side will become our new response body. const { readable, writable } = new TransformStream(); // Create a cloned response with our modified stream const newResponse = new Response(readable, response); // Start the async processing of the response stream (NO await!) modifyHtmlStream(response.body, writable, request); // Return the in-process response so it can be streamed. return newResponse; } return response; }

The key thing here is to not wait for the async modifyHtmlStream async function to complete before passing the new response back from the worker. This way the initial headers can be sent immediately and the response will continue to stream anything written into the TransformStream until it is closed.

Processing the HTML stream in chunks as it arrives is a little tricky. The HTML stream will arrive in chunks of arbitrary sizes as strings. We need to add some protection to make sure that a chunk boundary doesn’t split a link tag. If it does, and we don’t account for it, we can miss a stylesheet link (or worse, process a partial link URL with the wrong style type). To make sure we don’t split link tags, we search from the end of the string for “<link “ and from the start of the last link tag we search forward for a closing “>” tag. If we don’t find one, then there is a partial link tag and we split the string just before the link tag starts. We process everything up to the split link tag and keep the partial tag to prepend it to the next chunk of data that arrives.

An alternative would be to keep accumulating data and only process it when there is no split link tag at the end, but this way we can return more data to the browser sooner.

When the incoming stream is complete, we process any partial data left over from the previous chunk and close the output stream (ending the response to the browser).

async function modifyHtmlStream(readable, writable, request) { const reader = readable.getReader(); const writer = writable.getWriter(); const encoder = new TextEncoder(); let decoder = new TextDecoder(); let partial = ''; let content = ''; for(;;) { // Read the next chunk of HTML from the fetch request. const { done, value } = await reader.read() if (done) { // Send any remaining fragment and complete the request. if (partial.length) { partial = await modifyHtmlChunk(partial, request); await writer.write(encoder.encode(partial)); partial = ''; } break; } try { let chunk = decoder.decode(value, {stream:true}); // Add the inbound chunk to the the remaining partial chunk // from the previous read. content = partial + chunk; partial = ''; // See if there is an unclosed link tag at the end (and if so, // carve it out to complete when the remainder comes in). const linkPos = content.lastIndexOf('<link'); if (linkPos >= 0) { const linkClose = content.indexOf('/>', linkPos); if (linkClose === -1) { partial = content.slice(linkPos); content = content.slice(0, linkPos); } } if (content.length) { // Do the actual HTML modifications on the current chunk. content = await modifyHtmlChunk(content, request); } } catch (e) { // Ignore the exception } // Send the processed HTML chunk to the requesting browser. if (content.length) { await writer.write(encoder.encode(content)); content = ''; } } await writer.close() }

One thing I was initially worried about was having to modify the “content-length” response header from the original response since we are modifying the content. Luckily, the worker takes care of that automatically and it isn’t something you have to implement.

There is a try/catch handler around the processing in case something goes horribly wrong with the decode.

The actual HTML rewriting is handled in “modifyHtmlChunk”. This is just the logic for processing the incoming data as incremental chunks.

Dealing with character encodings other than UTF-8

We intentionally skipped over handling character encodings other than UTF-8 up until now. To handle arbitrary pages you will need to be able to process other character encodings. The Worker runtime only supports decoding UTF-8 but we need to make sure that we don’t break any content that isn’t UTF-8 (or similar). To do this, we detect the current encoding if it is specified and anything that isn’t UTF-8 is passed through unmodified. In the case that the content type can not be detected we also detect decode errors and pass content through unmodified when they occur.

The HTML charset can be specified in the content-type response header or as a <meta charset> tag in the HTML itself.

For the response headers it is pretty simple. When we get the original response, see if there is a charset in the content-type header. If there is, extract the current value and if it isn’t a supported charset just pass the response through unmodified.

// Workers can only decode utf-8. If it is anything else, pass the // response through unmodified const VALID_CHARSETS = ['utf-8', 'utf8', 'iso-8859-1', 'us-ascii']; const charsetRegex = /charset\s*=\s*([^\s;]+)/mgi; const match = charsetRegex.exec(contentType); if (match !== null) { let charset = match[1].toLowerCase(); if (!VALID_CHARSETS.includes(charset)) { return response; } } // Create an identity TransformStream (a.k.a. a pipe). // The readable side will become our new response body. const { readable, writable } = new TransformStream(); // Create a cloned response with our modified stream const newResponse = new Response(readable, response); // Start the async processing of the response stream modifyHtmlStream(response.body, writable, request, event);

For the cases where there is a “” tag in the HTML (and possibly no header) things get a bit more complicated. If at any point an unsupported charset is detected then we pipe the incoming byte stream directly into the output stream unmodified. We first decode the first chunk of HTML response using the default decoder. Then, if a ” tag is found in the html we extract the charset. If it isn’t a supported charset then we enter passthrough mode. If at any point the input stream can’t be decoded (likely because of an invalid charset) we also enter passthrough mode and pipe the remaining content through unprocessed.

async function modifyHtmlStream(readable, writable, request, event) { const reader = readable.getReader(); const writer = writable.getWriter(); const encoder = new TextEncoder(); let decoder = new TextDecoder("utf-8", {fatal: true}); let firstChunk = true; let unsupportedCharset = false; let partial = ''; let content = ''; try { for(;;) { const { done, value } = await reader.read(); if (done) { if (partial.length) { partial = await modifyHtmlChunk(partial, request, event); await writer.write(encoder.encode(partial)); partial = ''; } break; } let chunk = null; if (unsupportedCharset) { // Pass the data straight through await writer.write(value); continue; } else { try { chunk = decoder.decode(value, {stream:true}); } catch (e) { // Decoding failed, switch to passthrough unsupportedCharset = true; if (partial.length) { await writer.write(encoder.encode(partial)); partial = ''; } await writer.write(value); continue; } } try { // Look inside of the first chunk for a HTML charset or // content-type meta tag. if (firstChunk) { firstChunk = false; if (chunkContainsInvalidCharset(chunk)) { // switch to passthrough unsupportedCharset = true; if (partial.length) { await writer.write(encoder.encode(partial)); partial = ''; } await writer.write(value); continue; } } content = partial + chunk; partial = ''; // See if there is an unclosed link tag at the end (and if so, // carve it out to complete when the remainder comes in). const linkPos = content.lastIndexOf('<link'); if (linkPos >= 0) { const linkClose = content.indexOf('/>', linkPos); if (linkClose === -1) { partial = content.slice(linkPos); content = content.slice(0, linkPos); } } if (content.length) { content = await modifyHtmlChunk(content, request, event); } } catch (e) { // Ignore the exception } if (content.length) { await writer.write(encoder.encode(content)); content = ''; } } } catch(e) { // Ignore the exception } try { await writer.close(); } catch(e) { // Ignore the exception } }

There is a helper that scans for the charset in both meta tags that support setting the charset:

function chunkContainsInvalidCharset(chunk) { let invalid = false; const VALID_CHARSETS = ['utf-8', 'utf8', 'iso-8859-1', 'us-ascii']; // meta charset const charsetRegex = /<\s*meta[^>]+charset\s*=\s*['"]([^'"]*)['"][^>]*>/mgi; const charsetMatch = charsetRegex.exec(chunk); if (charsetMatch) { const docCharset = charsetMatch[1].toLowerCase(); if (!VALID_CHARSETS.includes(docCharset)) { invalid = true; } } // content-type const contentTypeRegex = /<\s*meta[^>]+http-equiv\s*=\s*['"]\s*content-type[^>]*>/mgi; const contentTypeMatch = contentTypeRegex.exec(chunk); if (contentTypeMatch) { const metaTag = contentTypeMatch[0]; const metaRegex = /charset\s*=\s*([^\s"]*)/mgi; const metaMatch = metaRegex.exec(metaTag); if (metaMatch) { const charset = metaMatch[1].toLowerCase(); if (!VALID_CHARSETS.includes(charset)) { invalid = true; } } } return invalid; }

HTML Business Logic

Finally, we can start the actual logic for inlining the font CSS. The basic logic is:

  • Use a regex to find link tags for Google fonts css.
  • Fetch the browser-specific version of the CSS (from cache if possible).
    • The fetch logic (discussed later) modifies the font URLs in the CSS to proxy through the worker.
  • Replace the link tag with an inline style block with the CSS.

async function modifyHtmlChunk(content, request, event) { const fontCSSRegex = /<link\s+[^>]*href\s*=\s*['"]((https?:)?\/\/fonts.googleapis.com\/css[^'"]+)[^>]*>/mgi; let match = fontCSSRegex.exec(content); while (match !== null) { const matchString = match[0]; if (matchString.indexOf('stylesheet') >= 0) { const fontCSS = await fetchCSS(match[1], request, event); if (fontCSS.length) { // See if there is a media type on the link tag let mediaStr = ''; const mediaMatch = matchString.match(/media\s*=\s*['"][^'"]*['"]/mig); if (mediaMatch) { mediaStr = ' ' + mediaMatch[0]; } // Replace the actual css let cssString = "<style" + mediaStr + ">\n"; cssString += fontCSS; cssString += "\n</style>\n"; content = content.split(matchString).join(cssString); } match = fontCSSRegex.exec(content); } } return content; }

The fetching (and modifying) of the CSS is a little more complicated than a straight passthrough because we want to cache the result when possible. We cache the responses locally using the worker’s Cache API. Since the response is browser-specific, and we don’t want to fragment the cache too crazily, we create a custom cache key based on the browser user agent string that is basically browser+version+mobile.

Some plans have access to named cache storage, but to work with all plans it is easiest if we just modify the font URL that gets stored in cache and append the cache key to the end of the URL as a query parameter. The cache URL never gets sent to a server but is useful for local caching of different content that shares the same URL. For example:

https://fonts.googleapis.com/css?family=Roboto&chrome71

If the CSS isn’t available in the cache then we create a fetch request for the original URL from the Google servers, passing through the HTML url as the referer, the correct browser user agent string and the client’s IP address in a standard proxy X-Forwarded-For header. Once the response is available we store it in the cache for future requests.

For browsers that can’t be identified by user agent string a generic request for css is sent with the user agent string from Internet Explorer 8 to get the lowest common denominator fallback CSS.

The actual modification of the CSS just uses a regex to look for font URLs, replaces them with the HTML origin as a prefix.

async function fetchCSS(url, request) { let fontCSS = ""; const userAgent = request.headers.get('user-agent'); const clientAddr = request.headers.get('cf-connecting-ip'); const browser = getCacheKey(userAgent); const cacheKey = browser ? url + '&' + browser : url; const cacheKeyRequest = new Request(cacheKey); let cache = null; let foundInCache = false; // Try pulling it from the cache API (wrap it in case it's not implemented) try { cache = caches.default; let response = await cache.match(cacheKeyRequest); if (response) { fontCSS = response.text(); foundInCache = true; } } catch(e) { // Ignore the exception } if (!foundInCache) { let headers = {'Referer': request.url}; if (browser) { headers['User-Agent'] = userAgent; } else { headers['User-Agent'] = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)"; } if (clientAddr) { headers['X-Forwarded-For'] = clientAddr; } try { const response = await fetch(url, {headers: headers}); fontCSS = await response.text(); // Rewrite all of the font URLs to come through the worker fontCSS = fontCSS.replace(/(https?:)?\/\/fonts\.gstatic\.com\//mgi, '/fonts.gstatic.com/'); // Add the css info to the font caches FONT_CACHE[cacheKey] = fontCSS; try { if (cache) { const cacheResponse = new Response(fontCSS, {ttl: 86400}); event.waitUntil(cache.put(cacheKeyRequest, cacheResponse)); } } catch(e) { // Ignore the exception } } catch(e) { // Ignore the exception } } return fontCSS; }

Generating the browser-specific cache key is a little sensitive since browsers tend to clone each other’s user agent strings and add their own information to them. For example, Edge includes a Chrome identifier and Chrome includes a Safari identifier, etc. We don’t necessarily have to handle every browser string since it will fallback to the least common denominator (ttf files without unicode range support) but it is helpful to catch as many of the large mainstream browser engines as possible.

function getCacheKey(userAgent) { let os = ''; const osRegex = /^[^(]*\(\s*(\w+)/mgi; let match = osRegex.exec(userAgent); if (match) { os = match[1]; } let mobile = ''; if (userAgent.match(/Mobile/mgi)) { mobile = 'Mobile'; } // Detect Edge first since it includes Chrome and Safari const edgeRegex = /\s+Edge\/(\d+)/mgi; match = edgeRegex.exec(userAgent); if (match) { return 'Edge' + match[1] + os + mobile; } // Detect Chrome next (and browsers using the Chrome UA/engine) const chromeRegex = /\s+Chrome\/(\d+)/mgi; match = chromeRegex.exec(userAgent); if (match) { return 'Chrome' + match[1] + os + mobile; } // Detect Safari and Webview next const webkitRegex = /\s+AppleWebKit\/(\d+)/mgi; match = webkitRegex.exec(userAgent.match); if (match) { return 'WebKit' + match[1] + os + mobile; } // Detect Firefox const firefoxRegex = /\s+Firefox\/(\d+)/mgi; match = firefoxRegex.exec(userAgent); if (match) { return 'Firefox' + match[1] + os + mobile; } return null; }

Profit!

Any site served through Cloudflare can implement workers to rewrite their content but for something like Google fonts or other third-party resources it gets much more interesting when someone implements it once and everyone else can benefit. With Cloudflare Apps’ new worker support you can bundle up and deliver complex worker logic for anyone else to consume and publish it to the Apps marketplace.

If you are a third-party content provider for sites, think about what you might be able to do to leverage workers for your content for sites that are served through Cloudflare.

I get excited thinking about the performance implications of something like a tag manager running entirely on the edge without the sites having to change their published pages and without browsers having to fetch heavy JavaScript to do the page modifications. It can be done dynamically for every request directly on the edge!

Categories: Technology

My time as an intern (thus far)

Wed, 21/11/2018 - 06:19
My time as an intern (thus far)

It has been over three months since I started as a marketing intern at Cloudflare. Even before joining the Cloudflare team, I enjoyed reading the technical blog posts about Cloudflare’s use cases and solutions, as well as the inclusive and creative culture. Educating the world about the threats we face on the Internet is something that I found truly valuable. I figured that I would give my own spin on what it’s like to join and work at Cloudflare by writing a blog post too.

Chapter 1: The Path towards the Orange Cloud

My time as an intern (thus far)Photo by alexander milo / Unsplash

Before starting as a freshman in university, I created an online portfolio for my photography. It has been a passion of mine for about 9 years. I tried a multitude of platforms but none afforded me the aesthetic control that I wanted. The only solution was to build and host my own site. I started learning HTML/CSS, a bit of JavaScript and jQuery and so on.

This led to me using the Koken CMS, and hosting it on DigitalOcean in a Docker container. What was left was SSL/TLS encryption and a CDN (my friends in Europe find the loading times unbearable). I tried ISRG’s Let’s Encrypt (SSL makes for a better Google rank, and ensures the connection is secure) but did not quite manage to get it to work (entirely my fault). I scrolled through Reddit, talked to my Microsoft Student Partners buddies, and found out about Cloudflare.

I was seeing orange.

Cloudflare has the security and performance features that I needed for SSL/TLS encryption and the CDN features for my website, and that is just scratching the surface. From then on, I learned about how Cloudflare came to be, their mission and emphasis on privacy and security. All of which became reasons why I applied for Cloudflare as a marketing intern. Being a part of a tech company whose mission and products I believe in is a pretty big deal for me.

Why is it a big deal?

I strongly believe in advocating for the right to speak freely - being able to hear all opinions of a topic whether I agree with them or not. Privacy ensures that people still have the power to speak about personal issues or speaking out against oppressive regimes without having their voices suppressed. Security relates to trust and credibility for end users and ensures that businesses do not resort to nefarious methods to compete such as acquiring DDoS services to shut down their competitors.

Cloudflare enables. And that is just a small part of the bigger picture that is to help create a better Internet for everyone.

Chapter 2: Riding the Cloud

A big part of working at Cloudflare is working with diverse cross-functional teams. I am a part of the APAC team based in Singapore, being the bridge to the marketing team in San Francisco alongside a fellow intern. Suffice to say, I have learned a lot about the business having worked with the amazing people who have been responsible for the region’s accelerating growth.

Two weeks into the internship, I was supporting an event in Malaysia meeting potential prospects and managing the event. This proved to be very valuable in understanding how we connect to our audience and the characteristics of the market that the event represents. It was definitely a learning experience to familiarise myself with the process in order to enable the sales team and being part of Cloudflare’s growth (more than 12 million protected domains). Through the trust and ownership that was given to me, I set about understanding the target audience and learning new tools as fast as possible.

Chapter 3: It is a starship

My time as an intern (thus far)

Image source: Vollhov @ https://www.deviantart.com/vollhov

Being at Cloudflare feels like you are in a state-of-the-art starship. Simplicity with underlying innovation, sophistication and complexity. Cloudflare makes it super intuitive for users with their one-click features through its dashboard, but underneath those features lies a complex system of code that is also lightweight and efficient.

I suppose what really puts Cloudflare on the map for me is the 1.1.1.1 release. I was especially excited because finally I had an alternative DNS to use, all the more because I have always been an advocate of privacy and security. Believing in the mission and products that the company provides played an important role for me in applying to Cloudflare.

Being part of this ever evolving starship has certainly been an exhilarating ride. In my opinion, it will only keep accelerating towards the fringes of the galaxy, and beyond.

Chapter 4: What’s next?

With about two months left at Cloudflare, and the events and campaigns coming up in rapid-fire fashion, I will certainly be ending my internship with a bang. Having to dig deep into learning one concept made me realise that it takes a long time to master, be it the intricacies of marketing or technicalities of a product. It is definitely a stimulating assault on the brain but in a good way.

I have learned a lot about working in a very diverse team; multicultural and cross-functional. Cloudflarians wear many hats – the myriad of backgrounds across teams means that there is never a shortage of differing opinions and ideas to share with others. This all the more helps create a refined approach towards building this mission of ours: to help build a better internet for everyone.

Here's a snippet of the diversity that lives and breathes in Cloudflare

Interested in @Cloudflare? Here it is in 7 different languages

Categories: Technology

Cloudflare's Response to a Privacy Framework

Sat, 17/11/2018 - 20:09
Cloudflare's Response to a Privacy FrameworkPhoto by Dayne Topkin / UnsplashCloudflare's Response to a Privacy Framework

Why We Weighed In on US Privacy Efforts

Cloudflare’s mission is to help build a better internet, and privacy has to be at the heart of that effort. That’s why we submitted comments last week on the National Telecommunications and Information Administration (NTIA)’s request for comment on its proposed approach to advance consumer privacy.

We think it is important for Internet infrastructure companies like us to be a part of the conversation about the future of internet privacy. We want to advocate for an internet that remains accessible to all, while becoming more secure and protective of privacy.

What is NTIA and what is it trying to do?

In 2018, we’ve seen high profile data breaches and data misuse, Europe’s sweeping data protection law – the General Data Protection Regulation (GDPR) – come into effect, and California pass its own comprehensive Consumer Privacy Act (CCPA). All of this has captured the attention of Washington, D.C. lawmakers and regulators.

On September 25, 2018, NTIA began a process to solicit feedback from stakeholders on a proposed approach to consumer data privacy.  NTIA is the Executive Branch agency in the Department of Commerce that is principally responsible for advising the President on telecommunications and information policy issues. The Administration’s hope is that NTIA will produce an approach to privacy that could inform future federal privacy efforts.

At the same time, another Department of Commerce agency, the National Institute of Standards and Technology (NIST) has begun a parallel process. NIST is a physical science lab and non-regulatory agency whose mission is to promote innovation and industrial competitiveness. Its mandate is to advance standards and measurement science in service to economic security, and their voluntary cybersecurity framework is used by businesses to manage cybersecurity risk. Their aim with their own stakeholder process on consumer privacy is to develop an enterprise-level voluntary framework that businesses can use to mitigate privacy risks for consumers.

What we do to improve privacy in our community

As we thought about how best to engage with NTIA and NIST on their efforts, we thought it was important to stress that, for us, privacy is about much more than what is required by regulation. Protecting privacy is essential to maintaining trust not only with our customers but with all Internet users. That is why we have worked for years to develop and expand access to privacy-enabling technologies. For example, our customers – those who pay for our services and those who use our services for free – benefit from free SSL certificates. We also support DNSSEC, and recently announced that we would be supporting automatic DNSSEC, enabling increased usage of DNSSEC and additional security on the net. This year we also launched a product called Spectrum, which allows us to provide security and encryption for all TCP traffic rather than just for HTTP traffic.

We also have created products that make web browsing more private. To enable our users to take control over who is viewing their personal browsing information, this year Cloudflare launched 1.1.1.1, a privacy-focused DNS resolver. We just released the mobile App version this week that will allow you to take advantage of this service on your phone. Cloudflare also introduced encrypted Server Name Identification (eSNI), which encrypts the URL of the website a user is accessing. Mozilla has recently added eSNI functionality for testing on Firefox Nightly.

Towards a U.S framework for privacy protection

Although Cloudflare has a longstanding commitment to privacy, the last few years have strengthened our view that it is not enough for individual companies to be focused on privacy.  Given the importance of the issue, the U.S. government needs to be involved as well. The EU, and several other countries such as Canada, Japan, China, and Brazil, have already weighed in with privacy laws of their own. We believe an effort to develop a U.S. privacy approach will bolster and strengthen the ability of technology companies to continue to operate globally, providing confidence that the United States shares the view that the privacy of personal data is worth protecting.

In our comments to NTIA, we asked the US government to use all of the tools at hand, including trade agreements, to ensure that data is able to flow freely across borders and that our rules are interoperable with other laws and regulations around the world. We went on the record supporting Federal legislation that serves the goals of advancing consumer privacy, protecting innovation, and enabling security research.

Any US effort on data privacy must also have the ability to evolve and flex over time. This can be achieved by using technology neutral language and leveraging industry advisory groups and technical experts for ongoing guidance.

We believe that companies should be motivated to use privacy by design and encouraged to deploy innovation in the area of privacy protection. Organizations should use appropriate measures to secure their data in meaningful and proportionate ways. Cloudflare fully supports the use of a risk-management framework to provide companies the flexibility to make decisions based on the context of their individual businesses. And we think there should be a statutory baseline, with flexibility to add features on top, based on the type of data an organization collects and how it uses it.

We think accountability is essential to raising the bar on consumer privacy protections. We told NTIA that if the U.S. Federal Trade Commission (FTC) is to hold companies accountable under its Section 5 authority, then the FTC will need significantly more resources before it can be expected to effectively enforce new privacy standards.

We also urged the US government to consider creating incentives to support privacy research. We agree that we need to incentivize technology development that increases privacy and security, and we also want to ensure that the government doesn’t hinder technology developments that improve privacy and security.

Encryption is key to privacy on the internet, and any government-mandated encryption back doors would be highly concerning as such backdoors undermine data protection. Moreover, in the wake of discussions around proposed content filtering initiatives in the EU, we would urge governments to consider potential resultant privacy weaknesses. Some incentives towards privacy research should be dedicated to analyzing the costs and benefits of government mandates that weaken security.

Wish List

While we were at it, we also threw out a few new ideas. We asked if the Federal government could explore a sufficiency scheme, where companies under a certain size, or with a presence below a certain threshold in another country, could be free from the burden of answering complaints in that jurisdiction. The country could then file the complaint with the FTC and rely on the FTC to take appropriate action. To allow small and medium enterprises to answer complaints in front of a single body, regardless of the jurisdiction where a breach occurred, for example, would go a long way towards reducing the burden of compliance.

We also suggested that the U.S. government could play a positive role in risk management by taking steps to reduce the potential impact of exposure of information. Collection of personal data poses a more significant risk to consumers if that same personal data can be misused to assume someone’s identity or affect their access to goods and services. A leak of social security numbers, for example, is problematic because social security numbers have become the way in which to access sensitive documents, like financial, health and education records. Rethinking this model, and potentially developing new ways of addressing digital identity, could go along way to reducing privacy risk for consumers.

Next Steps

Cloudflare appreciates the U.S. government’s efforts to modernize U.S. privacy policy, and we look forward to continuing to collaborate with NTIA, NIST, the U.S. International Trade Administration, Congress and others as they work towards meaningful consumer privacy protections.

Categories: Technology

Pages

Additional Terms