Write your Background Tasks in C++

This year at Build I went to a few sessions covering the new Background Task infrastructure available for Windows Phone 8.1 Apps. The Microsoft Program Managers running these sessions all echoed a common theme: you should write your background task code in C++.

Why C++ you might ask? The main reason is conservation of memory resources. If you write your background tasks in C# you need to load up the Common Language Runtime and according to the Microsoft PMs at Build this could take 3-4 MB of memory. These days 3-4 MB of memory doesn’t sound like a whole lot, but when you’re writing background task code, you don’t have much to work with. Below is a chart of the memory available to background tasks on different device types.



Source: http://msdn.microsoft.com/en-US/library/windows/apps/xaml/hh977056.aspx#WP_EXTRA_RESOURCE_CONSTRAINTS

The most popular device in the Windows Phone ecosystem is the Lumia 520, a 512MB device, so when writing your background task code you want to keep that 16MB constraint in mind. What this means is that by writing background tasks in C++ instead of C# you have up to 25% more memory available to you. That’s significant!

I decided I wanted to investigate this more on my own, so I wrote 2 sets of sample apps using background tasks. The first one is the simplest type of background task you can have; a SystemTrigger that displays a toast notification when the Time Zone changes.

Here is the tasks Run() method written in C#:

public sealed class SampleSystemTrigger : IBackgroundTask { public void Run(IBackgroundTaskInstance taskInstance) { ToastTemplateType toastTemplate = ToastTemplateType.ToastText01; var notificationXml = ToastNotificationManager.GetTemplateContent(toastTemplate); var toastElements = notificationXml.GetElementsByTagName("text"); var memString = GetMemAvailString(); var toastString = "This is a C# toast - Mem Avail: " + memString; toastElements[0].AppendChild(notificationXml.CreateTextNode(toastString)); var stn = new ToastNotification(notificationXml); var toastnotifier = ToastNotificationManager.CreateToastNotifier(); toastnotifier.Show(stn); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public sealed class SampleSystemTrigger : IBackgroundTask { public void Run ( IBackgroundTaskInstance taskInstance ) { ToastTemplateType toastTemplate = ToastTemplateType . ToastText01 ; var notificationXml = ToastNotificationManager . GetTemplateContent ( toastTemplate ) ; var toastElements = notificationXml . GetElementsByTagName ( "text" ) ; var memString = GetMemAvailString ( ) ; var toastString = "This is a C# toast - Mem Avail: " + memString ; toastElements [ 0 ] . AppendChild ( notificationXml . CreateTextNode ( toastString ) ) ; var stn = new ToastNotification ( notificationXml ) ; var toastnotifier = ToastNotificationManager . CreateToastNotifier ( ) ; toastnotifier . Show ( stn ) ; } }

And here is a task Run() method written in C++/CX that performs the same behavior:

void SampleSystemTrigger::Run(IBackgroundTaskInstance^ taskInstance) { ToastTemplateType toastTemplate = ToastTemplateType::ToastText01; auto notificationXml = ToastNotificationManager::GetTemplateContent(toastTemplate); auto toastElements = notificationXml->GetElementsByTagName("text"); auto memString = GetMemAvailString(); auto toastString = "This is a C++ toast - Mem Avail: " + memString; toastElements->First()->Current->AppendChild(notificationXml->CreateTextNode(toastString)); auto stn = ref new ToastNotification(notificationXml); auto toastnotifier = ToastNotificationManager::CreateToastNotifier(); toastnotifier->Show(stn); } 1 2 3 4 5 6 7 8 9 10 11 12 void SampleSystemTrigger :: Run ( IBackgroundTaskInstance ^ taskInstance ) { ToastTemplateType toastTemplate = ToastTemplateType :: ToastText01 ; auto notificationXml = ToastNotificationManager :: GetTemplateContent ( toastTemplate ) ; auto toastElements = notificationXml - > ; GetElementsByTagName ( "text" ) ; auto memString = GetMemAvailString ( ) ; auto toastString = "This is a C++ toast - Mem Avail: " + memString ; toastElements - > ; First ( ) - > ; Current - > ; AppendChild ( notificationXml - > ; CreateTextNode ( toastString ) ) ; auto stn = ref new ToastNotification ( notificationXml ) ; auto toastnotifier = ToastNotificationManager :: CreateToastNotifier ( ) ; toastnotifier - > ; Show ( stn ) ; }

One thing I need to point out here is that the C++ code really isn’t that different from the C# code from the first sample. Just replace your var’s with auto’s and trade a few dot operators for scope or arrow operators and it’s essentially the same code. These minor changes in syntax will get you significant savings in memory though. I put in some additional statements to these methods to track available memory using the MemoryManager APIs. Here’s a graph showing the results of my profiling:

So my profiling shows a difference of ~2MB between the C# and C++ versions of my background task example. This is slightly better then the 3-4MB quoted by Microsoft during some of the Build sessions, but it could be that the MemoryManager APIs don’t show the full story. Either way I’m sure Microsoft did a lot of profiling of their new Background Task infrastructure, and they are recommending C++ for a reason, this test just shows that there is a clear advantage to using C++ over C# when writing background tasks.

It’s important to note that writing your background task code in C++ does not mean you need to write your app in C++ as well. With Windows Runtime you can mix C#, C++/CX and even WinJS easily.

Now this sample background task was an extremely simple one, so I wanted to look at a more real world example as well. One of the new types of tasks introduced to Windows Runtime at Build is the XamlRenderingBackgroundTasks. This can be used to render some XAML to a PNG, which can then be used to update your app’s live tiles.

I found the sample code for a C++ XamlRenderingBackgroundTask on MSDN, but interestingly enough there was not a sample for a C# XamlRenderingBackgroundTask. That’s right, Microsoft thinks that writing your tasks in C++ is so important that they didn’t even create a C# version of the sample code.

So to do this comparison I wrote a C# version of the task. Again this isn’t terribly difficult to do since they are both C based languages using the same WinRT APIs. There is a lot more code involved in the XamlRenderingBackgroundTasks, so I’ll be showing the OnRun() methods here, and you can grab the finished projects on my OneDrive here: 1drv.ms/1oW9KYn

Note that instead of using the IBackgroundTask interface and implementing it’s Run() method we are inheriting from the XamlRenderingBackgroundTask class and overriding it’s OnRun() method. If you try using XAML from a normal IBackgroundTask you will hit exceptions.

public sealed class AppTileUpdater : XamlRenderingBackgroundTask { protected override async void OnRun(IBackgroundTaskInstance taskInstance) { var deferral = taskInstance.GetDeferral(); await GenerateHighResTileImageUpdateAsync("customTile.xml", "customTile.png", new Size(150, 150)); UpdateTile("customTile.png"); // Complete the deferral so that the system knows we're done. deferral.Complete(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public sealed class AppTileUpdater : XamlRenderingBackgroundTask { protected override async void OnRun ( IBackgroundTaskInstance taskInstance ) { var deferral = taskInstance . GetDeferral ( ) ; await GenerateHighResTileImageUpdateAsync ( "customTile.xml" , "customTile.png" , new Size ( 150 , 150 ) ) ; UpdateTile ( "customTile.png" ) ; // Complete the deferral so that the system knows we're done. deferral . Complete ( ) ; } }

And here is the XamlRenderingBackgroundTask OnRun() in C++:

void AppTileUpdater::OnRun(IBackgroundTaskInstance^ taskInstance) { auto deferral = Agile<BackgroundTaskDeferral^>(taskInstance->GetDeferral()); create_task(GenerateHighResTileImageUpdateAsync("customTile.xml", "customTile.png", Size(150, 150))) .then([this, deferral]() { UpdateTile("customTile.png"); // Complete the deferral so that the system knows we're done. deferral->Complete(); }); } 1 2 3 4 5 6 7 8 9 10 11 12 13 void AppTileUpdater :: OnRun ( IBackgroundTaskInstance ^ taskInstance ) { auto deferral = Agile < ; BackgroundTaskDeferral ^ > ; ( taskInstance - > ; GetDeferral ( ) ) ; create_task ( GenerateHighResTileImageUpdateAsync ( "customTile.xml" , "customTile.png" , Size ( 150 , 150 ) ) ) . then ( [ this , deferral ] ( ) { UpdateTile ( "customTile.png" ) ; // Complete the deferral so that the system knows we're done. deferral - > ; Complete ( ) ; } ) ; }

Having Asynchronous methods does increase the complexity of our C++/CX code obviously but we have the Parallel Patterns Library which gives us create_task and other useful functions. Making asynchronous programming in C++ surprisingly easy. I won’t be going in depth into PPL now since it’s outside the scope of this post, but there are plenty of great resources to be found online about PPL. If you have a Pluralsight subscription I’d recommend Kenny Kerr’s course on Modern C++ Concurrency.

Anyway, let’s have another look at the memory comparison between the XamlRenderingBackgroundTask in C# and C++:

The profiling of this task shows a peak difference of 2.4MB when comparing the C++/CX Release task versus the C# Release task.

Well I hope this post has been helpful. There is definitely a demonstrable benefit to writing your background task code in C++/CX. And doing so is not as scary as some of you may think. Modern C++ has really improved the process of writing C++ code, but that’s a topic for another post.

If you want to learn more about Background Tasks in Windows Phone 8.1 I highly recommend watching the following sessions from Build 2014:

Multitasking and Triggered Background Tasks for Windows Phone Apps http://channel9.msdn.com/Events/Build/2014/2-518

Building Geo-Aware Apps with Maps and Geofencing http://channel9.msdn.com/Events/Build/2014/2-526

Building Great Bluetooth Apps for Windows Phone http://channel9.msdn.com/Events/Build/2014/2-519

Live Tiles Enhancements http://channel9.msdn.com/Events/Build/2014/2-523

Notification Platform Development on Windows http://channel9.msdn.com/Events/Build/2014/2-521

As usual, comments are always welcome.