惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Recent Announcements
Recent Announcements
Y
Y Combinator Blog
Vercel News
Vercel News
Martin Fowler
Martin Fowler
V
V2EX
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LangChain Blog
云风的 BLOG
云风的 BLOG
H
Hackread – Cybersecurity News, Data Breaches, AI and More
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium
Google DeepMind News
Google DeepMind News
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
F
Fortinet All Blogs

博客园 - winkingzhang

Build 2016概览 WinRT开发系列之基础概念:WinRT不是…… VS2010 Extension实践(3)——实现自定义配置 [VS2010 Extension]PowerExtension.GoToDefinition VS2010 Extension实践(2) VS2010 Extension实践 如何通过反射调用带有ref或者out的参数的方法[迁移] Win7硬盘安装和移动硬盘访问出错的修复办法[迁移] zt. Windows Mobile开发文章收藏 [WPF]在Style中设置ToolTip的问题分析 [WPF]RadioButton在Group的Header区部分不响应鼠标选择的bug分析 WPF模式思考 (zt) How to Get IIS Web Sites Information Programmatically Visual Studio 调试器 Application Request Routing and the IIS 7.0 Web Management Service Reference Resources for MOSS and WSS FileSystemWatcher事件多次触发的解决方法 ASP.NET Application Life Cycle CAS and Native Code
WinRT开发系列之编程语言:功能和效率
winkingzhang · 2013-02-19 · via 博客园 - winkingzhang

WinRT开发有着多种选择性,就编程语言这一点就表现的很突出;这里就这一点深入展开,探讨在WinRT开发之初如何依据各个编程语言的特性、功能和效率来对产品的技术方向做出选择。

这里我选择运行计算复杂度较高的算法作为测试方法,虽然不能代表全部,但是很大程度上展示大家平时开发过程中所面临的常见场景和问题。考虑到演示和理解,就选择了查找100000以内的所有素数的个数的算法作为演示。另外也顺带演示如何在WinRT下实现多编程语言和技术之间的协作吧。

关于基本知识和算法吧详细的说明,请自行搜索各大引擎吧(关键词:prime、素数),这里我就列举在各个语言下我的简单实现吧,其中包括使用普通算法和并行计算的两个版本。

第一部分,从目前.NET主流来看吧,以C#为例,普通版本,这个没什么多说的,就是从前往后看某个数是不是素数:

private static int CountingInternal(int n)
{
    var numprimes = 1;
    for (var i = 3; i <= n; i += 2)
    {
        var isPrime = true;
        var limit = Math.Ceiling(Math.Sqrt(i)) + 1;
        for (var j = 3; j < limit; j += 2)
        {
            if (i%j == 0)
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
        {
            numprimes++;
        }
    }
    return numprimes;
}

并行版本稍微复杂一点点,选择Parallel.For来并行执行一个从1至n/2的并行循环(我这里偷懒了一下,没有处理奇偶数的情况,因为我的调用时传入的都是偶数),发现是素数,使用Interlocked辅助方法给计数增加1。

private static int CountingParallel(int n)
{
    var numprimes = 1;
    Parallel.For(1, n/2, i =>
    {
        if (IsPrime(i*2 + 1))
        {
             Interlocked.Increment(ref numprimes);
        }
    });
    return numprimes;
}

public static bool IsPrime(int n)
{
    if (n%2 == 0)
        return false;
    var limit = (int) (Math.Ceiling(Math.Sqrt(n)) + 1);
    for (var i = 3; i < limit; i += 2)
    {
        if (n%i == 0)
        {
            return false;
        }
    }
    return true;
}

第一种场景,直接嵌入算法到C# WinRT App工程,执行结果如下(单位毫秒):

执行次数 1(启动) 2 3 4 5
普通 14.0299 9.0005 9.1825 8.0021 11.0181
并行 6.0008 2.0004 2.9993 2.0014 3.999

第二种场景,将C#算法包装在一个类库里(注意是CLR类库,只能在C#/VB直接通用),在C# WinRT App工程中调用这个类库,执行结果如下(单位毫秒):

执行次数 1(启动) 2 3 4 5
普通 12.0299 9.0019 10.003 9.0014 9.00017
并行 6.0008 2 3.0003 2.9997 1.9995

第三种场景,将C#算法包装到一个Windows Runtime Component(WRC)中,在C# WinRT App工程中调用这个WRC类库,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  11.9904  9.0032  9  9。0028 9.00149 
并行   6.0008  1.9817  1.9985  1.9993  2

第四种场景,将C#算法包装到一个Windows Runtime Component(WRC)中,在WinJS App工程中调用这个WRC类库,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  11  9  8  9 8
并行   4  1  1  3  2

小结:以上是从.NET角度来进行的比较,很容易看出第一次CLR加载在这里性能损耗表现的很明显,完成加载之后性能将稳定在一定范围内波动;另外,并行计算在纯算法的应用中有很明显的性能优势。

第二部分,接下来我们回归Native环境,这里我依然使用普通和并行计算两种来尝试,普通的依然没什么可说的(实际上和C#的没区别,除了关键字不一样)。

static int CountingInternal(int n)
{
	auto numprimes = 1;
	for (auto i = 3; i <= n; i += 2)
	{
		auto isPrime = true;
		auto limit = ceil(sqrt(i)) + 1;

		for (auto j = 3; j < limit; j += 2)
		{
			if (i%j == 0)
			{
				isPrime = false;
				break;
			}
		}

		if (isPrime)
		{
			numprimes++;
		}
	}
	return numprimes;
}

并行版本,需要注意的是C++ lambda的传值和作用域问题,其他的和C#的没区别:

static bool IsPrime(int n)
{
	if (n%2 == 0)
		return false;
	auto limit = (int) (ceil(sqrt(n)) + 1);
	for(auto i=3; i<limit; i+=2)
	{
		if(n%i == 0)
		{
			return false;
		}
	}
	return true;
}

static int CountingParallel(int n)
{
	auto numprimes = 1;
	parallel_for(1, n/2, [&](int i)
	{
		if(IsPrime(i*2+1))
		{
			InterlockedIncrement((volatile unsigned long*)&numprimes);
		}
	});
	return numprimes;
}

第一种场景,直接将C++算法放到C++ WinRT App中使用,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  8.0019 7.9991  8.0209  8.9843  8.0181 
并行   1.9794  1.998  1.9994  1.984  2.0003

第二种场景,将C++算法包装在DLL中,在C++ WinRT App中使用,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  9 9  9  8  9 
并行   3 2  3  2  2

第三种场景,将C++算法包装在动态连接库Dll中,在C# WinRT App中通过PInvoke来调用,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  9 9  8  9  9 
并行   3 2  3  2  3

第四种场景,将C++算法包装在静态链接库Lib中,在C++ WinRT App中调用,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  8 8  8  9  9 
并行   2 3  3  2  3

第五种场景,将C++算法包装在Windows Runtime Component(WRC)中,在C# WinRT App中调用,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  8.0014 8.0191  8.0293  8.0019  9.0291
并行   1.9994 1.9999  1.998  1.9994  2.99982

第六种场景,将Windows Runtime Component(WRC)中,在WinJS App中调用,执行结果如下(单位毫秒):

执行次数  1(启动)  2 3 4
普通  9 8  9  8  8 
并行   2 2  3  2  3

第七种场景是将C++算法包装在Windows Runtime Library(WRL,基于COM的底层开发)中,然后在任何一种WinRT App中调用,可以预见这是一种很强大的方式,但同时也是最费解的一种方式,我成功的包装了普通算法的COM版,但是尝试了很长时间不能成功实现并行运算的版本,也就放弃在这里展示了,如果你知道如何在WRL中实现并行计算并返回IAsyncOperation<T>,请不吝赐教。 

小结:基于C++的实现在适用性、稳定性和执行效率上无可挑剔,如果对于所有细节(包括第一次启动)的效率考虑,C++是优先的;如果考虑到C++的复杂度,如果项目对性能要求可以适当放松但对进度要求很高的时候,选择CLR会比较容易控制的;如果原来已有的Web项目向WinRT迁移,那么前段展示则可以考虑使用WinJS+HTML来实现,后台算法根据需要选择C++或者CLR。

第三部分,如果所有的算法全部运行在JavaScript中,那么其性能如何呢?这里我先买个关子,留待你自己去探究和发掘。

总结,WinRT在编程语言的选择性上有着非常好的灵活性,在做选择的时候需要充分考虑自己的要求,比如性能、比如工期、比如经验等等。对于全新项目,在有经验的情况下,追求极致性能的首先首当其冲是C++,如果考虑到经验和掌控,可以选择使用C++做底层,选择相对容易上手的C#/VB或者HTML+JS做界面的方法;如果项目工期要求很紧,或者从老系统迁移,那么这时候更多的考虑是使用已有资源,直到性能瓶颈的时候才采取措施——以C++重写性能瓶颈来解决,当然,如果没有C++经验,也可以考虑使用C#/VB来实现WRC以包装核心逻辑,从而提升运行效率。

附以上测试源代码和测试工程,点击这里下载

* 本文在葡萄城控件技术团队博客同时发布