为Windows编写像uTorrent这样的精简C++程序

42

我一直很喜欢原版 uTorrent 软件。它看起来很棒,不到 64kb,非常快速,并且具备我所需要的所有功能。不幸的是,该程序是闭源的(并且日益臃肿),因此我来到 Stackoverflow 寻求灵感。

你有什么方法可以在 Windows 上编写快速、内存高效、优美的程序?

虽然 C# (和整个 .NET 概念)都是很酷的想法,但我更加关心“纯粹主义”的答案以及编写类似于原始 uTorrent 客户端的高效、快速的 Windows 平台软件的挑战。我不介意分配自己的内存,做自己的垃圾回收并创建自己的数据结构。

欢迎提供有关书籍、文章、库、IDE(甚至是将更多咖啡因注入我的系统的有效方法)的建议。


1
哈哈,咖啡因的那部分让我笑了。好吧,我不认为我有一个答案,但我知道事实上,纯粹主义者会转向汇编语言,但在汇编语言中编写像种子客户端这样复杂的程序几乎是不可能的。我认为,如果你编写自己的代码(避免使用任何库),你可以轻松地将C/C++程序压缩到64kb以下。如果你用汇编语言编写,你可能可以在1kb以下完成它。 - ldog
据我所知,uTorrent带有压缩器,因此其物理大小很小。但在运行时,它会被解压缩并使用更多的内存。通过避免使用许多库(特别是C++标准库和流设施)并创建专门为程序编写的替代品,可以实现小型可执行文件的大小。(请参见维基百科上的文章) - Vanuan
3
你认真的吗,威尔?这已经是我在过去几分钟里看到你关闭的第二个问题了……仅仅因为有一些人对问题的细节存在争议或者不同意,这并不意味着问题本身不具有建设性。事实上,这让我觉得那些讨论问题的人没有起到建设性的作用(无意冒犯)。在我看来,这是一个完全有效的问题。而且请看下面 - 一个回答被接受并获得了13个支持票。对我来说,这表明更多的人喜欢这个问题,而不是你...... - bendr
2
现在,为了谈论所有技术方面,"建设性(Constructive)"的定义(来自Google)是:“有用的目的;倾向于建立” - 我认为这个问题及其答案具有有用的目的。它们旨在帮助确定在C++中如何快速高效地制作应用程序。 - bendr
只是一个建议...小心咖啡因过量。我不是在开玩笑,我曾经有过几次过量的经历,第一次时我没有意识到是咖啡因,但第二天我上网查了一下。当你过量摄入咖啡因时,感觉非常糟糕,现在我自愿地进行茶叶饮食疗法。 </offtopic> - Camilo Martin
显示剩余2条评论
7个回答

17

Windows Template Library (WTL) 适合你想要实现的功能。它是一个基于模板的轻量级 C++ 封装,用于 Win32 API。使用它,你不必经历直接进行 Win32 编码的痛苦,但它也不像 MFC 一样增加了很多开销。


10

uTorrent是用C++编写的,使用老式的Win32 API。Google Chrome也是这样编写的,那么为什么不下载源代码并从他们的代码中学习呢?


1
是的,我同意你的观点。尝试学习Win32 API,然后你就可以创建小巧而快速的应用程序。过去我使用Delphi,它也能创建小型应用程序,并且相当容易学习(Pascal语言)。 - nightingale2k1

8
如果您想要优化尽可能小的内存占用,并且不介意跨越一堆.NET CLR为您设计的障碍,那么编写直接的Win32API应用程序并钩取GDI+是正确的方法。Petzold是权威参考资料。 但实际上,这有点儿愚蠢,因为无论您的应用程序是否使用它,.NET运行时都将被加载到操作系统的内存中,所以最好链接到它。

我认为.NET框架会被单独加载到每个进程中,就像任何其他DLL集合一样。不过最近有改变吗? - Pontus Gagge
8
@Pontus Gagge:你的理解是错误的。Windows 3.x 时代引入 DLL 的整个想法是为了避免把相同的代码加载多次到内存中。Win16 和 Win32 都通过不同的机制在进程之间共享 DLL 代码。DLL 会通过虚拟内存的魔法出现在每个进程的地址空间中,但实际上只有一份 DLL 代码在内存中。 - Greg Hewgill
1
.NET仍然会因为自动内存管理而产生一定的内存开销。此外,除非你想要绘制某些东西(缓慢),否则为什么要使用GDI+? - Joey
我假设他想要一个用户界面,而GDI是我所知道的最紧凑的实现方式。 - Crashworks
1
@Greg:当然,可写静态数据仍会按进程进行复制。我不知道CLR中有多少这样的数据,但几乎肯定比你否则要使用的位的重现所需的代码大小要小... - Steve Jessop
显示剩余3条评论

7

演示场景是一个由一群人组成的团体,他们利用业余时间尝试制作令人印象深刻且非常小的可执行文件,通常将某些内容渲染到音乐中。通常整个演示(代码、音乐、3D数据)会编译成一个单一的可执行文件,该文件被压缩为64k或其他非常小的大小。

你可以从这些演示中获得一些启发,并了解它们的制作方式,这将有助于你追求创建小型可执行文件的热情。

通常,关键是尽可能利用安装在Windows上的许多第三方DLL。此外,还需要进行低级别的自定义编码。


5
一般情况下,对于较小的可执行文件,#define WIN32_LEAN_AND_MEAN和VC_EXTRALEAN(假设使用VS)。不要使用调试符号进行编译(您可能已经知道这一点)。使用更少的库,并确保仅链接所需的库部分(VC的链接器在此方面非常好,但如果可以避免,请勿触碰optlink)。
剥离重定位头:转到http://www.paehl.de/cms/oldtools并搜索“ReduceEXE”(直接下载链接:http://www.paehl.de/reduce.zip)。
运行可执行文件打包程序:http://upx.sourceforge.net/...它在运行时使用更多内存并且启动稍微慢一些,但文件要小得多。
如果您更关心文件大小而不是速度,则VC有一个“优化大小”的选项,它关闭了一些内容,例如循环展开和函数内联。
如果您想要更加极致(并且不关心所有软件工程的优势),可以尝试使用较少的类,优先选择没有虚函数的POS类型。维基百科指出,程序执行时间的6-13%用于进行虚拟调用。此外,vtable本身占用了(一点点)内存,并且为具有虚函数的每个类实例分配了vtable指针的大小_t字节的内存。换句话说,“纯C”可能会稍微快一些(但如果您发现自己正在使用函数指针模拟类,请返回C ++)。

不要使用调试符号进行编译 - 调试符号会在外部PDB文件中创建。即使是发布版本,也绝对没有理由不生成它们(请参见http://www.wintellect.com/CS/blogs/jrobbins/archive/2009/06/19/do-pdb-files-affect-performance.aspx)如果您更关心文件大小而不是速度,则VC有一个“优化大小”的选项 - 这实际上往往会转化为速度性能 - 更小的大小会导致较少的缓存未命中和页面错误。 - On Freund
3
@On Freund -- 不一定 -- 他没有特别提到VC++。VC将调试信息实现为PDB,但是如果你使用DMC或MinGW GCC编译,调试信息会包含在目标文件中。 - Robert Fraser

5

老旧的"LIBCTiny"技巧仍然有效。在现代VC++版本中,您可能需要关闭一些功能。

另一个好的技巧是Kernel32中的lstr*函数集合。因为已经在内存中,所以这些函数可能是更轻量级的选择。


1

Notepad++也是一个非常快速、高度优化和非常有用的操作系统程序,可以激发您的灵感。它的哲学与uTorrent类似。它使用了老牌的Win32 API,应该仍然是Windows上最快的。

如果你想真正地追求艺术,演示场景是一个完美的去处。虽然他们的代码并不总是开放的。


1
无法完全同意“Notepad++非常快速,高度优化”的说法。有时在处理大文件时会变得很慢。而且最近的版本有些错误。另一个轻量级和快速的编辑器是AkelPad - http://akelpad.sf.net/(它的打包exe大小约为100k,插件打包后大小约为250K)。 - zxcat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接