Friday, December 21, 2012
C# Tip: A nice format code for numbers {0:N}
I work with a lot of large numbers, and I have a hard time reading them without a thousands separator You can make my life better by using the thousands separator e.g. {0:N} in your C# code. Thanks in advance!
Thursday, December 20, 2012
Cool Tools: Google Reader (any blog reader)
Sadly google has decommissioned google reader, check out the old reader as a replacement.
Skip this post if you already use a blog (/news/atom/rss) reader, otherwise you need to read this post.

- I wonder if Igor wrote something interesting?
- What the heck was Igor's blog again?
- Dang - Igor didn't post, oh well.
There's a much better way, an application called a news reader. You tell the news reader which blogs you want to read and the news reader tells you when the blogs have posts available. The blog reader even lets you read the posts mostly formatting free inside the news application. The news reader I use is Google Reader, but any reader will do. Again if you don't yet use a news reader, fire up Google Reader right now and subscribe to this blog.
Enjoy your more efficient blog reading!
Tuesday, December 18, 2012
Category:SaaS - Software as a Story
Software is about
people, and people like stories. Thus, I'm adding a new
category SaaS or
Software as a Story, to the blog. This will be a chance to tell stories related
to software engineers and software engineering. Some stories will be happy and
some stories will be sad, some stories will be mine, and some will be my friends, some of the stories might even be true, but ascertaining the truth of the story will be left as an exercise for the reader. Fire me a mail if you'd like to donate a story.
people, and people like stories. Thus, I'm adding a new
category SaaS or
Software as a Story, to the blog. This will be a chance to tell stories related
to software engineers and software engineering. Some stories will be happy and
some stories will be sad, some stories will be mine, and some will be my friends, some of the stories might even be true, but ascertaining the truth of the story will be left as an exercise for the reader. Fire me a mail if you'd like to donate a story.
Category:DevPreview - Developer Preview
I've decided to
create a new blog category - Developer Preview (DevPreview). Posts in DevPreview aren't fully baked, but I want
to post them anyways so I can get feedback from smart people like you.
As I get feedback,
I'll
update the posts in place until I'm proud enough of the post to make it ready. Once ready I'll remove the developer preview tag and the post will be ready
for general availability.
If you're not passionate about a topic being discussed in DevPreview I recommend you skip the post until it clears DevPreveiw.
Sunday, December 9, 2012
Dumping the method descriptor for a System.Action (delegate)
In my previous post I needed to figure out what method descriptor a System.Action points to. This is painful so I wrote a small debugger script to automate it. To use the script, copy this code to dumpMDForAction.dbg. Many thanks to Alois Kraos for posting how to do this.
Without further ado, fire up cdb and find a System.Action:
Next launch the script, passing the address of the action to the script:
Happy debugging!
Without further ado, fire up cdb and find a System.Action:
0:000> !do 0000003de7023078
Name: System.Action
MethodTable: 000007f9d26b9a68
EEClass: 000007f9d2071c10
Size: 64(0x40) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
MT Field Offset Type VT Attr Value Name
000007f9d26c1ac8 400002d 8 System.Object 0 instance 0000003de7023078 _target
000007f9d26c1ac8 400002e 10 System.Object 0 instance 0000003de7028800 _methodBase
000007f9d26c60d8 400002f 18 System.IntPtr 1 instance 3de52e0900 _methodPtr
000007f9d26c60d8 4000030 20 System.IntPtr 1 instance 7f97fbfc040 _methodPtrAux
000007f9d26c1ac8 4000031 28 System.Object 0 instance 0000000000000000 _invocationList
000007f9d26c60d8 4000032 30 System.IntPtr 1 instance 0 _invocationCount
Next launch the script, passing the address of the action to the script:
0:000> $$>a< dumpMDForAction.dbg 0000003de7023078
usage - $$>a< ".\dumpMDForAction.dbg" addressOfObject
<.shell waiting 10 second(s) for process>
.shell: Process exited
0:000> r $t0=7f97fbfc040
0000003de7023078
Dumping
Method Name: PerformanceOfSmallThings.Program.b__3()
Class: 000007f97fd022d0
MethodTable: 000007f97fbf39d0
mdToken: 0000000006000009
Module: 000007f97fbf2f90
IsJitted: yes
CodeAddr: 000007f97fd10950
Transparency: Critical
0:000>
Happy debugging!
Friday, December 7, 2012
Cool Tools:LinqPad
If you use C# and you don't use LinqPad, drop everything you're doing and download it - it will change your life. LinqPad is so great, I'm not going to describe it - just download and run it.
Tuesday, December 4, 2012
Run time costs of small operations in C#.
Thank you smart people that helped me understand this: Vance Morrison ; Kevin Frei; Dave Detlefs; Jan Kotas; Sean Selitrennikoff; Nathan Iarovich; Dave Driver.
One of my colleagues read this post and pointed out for really small operations e.g {i++ } the cost of the measurement system is exceeding the cost of the operations. I agree completely, and recommend using measure it and reading the help if you need to measure small things.
My colleague also noticed something interesting; in this code (full code here, proof of concept in linqpad using measureit.):
We get the surprising result, incrementing a local number is faster then doing nothing:
What the heck is going on? Why would incrementing a local variable be faster then running nop, and why would incrementing a static variable take the same as NOP? To answer that we need to see the assembly code. To find the assembly code read this.
Here's what the assembly looks like:
staticInt++:
static nop:
lambda nop:
localInt++:
Maybe the caller is different - lets check:
Nope! Now what? We need to trace execution from the caller to our function. breakpoint caller and trace execution.
Yeah, I know painful to read - lets look at that in VIMDiff:
What do we see? When running the nop function we have 5 extra instructions. Those instructions are a shuffle thunk (see Figure 2 here), and is required when calling static delegates.
OK so why is incrementing a local number is faster then doing nothing? Because we're not measuring those things, we're measuring the cost of delegate invocation. In this case, we're actually comparing the cost of static vs instance delegates, and instance is faster. You see there are two performance optimizations in play interacting from an execution time perspective:
1) JIT makes instance delegates faster then statics delegates (I assume it's the common case).
2) Compiler optimizes nop to be a static delegate (I assume to save a potential object instantiation).
One of my colleagues read this post and pointed out for really small operations e.g {i++ } the cost of the measurement system is exceeding the cost of the operations. I agree completely, and recommend using measure it and reading the help if you need to measure small things.
My colleague also noticed something interesting; in this code (full code here, proof of concept in linqpad using measureit.):
class Program
{
static int interlocked = 0;
static int staticInt = 0;
static void DoNop() { }
static void IncrementStaticInt() { staticInt++; }
// This is an array to make it easier to debug in cdb.
static NamedAction[] namedActions;
static void Main(string[] args)
{
int localInt = 0;
namedActions = new NamedAction[]
{
new NamedAction {name="lambda function: InterlockedIncrement()", action = ()=>Interlocked.Add(ref interlocked,interlocked)},
new NamedAction {name="static function: staticInt++", action = IncrementStaticInt},
new NamedAction {name="static function: nop", action = DoNop},
new NamedAction {name="lambda function: staticInt++", action = ()=>{staticInt++;}},
new NamedAction {name="lambda function: localInt++", action = ()=>{localInt++;}},
new NamedAction {name="lambda function: nop", action = ()=>{}}
};
int Million = 1000 * 1000;
int iterations = 10 * Million;
Console.WriteLine("Milliseconds to perform {0} iterations", iterations);
...
We get the surprising result, incrementing a local number is faster then doing nothing:
C:\hgs\ig2600_blog\PerformanceOfSmallThings\bin\Release>PerformanceOfSmallThings.exe
Milliseconds to perform 10000000 iterations
lambda function: InterlockedIncrement() took 221.1698 ms
static function: staticInt++ took 47.3857 ms
static function: nop took 43.1259 ms
lambda function: nop took 43.0301 ms
lambda function: staticInt++ took 42.7559 ms
lambda function: localInt++ took 35.0208 ms
What the heck is going on? Why would incrementing a local variable be faster then running nop, and why would incrementing a static variable take the same as NOP? To answer that we need to see the assembly code. To find the assembly code read this.
Here's what the assembly looks like:
staticInt++:
000007f9`7fd40940 ff05262deeff inc dword ptr [000007f9`7fc2366c]
000007f9`7fd40946 c3 ret
static nop:
000007f9`7fd40960 c3 ret
lambda nop:
000007f9`7fd50950 c3 ret
localInt++:
000007f9`7fd50930 8b4108 mov eax,dword ptr [rcx+8]
000007f9`7fd50933 ffc0 inc eax
000007f9`7fd50935 894108 mov dword ptr [rcx+8],eax
000007f9`7fd50938 c3 ret
Maybe the caller is different - lets check:
Breakpoint 0 hit
000007f9`7fd50930 8b4108 mov eax,dword ptr [rcx+8] ds:000000fa`9f582fb8=01312d0d
0:000> k
Child-SP RetAddr Call Site
000000fa`9d37e9a8 000007f9`d32d3c5c 0x7f9`7fd50930
000000fa`9d37e9b0 000007f9`7fd506a8 mscorlib_ni+0x1263c5c
000000fa`9d37ea00 000007f9`df419133 0x7f9`7fd506a8
0:000> g
Breakpoint 1 hit
000007f9`7fd50950 c3 ret
0:000> k
Child-SP RetAddr Call Site
000000fa`9d37e9a8 000007f9`d32d3c5c 0x7f9`7fd50950
000000fa`9d37e9b0 000007f9`7fd506a8 mscorlib_ni+0x1263c5c
000000fa`9d37ea00 000007f9`df419133 0x7f9`7fd506a8
Nope! Now what? We need to trace execution from the caller to our function. breakpoint caller and trace execution.
0:000> ub 000007f9`d32d3c5c
mscorlib_ni+0x1263c48:
000007f9`d32d3c48 94 xchg eax,esp
000007f9`d32d3c49 ab stos dword ptr [rdi]
000007f9`d32d3c4a 2aff sub bh,bh
000007f9`d32d3c4c 3c00 cmp al,0
000007f9`d32d3c4e 0000 add byte ptr [rax],al
000007f9`d32d3c50 488b54ca18 mov rdx,qword ptr [rdx+rcx*8+18h]
000007f9`d32d3c55 488b4f08 mov rcx,qword ptr [rdi+8]
000007f9`d32d3c59 ff5718 call qword ptr [rdi+18h]
0:000> bp 000007f9`d32d3c59
0:000> g
Breakpoint 2 hit
0:000> ta
000000fa`9d9c0960 488bc4 mov rax,rsp
000000fa`9d9c0963 4c8bd9 mov r11,rcx
000000fa`9d9c0966 4889d1 mov rcx,rdx
000000fa`9d9c0969 4d8b5320 mov r10,qword ptr [r11+20h] ds:000000fa`9f5897e8=000007f97fc3c070
000000fa`9d9c096d 4983c320 add r11,20h
000000fa`9d9c0971 49ffe2 jmp r10 {000007f9`7fc3c070}
000007f9`7fc3c070 e99b501100 jmp 000007f9`7fd51110
000007f9`7fd51110 488b4110 mov rax,qword ptr [rcx+10h] ds:000000fa`9f583068=000000fa9f583078
000007f9`7fd51114 488b4808 mov rcx,qword ptr [rax+8] ds:000000fa`9f583080=000000fa9f583078
000007f9`7fd51118 488b4018 mov rax,qword ptr [rax+18h] ds:000000fa`9f583090=000000fa9d9c0900
000007f9`7fd5111c 48ffe0 jmp rax {000000fa`9d9c0900}
000000fa`9d9c0900 488bc4 mov rax,rsp
000000fa`9d9c0903 4c8bd9 mov r11,rcx
000000fa`9d9c0906 4d8b5320 mov r10,qword ptr [r11+20h] ds:000000fa`9f583098=000007f97fc3c040
000000fa`9d9c090a 4983c320 add r11,20h
000000fa`9d9c090e 49ffe2 jmp r10 {000007f9`7fc3c040}
000007f9`7fc3c040 e90b491100 jmp 000007f9`7fd50950
Breakpoint 1 hit
000007f9`7fd50950 c3 ret
0:000> g
Breakpoint 2 hit
mscorlib_ni+0x1263c59:
000007f9`d32d3c59 ff5718 call qword ptr [rdi+18h] ds:000000fa`9f5897e0=000000fa9d9c0960
0:000> ta
000000fa`9d9c0960 488bc4 mov rax,rsp
000000fa`9d9c0963 4c8bd9 mov r11,rcx
000000fa`9d9c0966 4889d1 mov rcx,rdx
000000fa`9d9c0969 4d8b5320 mov r10,qword ptr [r11+20h] ds:000000fa`9f5897e8=000007f97fc3c070
000000fa`9d9c096d 4983c320 add r11,20h
000000fa`9d9c0971 49ffe2 jmp r10 {000007f9`7fc3c070}
000007f9`7fc3c070 e99b501100 jmp 000007f9`7fd51110
000007f9`7fd51110 488b4110 mov rax,qword ptr [rcx+10h] ds:000000fa`9f583008=000000fa9f583018
000007f9`7fd51114 488b4808 mov rcx,qword ptr [rax+8] ds:000000fa`9f583020=000000fa9f582fb0
000007f9`7fd51118 488b4018 mov rax,qword ptr [rax+18h] ds:000000fa`9f583030=000007f97fc3c1e8
000007f9`7fd5111c 48ffe0 jmp rax {000007f9`7fc3c1e8}
000007f9`7fc3c1e8 e943471100 jmp 000007f9`7fd50930
Breakpoint 0 hit
000007f9`7fd50930 8b4108 mov eax,dword ptr [rcx+8] ds:000000fa`9f582fb8=01312d0d
0:000>
Yeah, I know painful to read - lets look at that in VIMDiff:
What do we see? When running the nop function we have 5 extra instructions. Those instructions are a shuffle thunk (see Figure 2 here), and is required when calling static delegates.
OK so why is incrementing a local number is faster then doing nothing? Because we're not measuring those things, we're measuring the cost of delegate invocation. In this case, we're actually comparing the cost of static vs instance delegates, and instance is faster. You see there are two performance optimizations in play interacting from an execution time perspective:
1) JIT makes instance delegates faster then statics delegates (I assume it's the common case).
2) Compiler optimizes nop to be a static delegate (I assume to save a potential object instantiation).
Thursday, November 22, 2012
Batch Tip: Extend path with the current directory.
To extend the path by a directory I usually take several tries as I incorrectly type the new path line, or merge copy and paste lines. Today I discovered a simple alternative, cd to the directory and use the %PWD% special environment variables, e.g:
cd directory\Of\Interest
set PATH=%PATH%;"%CD"
Sunday, November 18, 2012
Downloading files in powershell
I used to download files in batch files using wget or curl. Now that I have "the powershell" I can do better, by instantiating a WebClient object. Here's my example dependency downloader for my coffee script unit tests.
$filesToDownload = (
"http://jashkenas.github.com/coffee-script/extras/coffee-script.js",
"https://raw.github.com/jquery/qunit/master/qunit/qunit.js",
"https://raw.github.com/jquery/qunit/master/qunit/qunit.css"
)
$webClient = new-object System.Net.WebClient
$filesToDownload | % {
$uri = new-Object System.Uri $_ ;
$localPath = "$($pwd.Path)\$($uri.Segments[-1])";
Write-Host "Writing $localPath" ;
$webClient.DownloadFile($uri,$localPath);
}
Saturday, November 10, 2012
Cool Tools: ConEmu
ConEmu is a windows console replacement. You still run cmd.exe or powershell.exe, but you get a console host which doesn't suck. I've only been using it for a few hours but here's some of the stuff I love:
- Multi Tab Support
- Sane Copy/Paste Support
- Correct Handling of Win-Left and Win-Right
- Win7 integration (see different tabs during icon hover)
- Everything just works (so far)
Friday, August 31, 2012
Run time costs of common operations in C#
Disclaimers:
1) The benchmarking methodology below should not be used for very small things like i++. For measuring small things see this.
2) Since writing the below I've modified measure it for integration with linqpad - use that instead.
3) If you still want to read this, be my guest :)
Today we debated if Enum.ToString() is too slow to be used. To my surprise it takes 1-5 micro seconds on my core-i7 laptop. If 5 micro seconds is too slow for you, the internet has you covered.
Naturally, I was curious about cost of other operations so I measured those as well. You can reproduce and extend my experiment by putting the below code into linqpad. The output from the current code on my laptop is:
Milliseconds to perform 100000 iterations
Enum.ToString() took 411 ms
NewGuid() took 31
ms
InterlockedIncrement() took 3 ms
Lock() took 3 ms
i++ took 2 ms
1) The benchmarking methodology below should not be used for very small things like i++. For measuring small things see this.
2) Since writing the below I've modified measure it for integration with linqpad - use that instead.
3) If you still want to read this, be my guest :)
Today we debated if Enum.ToString() is too slow to be used. To my surprise it takes 1-5 micro seconds on my core-i7 laptop. If 5 micro seconds is too slow for you, the internet has you covered.
Naturally, I was curious about cost of other operations so I measured those as well. You can reproduce and extend my experiment by putting the below code into linqpad. The output from the current code on my laptop is:
Milliseconds to perform 100000 iterations
Enum.ToString() took 411 ms
NewGuid() took 31
ms
InterlockedIncrement() took 3 ms
Lock() took 3 ms
i++ took 2 ms
static TimeSpan TimeIt(int iterations, string title, Action action)
{
var watch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
action();
}
return watch.Elapsed;
}
struct timeable
{
public string title;
public Action code;
}
internal enum AnEnum
{
Fast,
Slow
}
void Main()
{
int interlocked=0;
object lockable = new Object();
var timeables = new timeable []
{
new timeable {title="Enum.ToString()", code = ()=>AnEnum.Fast.ToString()},
new timeable {title="NewGuid()", code = ()=>Guid.NewGuid()},
new timeable {title="Lock()", code = ()=>Monitor.Enter(lockable)},
new timeable {title="InterlockedIncrement()", code = ()=>Interlocked.Add(ref interlocked,interlocked)},
new timeable {title="i++", code = ()=>interlocked++},
};
int iterations=100*1000;
Console.WriteLine("Milliseconds to perform {0} iterations", iterations);
var timings = timeables.Select(x=>new {x.title, elapsed = TimeIt(iterations,x.title,x.code)}).OrderByDescending(x=>x.elapsed).ToList();
timings.ForEach(t=>Console.WriteLine("{0} took {1} ms",t.title,t.elapsed.TotalMilliseconds));
}
Sunday, June 24, 2012
Minimum price for a Storage SaaS subscription?
Storage SaaS providers (SSaaS) are companies which sell end users a product that requires lots of storage. Examples are backup (Mozy) and file sync (DropBox). When SSaaS get to a billion customers, their primary expense will be that of storage and that's what I focus on in this post.
I suspect all SSaaS buy their storage from one of the big 3 utility computing sources (Microsoft, Amazon, Google), which all have identical pricing. As of June '12 - The utility computing price for storage is 1.25$/GB/yr for low volume and 0.6$/GB/yr for high volume.
This means, that a SSaaS providers price must exceed the price of 1.25$/GB/yr to be profitable even with a smaller customer base. Lets check the pricing of some services:
- DropBox - 100$/50GB = 2$/GB
- Mozy - 60$/50GB = 1.2$GB
Dropbox is profitable on storage cost out of the gate, but what about Mozy? At 1.2$/GB they would be taking a loss until they can get enough customers to qualify for storage volume discount pricing.
I have no insider information but I can deduce a few factors come into play to make SSaaS pricing model calculations:
- Customers over provision: Few customers have exactly 50GB, I suspect most customer are vastly over provisioned.
- Storage optimization: By being an SSaaS you can apply compression over your entire customer base. As a trivial example assume 200 customers own the same movie, you can store a single copy of it and save your self gigabytes in cost.
- Reduced storage cost over time: Over time your company will get larger data volume discounts, and the general price of storage will fall (although odds are your competitors price will drop as well).
There are obvious things my price model doesn't consider, and before setting a SSaaS pricing strategy you should probably evaluate them :)
- Free accounts Ratios- Most SSaaS provide free storage on the order of 5-20GB - that's 6-24$ per year, with a lot of free accounts that significantly increases your cost.
- Every cost not including storage :)
Monday, May 28, 2012
System Testing Bookmarklets: Selenium
My switch search engine bookmarklet had two bugs. First when I deployed the page, the URL was screwed up (thanks for telling me Chris) and second my bookmarklet was incompatible with FireFox[1].
Conveniently the software engineering community has a solution to this class of problems and it's called system testing. In this context, System testing means interact with the web pages via a browser just like users do. I did some digging and I couldn't find a perfect (or awesome) tool for system testing web apps.
I was able to get a tool that could drive FireFox and Chrome (IE isn't working). This tool had some warts, and took some experimentation to get working, but it mostly meets the needs. Please comment if you find a better tool!
The tool I used is called Selenium, and it's scriptable via python (also Java and C#). Selenium is able to impersonate a user performing actions via the common web browsers. The code below should make reasonable sense to someone familiar with the DOM. As usual, the code is also available on bitbucket:
Notes:
[1] InnerText isn't support in FF, need to use InnerHtml instead.
Conveniently the software engineering community has a solution to this class of problems and it's called system testing. In this context, System testing means interact with the web pages via a browser just like users do. I did some digging and I couldn't find a perfect (or awesome) tool for system testing web apps.
I was able to get a tool that could drive FireFox and Chrome (IE isn't working). This tool had some warts, and took some experimentation to get working, but it mostly meets the needs. Please comment if you find a better tool!
The tool I used is called Selenium, and it's scriptable via python (also Java and C#). Selenium is able to impersonate a user performing actions via the common web browsers. The code below should make reasonable sense to someone familiar with the DOM. As usual, the code is also available on bitbucket:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
import unittest, time, re
import urllib
class RunTheClick(unittest.TestCase):
def setUp(self):
self.base_url = "http://ig2600.blogspot.com"
self.drivers=[]
def test_chrome(self):
d = webdriver.Chrome()
self.drivers.append(d)
self.run_suite(d)
def test_firefox(self):
d = webdriver.Firefox()
self.drivers.append(d)
self.run_suite(d)
def run_suite(self,driverToUse):
self.driver = driverToUse
driver = self.driver
# Get Blog Page
driver.get(self.base_url + "/2012/05/assembly-to-javascript-tsrs-to.html")
bookmarklet = driver.find_element_by_partial_link_text("SwapSearchEngine")
#bookmarkletURL starts with uri scheme(javascript:), remove it to get code
bookmarkletURL = bookmarklet.get_attribute("href")
bookmarkletCode = bookmarkletURL.split("javascript:")[1]
# BUGBUG: bookmarkletCode is URLEncoded in FF but not in Chrome.
# In future, unquoting might not be safe in chrome, find correct way to handle this.
bookmarkletCode = urllib.unquote(bookmarkletCode)
# click on bookmarklet in page and verify it throws a failure alert.
bookmarklet.click()
self.verify_and_dismess_not_on_google_or_bing_alert()
# Selenium can't execute bookmarklet if there is an alert in play - that's too bad.
# otherwise we'd do the below
# driver.execute_async_script(bookmarkletCode)
# self.verify_and_dismess_not_on_google_or_bing_alert()
#go to google and search
driver.get("http://google.com")
# Very brittle (found it using web inspect in firefox developer tools)
textBox = self.driver.find_element_by_id("gbqfq")
textBox.send_keys ("a b c")
textBox.send_keys (Keys.RETURN)
time.sleep(2) # sleep to let autocomplete finish.
self.assertEqual(driver.title,u"a b c - Google Search")
# execute bookmarklet
print bookmarkletCode
driver.execute_script(bookmarkletCode);
# verify we switched to bing
self.assertEqual(driver.title,u"a b c - Bing")
# execute bookmarklet
driver.execute_script(bookmarkletCode);
# verify back on google.
self.assertEqual(driver.title,u"a b c - Google Search")
def verify_and_dismess_not_on_google_or_bing_alert(self):
# Click on the alert
alert = self.driver.switch_to_alert()
self.assertEqual(alert.text,u"Only works on a Google or Bing search")
alert.accept()
time.sleep(2) # sleep to let dialog go away.
def tearDown(self):
for d in self.drivers:
d.quit()
if __name__ == "__main__":
unittest.main()
Notes:
[1] InnerText isn't support in FF, need to use InnerHtml instead.
Sunday, May 27, 2012
Assembly to JavaScript;TSRs to Bookmarklets
Back when I started computing (the nineties), the coolest thing to to know was assembly. If you knew assembly you could make the computer do anything because the computer was essentially executing assembly[1].
Back in those days the way to inject code into the system was something called a Terminate And Stay Resident (TSR). This mini application could be invoked via a hotkey and run arbitrary code in your memory space - this was a great way to hack applications, or provide additional capabilities to programs.
Luckily history repeats itself. Today, assembly has been replace by JavaScript, and the mechanism for TSRs is called bookmarklets. Bookmarklets are book marks which execute arbitrary javascript code.
To show you the power of bookmarklets I wrote a JavaScript bookmarklet that will take a search from bing and run it on google or vice versa. You can install it by dragging SwapSearchEngine to your bookmark bar and then clicking on it. I've embedded a video showing the bookmarklet in use.
If you'd like to play with bookmarklets the code is below and also available @ bitbucket.
javascript:function swapSearchEngine() {
/* Test cases:
1) do a google search - run bookmarklet
2) do a bing search - run bookmarklet
3) Go to a random page - run bookmarklet - make sure error occurs.
*/
/* Find page title. */
var title = document.getElementsByTagName('title')[0].innerText;
var errorMessage = "Only works on a Google or Bing search";
/* search engine format is title="search term - Search Engine" */
var searchEngineIndex = title.lastIndexOf(" - ");
if (searchEngineIndex == -1) {
alert(errorMessage);
return;
}
var currentSearchEngine = title.substring(searchEngineIndex + 3);
var newSearchLocation = "";
if (currentSearchEngine == "Google Search") {
newSearchLocation = "http://www.bing.com";
} else if (currentSearchEngine == "Bing") {
newSearchLocation = "http://www.google.com";
} else {
alert(errorMessage);
return;
}
var searchTerms = title.substring(0, searchEngineIndex);
var newURL = newSearchLocation + "/search?q=" + escape(searchTerms);
window.location = newURL;
};
swapSearchEngine();
void(0);
Notes:
[1] Technically the computer didn't execute assembly, but there was a 1:1 mapping between assembly and what the computer executes.
Sunday, May 20, 2012
Soft Skills: Listen to people, and understand what they are telling you
(This post is not designed to be belittling, instead it's designed to be root cause analysis.)
When Zach, my two year old, figures something out he screams out barely intelligible words and repeats them until I can figure out what he is saying and repeat it back to him. The more non-obvious Zach's discovery the more insistent Zach will be that I understand his unintelligible statement.
For example, the other day Zach and I were outside and Zach started screaming buddaf - I was baffled! Zach was very insistent so I started looking around and sure enough I found a picture of a butterfly painted on a bus stop. I screamed butterfly, and Zach became ecstatic and we were able to move on to another topic.
By contrast if I can't figure out what Zach is saying he gets sad, and acts rejected. This morning at breakfast Zach wanted something and I couldn't figure out what it was. For 3 minutes Zach kept repeating masfd (which I still can't figure out) with vigour and passion. Then Zach started saying masfd quieter and quieter, looking sadder and sadder. Finally, rejected, Zach got up and left the table.
When people grow up, they're the same. They still have novel thoughts that they want heard and understood. If you can't understand and hear those thoughts the speaker will feel sad and rejected, and likely feel rejected for significantly longer than a two year old.
So, when someone, lets call her Alice, tells you something - hear Alice, and work really hard to understand what Alice says. Otherwise, Alice will feel sad and rejected, and will probably continue repeating her thoughts for a lot longer. Notice that just because you hear and understand Alice, doesn't mean you have to agree with her, just hearing Alice is often sufficient.
When Zach, my two year old, figures something out he screams out barely intelligible words and repeats them until I can figure out what he is saying and repeat it back to him. The more non-obvious Zach's discovery the more insistent Zach will be that I understand his unintelligible statement.
For example, the other day Zach and I were outside and Zach started screaming buddaf - I was baffled! Zach was very insistent so I started looking around and sure enough I found a picture of a butterfly painted on a bus stop. I screamed butterfly, and Zach became ecstatic and we were able to move on to another topic.
By contrast if I can't figure out what Zach is saying he gets sad, and acts rejected. This morning at breakfast Zach wanted something and I couldn't figure out what it was. For 3 minutes Zach kept repeating masfd (which I still can't figure out) with vigour and passion. Then Zach started saying masfd quieter and quieter, looking sadder and sadder. Finally, rejected, Zach got up and left the table.
When people grow up, they're the same. They still have novel thoughts that they want heard and understood. If you can't understand and hear those thoughts the speaker will feel sad and rejected, and likely feel rejected for significantly longer than a two year old.
So, when someone, lets call her Alice, tells you something - hear Alice, and work really hard to understand what Alice says. Otherwise, Alice will feel sad and rejected, and will probably continue repeating her thoughts for a lot longer. Notice that just because you hear and understand Alice, doesn't mean you have to agree with her, just hearing Alice is often sufficient.
Sunday, April 29, 2012
Cool Tools:Excel Online - The web services I use
I wanted to show off embedding online spreadsheets from excel and sky drive, and remind myself how much I spend renting content, hosting, and renting software. So, here's what an embedded excel sheet looks like To produce, save an excel file to your sky drive then click Share then Embedded:
The excel web app control is great, you can sort, filter, or make local modifications (they are not saved to the original). Anyway - have fun with this.
The excel web app control is great, you can sort, filter, or make local modifications (they are not saved to the original). Anyway - have fun with this.
Sunday, April 22, 2012
Why would you make the maximum RAM in a laptop 4GB?
Lots of the new laptops coming out have a 4GB RAM limit. This baffles me, the best bang for your buck improvement you can make to a machine is to put in more RAM. I use the Lenovo X220 precisely because it supports 16GB of RAM, and the first thing I did when I got it was upgrade the RAM to 16GB (total cost ~ 100$)
There are 2 reasons I can think of to have a 4GB ram maximum total:
1) Macbook Air Did It - If it's good enough for apple , it's good enough for us. By the way, while I hate this reasoning, I bet it's actually the real reason.
2) Resume From Hibernate - Hibernate is a feature where all RAM is written out to disk so the machine can be powered down thus saving battery. To power back up, all content is read from disk into RAM and then we re-start the program execution. The more RAM you have the longer resume from hibernate takes. Lets go through an example:
To resume from hibernate you need to read back all your RAM from disk.
Assume you can read disk at 100MB/s (that's pretty fast for a spinning disk)
Assume you need to read 16GB.
This will take (160s) or 2.5 minutes just to read back your RAM state.
I'll save how to fix hibernate for another blog post!
There are 2 reasons I can think of to have a 4GB ram maximum total:
1) Macbook Air Did It - If it's good enough for apple , it's good enough for us. By the way, while I hate this reasoning, I bet it's actually the real reason.
2) Resume From Hibernate - Hibernate is a feature where all RAM is written out to disk so the machine can be powered down thus saving battery. To power back up, all content is read from disk into RAM and then we re-start the program execution. The more RAM you have the longer resume from hibernate takes. Lets go through an example:
To resume from hibernate you need to read back all your RAM from disk.
Assume you can read disk at 100MB/s (that's pretty fast for a spinning disk)
Assume you need to read 16GB.
This will take (160s) or 2.5 minutes just to read back your RAM state.
I'll save how to fix hibernate for another blog post!
Thursday, April 19, 2012
The 5 assignment operators of "R"
In every programming language I've used, '=' is the assignment operator, but in R you can assign 1 to x in the 5 different ways listed below. I'm sharing this with you because it reminded me how many ways there are to skin a cat, and how each of those choices has tradeoffs.
[A] x = 1
[B] x <-1
[C] 1 -> x
[D] x <<- 1
[E] 1 ->> x
[A] x = 1
'=' is supported in R, but rarely used. Google Style Guide says don't use it. And I've never seen R code that does.
[B] x <-1
'<-' Is the most commonly used, so you'll almost always see x<-1. Some R people claim x<-1 is a superior assignment operator since the '=' assignment operator (x=1) can be confused with the equality operator (x==1). It strikes me the '<-' assignment operator can be confusing as well. When using negative integers a single space can really change the meaning aka: x<-1 vs x < -1. Trade offs are everywhere!
[C] 1 -> x
Assignment arrows work in both directions in the human mind, and they do in R too.
[D] x <<- 1
'<<-'. Assign not to x in local scope, but the x that is in the first parent scope. To do this in a C family language you'd have to specify you wanted the x in the parent scope e.g: ::x = 1. Notice the difference in R is that the operator changes the meaning of the variable passed to it. This is very strange in other languages.
[E] 1 ->> x
Assignment arrows work in both directions in the human mind, and they do in R too.
Monday, April 2, 2012
Cool Tools: Machine re-imaging:Ninite
I frequently need to re-image my machines, as a result, I need to install a bunch of apps (skype, paint.net quick time, picasa,etc). Every time I need to do this I forget that last time i found an app that solves this problem.
The app is called ninite, and it creates an on the fly downloader/installer for all the apps you want to install. Give it a try the next time you need to reimage a machine.
Hopefully, this will help me remember what I'm doing for next time.
The app is called ninite, and it creates an on the fly downloader/installer for all the apps you want to install. Give it a try the next time you need to reimage a machine.
Hopefully, this will help me remember what I'm doing for next time.
Saturday, March 10, 2012
The Azure Leap Year Bug
I work at Microsoft on the Windows Azure Fabric Controller team. Azure had a nasty outage on February 29 2012, which many people have asked me about.
The full write up is here. Not only does this write up describe the outage, it's an excellent description of the azure fabric controller, and many of the technologies I work on.
I'm extremely proud of our response to the outage. We were completely transparent in our assessment, and as it says in the write up: "Rest assured that we are already hard at work using our learnings to improve Windows Azure".
The full write up is here. Not only does this write up describe the outage, it's an excellent description of the azure fabric controller, and many of the technologies I work on.
I'm extremely proud of our response to the outage. We were completely transparent in our assessment, and as it says in the write up: "Rest assured that we are already hard at work using our learnings to improve Windows Azure".
Subscribe to:
Posts (Atom)