CodingMyLife

C# .NET MicroFramework

浅谈.NET Micro Framework的性能优化。

127 views
April23

(如有转载,请注明出处,谢谢)

由于工作原因,这几个月对.NET家族的新成员.NET Micro Framework做了一些研究和实践,对于这个可以直接运行在硬件上的小家伙还是很看好的。

它的可剪裁性,高定执行,和天生对硬件高集成度都让它的前途一片光明。当然,它现在还很年轻,就发布的SDK v3.0来看,它还有很长的路要走。

废话不说,就这几个月我用下来的经验谈谈在针对MF应用程序的性能优化吧。

1. 尽可能减少方法调用!
方法调用过于频繁对于性能的影响非常大,所以所有的优化都是以这个为大前提的。

2. 尽可能避免使用属性,而用公共域来代替。
因为编辑器会在编译的时候为每个属性的getter和setter添加访问方法,基于第一条,这是要避免地。
当然也不是说完全避免使用属性了,毕竟有的时候属性是很方便且必要的。
比如这个下面这个使用属性的例子:

public class Test
{
 public string Name { get; set; }
}

需要把它改成这样:

public class Test
{
 public string Name;
}

3. 只在构造函数里面初始化变量。
这一条很容易明白,看下面的例子:

public class Test
{
 private string name = "Test String";
 private DateTime date = DateTime.Now;
 private int score, counter;
 
 public Test()
 {
  score = 0;
  counter = 0;
 }
}

初始化的工作其实进行了两次,一次是在声明变量的时候,另外一次是在调用构造函数的时候。遵循第一条原则,我们要尽可能减少方法调用,且构造函数的使用概率很高,所以我们在此需要把初始化的工作全部集中到构造函数里面来进行。

4. 只在必要的地方调用lock。
对于MF这样一个半实时的系统来说,lock的成本远远高于我们的想象。在.NET里面可能感觉不出来,但到了MF这个小伙子手里感觉就非常明显了,也许这一条大家已经知道了,就当我老调重弹吧。
看这个例子:

public class Test
{
 private ArrayList objs;
 
 public void SomeMethod(object o)
 {
  for(int i = 0; i < 100; i++)
  {
   if (objs.Contains(o))
   {
    lock(objs.SyncRoot)
    {
     objs.Remove(o);
    }
   }
  }
 }
}

在一个循环里面增删一个集合,由于是多线程访问,所以在操作之前加了锁。之所以在循环内部加锁,理由可能是想尽可能的减少lock的访问次数,只有满足那个if条件的时候才会被调用。
实际上,这个想法错了,无论如何,这里的lock都会被调用很多次,这些开销加起来就会对性能造成很大的影响。
把代码改成这样就会好很多:

public class Test
{
 private ArrayList objs;
 
 public void SomeMethod(object o)
 {
  lock(objs.SyncRoot)
  {
   for(int i = 0; i < 100; i++)
   {
    if (objs.Contains(o))
    {
     objs.Remove(o);
    }
   }
  }
 }
}

5. 保证每个时间只有一个线程在运行。
用惯了.NET,来到MF世界第一个不适应就是它的多线程太慢了,如果同时打开两个线程工作,那么整个程序的效率都会受到极大的影响。
拿电子地图软件来做例子,主线程负责更新UI,工作线程负责在后台取得地图块。这样的设计本身无可厚非也是合理的,但最后我们发现性能实在太差了。
后来更改成为当用户在操作UI的时候,工作线程全都暂停,只有检测到用户没有任何操作的时候才进行工作。
要实现这一点,就要求程序在设计的时候就考虑到工作线程的可暂停性。

6. 尽可能少的并且在最小的范围内调用Invalidate()方法。
很多人在重画UI之后都会习惯性的调用顶层元素的Invalidate()方法来更新所有子控件,因为这样是最快捷的。可很多时候我们忽略了一点,Invalidate()这个方法可能在背后已经被调用过很多次了。
比如,有的控件会在得到焦点的时候调用这个方法,有的控件会在出发用户事件的时候自动调用这个方法。因为这些都是在背后发生的,我们可能并不知情,所以在完成我们自己控件的绘制之后通常会调用parent的Invalide来更新整个布局,这样就会在不知不觉之间导致了不必要的重画产生。
要避免这个问题也很简单,一则仔细观察,二则用Refactor!去阅读一下别人的代码。

7. 尽可能少的使用图片资源。
因为MF本身的数据吞吐量很小,如果载入过多图片资源的话,轻则程序运行效率变低,重则出现内存溢出。所以这里的原则我们要参照网页的设计原则,例如一个按钮图片,把它切割成几个小块,利用重复贴图来完成中间部分,而不要直接使用一整张图片。
同样在制作高亮的时候可以通过改变图片透明度或者在图片上面加一层透明矩形来实现。

8. 仅导入必要的字体资源。
这一点和上一条的理由是一样的,都是减少运行期间的数据吞吐量。对于英文来说还好,本来就不大,对于中文来说就很重要了,因为中文字体动辄就是几百k上兆,如果全部导入的话简直就是灾难。
最好就是程序用到多少就导入多少,实在没办法,就把生僻字全部剔出吧。

9. 窗体最好用完就是立即关闭。
这一点对于窗体很多的应用程序非常重要!在.NET的世界里,打开一个主窗体,然后在主窗体里面创建子窗体的做法非常常见。但这可能会成为你的MF程序运行效率最大的隐性杀手。
例如 主窗体 -> 产品列表 -> 产品详细信息 -> 产品操作窗口 -> 结算窗口
这是一个常见的逻辑线,此时一共有五个窗口被打看,如果你有时间尝试的话,会发现在打开结算窗口的时候,整个程序已经气喘吁吁,动弹不得了。
而且,因为MF的半实时性,导致GC在关闭窗口之后不能立即释放资源,如果用户反复打开关闭这些窗口,内存很快就溢出了。
所以实现一个窗口管理器非常重要,要确保每个时间只有一个窗口在运行。

10. 减少Timer的使用。
Timer也是性能消耗的大户,我曾见过一个程序里面打开了数十个Timer,那性能简直惨不忍睹。所以如果可能,保证整个程序只是用一个Timer,且只在必要的时候启动它,将会为你的程序减轻很多负担。

(如有转载,请注明出处,谢谢)

我的Visual Studio 2008颜色配置。

332 views
March20

花了好几天慢慢调出来的颜色,现在长时间看着眼睛也不会不舒服了。

My visutal studio theme.

My visutal studio theme.

点击这里下载
posted under 未分类 | No Comments »

发布PhotoInfo v0.6.

702 views
November3

最近喜欢上了摄影,交流的时候发现大家都会把拍摄信息加到照片上,Windows下面用光影魔术手就可以方便的做到,但是Mac下面我没找到可用的(中间尝试了iWatermark,但是不好用)。
索性自己写个算了,实现添加相框和拍摄参数。

用法:

1. 拖放照片(必须是jpg格式)或者包含照片的文件夹到程序窗口。
2. 设置好输出路径后点击“开始处理”就好了。

界面截图:

Read the rest of this entry »

posted under PhotoInfo | 6 Comments »

用NSData玩转二进制文件的读写。

969 views
October28

今天看到有同学问如何读写二进制文件,那就顺道讲一下吧。
在Cocoa的世界里,所有与某个物件相关的东西都被该物件实现。好吧,我承认,这句话比较扯…. 还是上例子说明吧。

在通常的编程语言里面(这里指的是C/C++/Java/C#/Ruby/…),以下几个东西是如此关联起来的:

字符串类用来处理字符串存储,查找,替换…等等常规功能。
路径类用来处理所有与文件系统路径相关的事情,比如什么把路径分段,拿到文件扩展名,文件是否存在等等。
目录类则用来看某个路径的目录是否存在,是不是为空,等等。

这里一般会有三个类来处理各自逻辑相关的事情,但是,注意但是,Cocoa里面的构建法则不是这样的,它把所有与字符串相关的功能全部封装到了字符串类里面。
所以,NSString可以做的事情超多,从打开文件读取/写入字符串内容,到判断路径是否存在,等等,所有的事情,它都一手包了。
这样倒是方便程序员了,反正一旦有什么事情和字符串有点关系,找它准没错。

好了,讲了这么一堆,回过头来看这个问题,如何读写二进制文件?我写了各种类型的数据(整形,浮点,字符串)进去,怎么读出来?
这里提到了二进制对吧,也就是数据对吧,那么好了,找NSData就行了。

来看这段小代码,我准备了一个文件路径做演示,还有几个要写进去的数据。 Read the rest of this entry »

posted under Cocoa | 6 Comments »

Core Image 学习笔记 - 为照片加边框和EXIF信息。

1,208 views
October20

最近迷上了摄影,拍了很多照片之后就想上传跟大家分享。到色影无忌和几个摄影论坛上一看,发现很多人的照片都有不错的边框、水印和拍摄信息(相机类型、光圈、快门、拍摄日期等等….)。
因为用的是Mac,找了很久,没有发现…(为什么每次写东西都是这个理由?)
好吧,废话少说,先把要做的事情分类。

1.调整图片大小,毕竟放到网上的图片不需要原图那么大。
————————————————–
以下是学习了Core Image之后的一点点实践。

NSImage *image = [[NSImage alloc] initWithContentsOfFile:path];
[image setScalesWhenResized:YES];
[image setSize:NSMakeSize(1000.0, [image size].height * (1000.0/[image size].width))];

这个方法确实可以调整大小了,但是缩小的图片质量非常差。花了很多时间仔细看文档,最后发现,Core Image其实就是对于Quartz 2D的包装,所有的绘图操作其实都值对于当前的NSGraphicsContext起作用,NSGraphicsContext本身有很多属性用来控制当前绘图的各方面。
而如果只是使用默认设置的话,绘图质量默认是最低。这里我们需要手动设置成高质量。
添加以下两行到代码开头:

//质量设置成高
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
//打开反锯齿
[[NSGraphicsContext currentContext] setShouldAntialias:YES];

Read the rest of this entry »

posted under Cocoa | No Comments »

C语言练习 - 用筛选法算质数。

448 views
September6

这是《C和指针》第六章的习题,用筛选法算质数。因为用到了动态数组,所以需要gcc 3.4 以上版本编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void primes(int range)
{
	int width = range / 2; //只计算奇数,空间效率提高一倍。
	char nums[width];
	char *pnums;
	size_t i, j, temp;
 
	for(pnums = nums; pnums < nums + width; pnums++)
	{
		*pnums = TRUE;
	}
 
	for(i = 1; i < width; i++)
	{
		pnums = nums + i;
		if (*pnums)
		{
			temp = i * 2 + 1;
			for(j = i + 1; j < width; j++)
			{
				if (*++pnums)
				{
					if (!((j * 2 + 1) % temp))
						*pnums = FALSE;
				}
			}
		}
	}
 
	printf ("1 2 ");
	for(i = 1; i < width; i++)
	{
		if (nums[i])
			printf("%d ", i * 2 + 1);
	}
 
	printf("\n");
}
posted under C | 3 Comments »

重新开始学习C语言的第一段小程序 - del_substr。

377 views
September2

看的书是《C和指针》,现在回头再看C,有了很多新的体悟,也让我开始觉得脑子抽筋。告别太多年的指针让我死去活来,非常不习惯,好在多年养成的习惯还在,也不需要赶时间,慢慢来吧。相信啃下了指针之后就会顺滑很多了。

下面这段代码是《C和指针》第六章的一个习题,写出来很是花了我一番力气。又一次体会到了那种为写一行代码抓掉n根头发的感觉。。。。。
中间为了调试,又花时间学习了gdb,让我这个被IDE惯坏的人惊艳了很久。好东西啊!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int del_substr(char *str, char const *substr)
{
	char *ptr_substr = (char *)substr;
	char *strP = str;
	char *temp;
	bool fullMatch = TRUE;
 
	for(;*strP != '\0'; strP++)
	{
		if (*strP == *ptr_substr)
			break;
	}
 
	temp = strP;
	if (*temp != '\0') //Match first char.
	{
		while(*ptr_substr)
		{
			if (*temp++ != *ptr_substr++)
			{
				fullMatch = FALSE;
				break;
			}
		}
 
		if (fullMatch)
		{
			while(*temp)
				*strP++ = *temp++;
			*strP = '\0';
			return 1;
		}
	}
 
	return 0;
}
posted under C | No Comments »

我使用的Firefox 3.0扩展。

309 views
June28

看到Shoegazer写了篇关于他使用的Firefox3.0的扩展。参考了他的选择,我也作出了自己的选择。

应用程序: Firefox 3.0 (2008061004)
操作系统: MacOS Leopard 10.5.3

- Extension List Dumper 1.14.1 转储已安装扩展的列表。
- Fast Dial 1.90 通过缩略图快速访问最喜欢的网站页面
- gladder 2.0.2.2 Get over Great Firewall with Great Ladder!
- Greasemonkey 0.8.20080609.0 面向 Firefox 的用户脚本管理器
- iGetter Extension 2.7.5 Adds iGetter contextual menu commands.
- MediaWrap 0.1.7.3 纠正不规范的网页内嵌媒体,使 Firefox 能准确播放。
- PicLens 1.7.0.3458 Discover More
- Stop-or-Reload Button 0.2.2 Turns the stop and reload buttons into a single one. When you can stop, you have a Stop button, otherwise you have a Reload button. (Like Safari)
- Tab History 1.0.5 Links opened in a new tab retain their history.
- Tab Mix Lite CE 3.0 轻巧便捷的标签式浏览辅助扩展。
- Taboo 0.2.1 The cure for tabitis
- Text Link 2.0.2008052801 允许通过双击载入网页中的 URI 文本。

posted under 未分类 | No Comments »

Project Euler - Problem 18

345 views
April14

今天解决了[Project Euler](http://projecteuler.net)的[第18个问题](http://projecteuler.net/index.php?section=problems&id=18),记录一下解题思路。
题目如下
——–
从三角型的顶端逐行往下移动,然后路径上数字的和最大是23.

1
2
3
4
   3
  7 5
 2 4 6
8 5 9 3

以上这个例子就是,3+7+4+9=23.
找到以下三角形从顶到底路径的最大和:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                           75
                         95 64
                       17 47 82
                     18 35 87 10
                   20 04 82 47 65
                 19 01 23 75 03 34
               88 02 77 73 07 63 67
             99 65 04 28 06 16 70 92
           41 41 26 56 83 40 80 70 33
         41 48 72 33 47 32 37 16 94 29
       53 71 44 65 25 43 91 52 97 51 14
     70 11 33 28 77 73 17 78 39 68 17 57
   91 71 52 38 17 14 91 43 58 50 27 29 48
 63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

*注意:这里只有16384种路径组合,所以可以使用暴力方式得到答案。但是Problem 67,同样的题目,三角形有一百行,不能用暴力方式求解。所以,找到一个聪明的方法才是正道。*

Read the rest of this entry »

posted under 算法 | No Comments »

4月3日算法挑战。

350 views
April3

今天的算法挑战题目如下:
输入字符串,比如: “fb,bc,ac,eb,da,ga”
每组2个字母,用逗号分割。
第一个字母是节点,第2个字母是它的父结点。
写个函数处理输入字符串,并输出整个树的结构。

输出结果:

1
2
3
4
5
6
7
c
|-a
|  |-g
|  |- d
|-b
    |- e
    |- f

Read the rest of this entry »

posted under Ruby | No Comments »
« Older Entries