你见过的最荒谬的悲观情绪是什么?

我们都知道,过早的优化是万恶之源,因为它会导致代码不可读/不可维护。更糟糕的是悲观主义,当有人实现一个“优化”,因为他们的 好好想想将更快,但它最终变得更慢,以及错误,不可维护,等等。你见过的最荒谬的例子是什么?

17019 次浏览

我承认没什么大不了的,但是我发现有人用 StringBuffer 在 Java 的循环之外连接 String。就像转身一样简单

String msg = "Count = " + count + " of " + total + ".";

进入

StringBuffer sb = new StringBuffer("Count = ");
sb.append(count);
sb.append(" of ");
sb.append(total);
sb.append(".");
String msg = sb.toString();

过去,在循环中使用这种技术是相当普遍的做法,因为它的速度明显更快。问题是,StringBuffer 是同步的,所以如果只连接几个 String,实际上会有额外的开销。(更不用说,在这个尺度上,差别绝对是微不足道的。)关于这种做法还有两点:

  1. StringBuilder 是非同步的,所以在无法从多个线程调用代码的情况下,应该优先于 StringBuffer。
  2. 现代 Java 编译器会在适当的时候将可读的 String 连接转换为优化的字节码。

我见过人们使用 alphadrive-7完全孵化 CHX-LT。这是一种罕见的做法。更常见的做法是初始化 ZT 转换器,以减少缓冲(由于更大的网络过载阻力)并创建 java 样式的字节表。

太悲观了!

数据库是悲观主义的乐园。

收藏包括:

  • 将表拆分成多个(按日期范围、字母范围等) ,因为它“太大了”。
  • 为退役记录创建一个归档表,但是继续使用生产表 UNION 它。
  • 通过(部门/客户/产品等)复制整个数据库
  • 不要将列添加到索引中,因为这样会使索引太大。
  • 创建大量的汇总表,因为从原始数据重新计算太慢。
  • 创建带有子字段的列以节省空间。
  • 反规范化为字段作为数组。

这是我第一时间想到的。

这可能比你追求的更高层次,但是修复它(如果你被允许的话)也涉及到更高层次的痛苦:

坚持手工滚动对象关系管理器/数据访问层,而不是使用已建立的、经过测试的、成熟的库之一(即使已经向您指出了它们)。

我曾经开发过一个应用程序,里面全是这样的代码:

 1 tuple *FindTuple( DataSet *set, int target ) {
2     tuple *found = null;
3     tuple *curr = GetFirstTupleOfSet(set);
4     while (curr) {
5         if (curr->id == target)
6             found = curr;
7         curr = GetNextTuple(curr);
8     }
9     return found;
10 }

只需删除 found,返回结尾的 null,并将第六行更改为:

            return curr;

应用程序性能翻倍。

我认为“过早优化是万恶之源”这句话用得太多了。对于许多项目,它已经成为一个借口,不考虑性能,直到后期在一个项目。

这句话常常是人们逃避工作的拐杖。我看到这个短语,当人们真的应该说: “哎呀,我们真的没有想到前面,现在没有时间处理它。”。

我见过许多愚蠢的性能问题的“荒谬”例子,而不是由于“悲观化”而引入的问题

  • 在程序启动期间读取同一个注册表项数千次(或数万次)。
  • 加载相同的 DLL 成百上千次
  • 不必要地保留文件的完整路径,浪费了大量的内存
  • 没有组织数据结构,因此它们占用的内存比需要的多得多
  • 调整存储文件名或 MAX _ PATH 路径的所有字符串的大小
  • 对具有事件、回调或其他通知机制的事件进行免费轮询

我认为更好的说法是: “没有测量和理解的优化根本不是优化——它只是随机变化”。

良好的性能工作是耗费时间的——往往更多的是为了开发特性或组件本身。

我曾经看到过一个使用“ Root”表的 MSSQL 数据库。Root 表有四列: GUID (uniqueIdentity)、 ID (int)、 LastModDate (datetime)和 CreateDate (datetime)。数据库中的所有表都被“外键”指向 Root 表。每当在数据库的 任何表中创建新行时,必须使用几个存储过程在 Root 表中插入一个条目,然后才能到达您关心的实际表(而不是由数据库用几个触发器简单触发器来完成这项工作)。

这造成了一大堆无用的无意听到的和令人头疼的东西,需要在它上面写任何东西才能使用 sprocs (并且消除了我向公司介绍 LINQ 的希望)。这是可能的,但不值得头疼) ,而且它甚至没有完成它应该做的事情。

选择这条路径的开发人员认为这节省了大量的空间,因为我们没有在表本身上使用 Guids (但是... ... 难道我们创建的每一行不是在 Root 表中生成的 GUID 吗?),以某种方式提高了性能,并使审计对数据库的更改变得“容易”。

数据库图看起来像地狱来的变种蜘蛛。

我认为没有绝对的规则: 有些事情是最好的前期优化,而有些则不是。

例如,我曾在一家公司工作,在那里我们接收来自卫星的数据包。每个数据包都要花很多钱,所以所有的数据都是高度优化的(即。包装)。例如,纬度/经度不是作为绝对值(floats)发送的,而是作为相对于“当前”区域的“西北”角的偏移量发送的。我们必须解压所有的数据才能使用。但我认为这不是悲观主义,这是智能优化,以降低通信成本。

另一方面,我们的软件架构师决定将未打包的数据格式化为非常可读的 XML 文档,并将其存储在数据库中(而不是将每个字段存储在相应的列中)。他们的想法是“ XML 是未来”、“磁盘空间很便宜”和“处理器很便宜”,因此没有必要对任何东西进行优化。结果是我们的16字节的数据包被转换成2kB 的文档存储在一个列中,甚至对于简单的查询,我们必须在内存中加载兆字节的 XML 文档!我们每秒收到超过50个数据包,所以你可以想象性能变得多么糟糕(顺便说一句,公司破产了)。

所以再一次,没有绝对的规则。是的,有时候过早的优化是错误的。但有时候“ CPU/磁盘空间/内存很便宜”的格言才是万恶之源。

我的一个前同事(实际上是一个 S.O.A.B.)被指派为我们的 Java ERP 构建一个新模块,该模块应该已经收集和分析了客户的数据(零售业)。他决定将每个日历/日期字段按其组件(秒、分钟、小时、天、月、年、周日、妊娠期、孕期(!))分割因为“不然我怎么查询‘每周一’呢?”

没有冒犯任何人的意思,但是我刚刚批改了一个有这个的作业(java)

import java.lang.*;

使用正则表达式分割字符串,当简单的 string.split 就足够了

天啊,我想我都看过了。这通常是一种修复性能问题的努力,这些人非常懒惰,不愿意去解决这些性能问题的原因,甚至不愿意去研究是否真的存在性能问题。在许多这样的案例中,我想知道这是否只是一个人想要尝试一种特定的技术,并且拼命地寻找一个适合他们闪亮的新锤子的钉子的案例。

下面是最近的一个例子:

数据架构师向我提出了一个详细的建议,即在一个相当大且复杂的应用程序中对关键表进行垂直分区。他想知道需要哪种类型的开发工作来适应变化。对话是这样的:

我: 你为什么要考虑这个问题? 你想解决的问题是什么?

他: 表 X 太宽了,出于性能原因,我们对它进行了分区。

我: 你为什么认为它太宽了?

他: 顾问说,一个表中的列太多了。

我: 这会影响性能吗?

他: 是的,用户报告了应用程序 XYZ 模块的间歇性减速。

我: 你怎么知道表的宽度是问题的根源?

他: 这是 XYZ 模块使用的键表,大概有200列。肯定是问题所在。

Me (Explaming) : 但是模块 XYZ 特别使用该表中的大部分列,并且它使用的列是不可预测的,因为用户将应用程序配置为显示他们想要从该表中显示的数据。很可能95% 的情况下,我们最终会将所有表重新连接在一起,这样就可以实现 伤害性能。

他: 顾问说它太宽了,我们需要改变它。

我: 这个顾问是谁?我不知道我们聘请了一位顾问,他们也根本没有和开发团队谈过。

他: 好吧,我们还没有雇佣他们。这是他们提议的一部分,但他们坚持认为我们需要重新构建这个数据库。

我: 嗯,所以销售数据库重新设计服务的顾问认为我们需要一个数据库重新设计... ..。

谈话就这样一直持续下去。之后,我又查看了一下有问题的表,确定它可以通过一些简单的标准化来缩小,而不需要特殊的分区策略。当然,一旦我调查了性能问题(以前没有报告过) ,并将其归结为两个因素,这个问题就变得毫无意义了:

  1. 少数键缺少索引 柱子。
  2. 一些流氓数据分析员 锁定键表锁定键表 (包括“太宽”的那个) 通过查询 生产数据库直接与 MSAccess.

当然,架构师仍然在推动对表进行垂直分区,以解决“太宽”的元问题。他甚至从另一个数据库顾问那里得到了一个建议,这个顾问能够确定我们需要对数据库进行重大的设计更改,而不需要查看应用程序或运行任何性能分析,从而支持了他的观点。

在每次 javascript 操作之前检查您正在操作的对象是否存在。

if (myObj) { //or its evil cousin, if (myObj != null) {
label.text = myObj.value;
// we know label exists because it has already been
// checked in a big if block somewhere at the top
}

我对这种类型的代码的问题是,似乎没有人关心它是否存在?什么都不做?不给用户反馈?

我同意 Object expected错误是恼人的,但这不是最好的解决方案。

我能想到的最糟糕的例子是我公司的一个内部数据库,其中包含所有员工的信息。它每晚都会从人力资源部获得更新,上面还有一个 ASP.NET Web 服务。许多其他应用程序使用 Web 服务来填充诸如搜索/下拉字段之类的内容。

悲观的是,开发人员认为对 Web 服务的重复调用太慢,无法进行重复的 SQL 查询。那他做了什么?应用程序启动事件读入整个数据库,并将其全部转换为内存中的对象,无限期存储,直到应用程序池被回收。这段代码非常慢,加载不到2000名员工需要15分钟。如果您在白天无意中回收了应用程序池,可能需要30分钟或更长时间,因为每个 Web 服务请求将启动多个并发重载。因为这个原因,新员工在他们的账户创建的第一天不会出现在数据库中,因此在他们创建的头几天不能访问大多数内部应用程序,无所事事。

悲观主义的第二个层面是,开发经理不想触碰它,因为害怕破坏依赖的应用程序,但是由于这样一个简单组件的糟糕设计,我们仍然有零星的关键应用程序在整个公司范围内停机。

“数据库独立”。这意味着没有存储进程,触发器等-甚至没有任何外键。

我曾经不得不尝试修改包含 Constants 类中的这些 gem 的代码

public static String COMMA_DELIMINATOR=",";
public static String COMMA_SPACE_DELIMINATOR=", ";
public static String COLIN_DELIMINATOR=":";

在应用程序的其余部分中,为了不同的目的多次使用了其中的每一个。COMMA _ DELIMINATER 在8个不同的包中使用了超过200次的代码。

我本来想提到 StringBuilder 用于小型/非循环字符串连接,但是提到了它。

将方法的变量放入私有类成员中,以防止它们“在方法运行时收集到垃圾”变量是值类型。

也许在早期快速浏览一下系统将有助于指出可能的瓶颈。

“此部分不需要快速”(归档日志) “这部分必须非常快”(接受新的连接)

然后,非常快的部分通常不需要额外的优化与肮脏的怪癖,通常体面的硬件和良好的编码部分将足够。

只需要回答一个简单的问题: “我是否可以从快速编写这部分代码中获得任何好处?”将是一个伟大的指导方针。我的意思是,使用常识优化项目的其他部分!

似乎没有人提到过分类,所以我会提到的。

有几次,我发现有人手工制作了一个冒泡排序,因为这种情况“不需要”调用已经存在的“太花哨”的快排算法。当开发人员手工制作的 Bubblesort 在他们用于测试的10行数据上工作得足够好时,他们就感到满意了。在客户添加了几千行之后,结果就不那么好了。

YAGNI 极端主义怎么样。这是一种过早的悲观主义。似乎任何时候你应用 YAGNI,然后你最终需要它,导致10倍的努力,添加它比如果你已经添加了开始。如果你创建了一个成功的程序,那么你很有可能需要它。如果您习惯于创建生命周期很快就结束的程序,那么继续实践 YAGNI,因为那样我就假设 YAGNI。

使用 Integer 字段分配 按位计算的应用程序,我们的客户端可以将其用户添加到这些应用程序访问分组。这意味着当时我们可以创建一个总共32个小组,在所有500多个客户端之间共享。

啊,但是按位比较比等于快,比连接快,对吧?

不幸的是,当我完全(相当口头地)被这段代码和它的作者吓到时,我发现这个作者是我的老板的老板。事实证明,他是个相当独裁的家伙。

附言。

我知道你在想什么,它完全应该是一个二进制字符串,对吗? :)

任何重要的优化工作,如果不是基于剖析工具的分流报告,都会让我获得很大的卧槽。

我的一些同事参与了一个现有服务器端批处理(用 C + + 编写)的“优化”项目,他们“优化”了日志类(!)使用 win32特定的代码和函数。

也许是伐木工的瓶颈。写(...) ,谁知道..。

一个同事必须检查对特定角色的页面访问权限——仅限于“管理员”。这是她写的:

.

if( CurrentUser.CurrentRole == "Role1" || CurrentUser.CurrentRole == "Role2")
{
// Access denied
}
else
{
// Access granted
}

而不是

if( !CurrentUser.CurrentRole.equals("Admin") )
{
// access denied
}

因此,无论何时向系统添加新角色,新角色都可以访问所有机密页面。


同一个同事还负责所有查询的生产表和归档表的连接。

我想我可以给你这颗宝石:

unsigned long isqrt(unsigned long value)
{
unsigned long tmp = 1, root = 0;
#define ISQRT_INNER(shift) \
{ \
if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
{ \
root += 1 << shift; \
value -= tmp; \
} \
}


// Find out how many bytes our value uses
// so we don't do any uneeded work.
if (value & 0xffff0000)
{
if ((value & 0xff000000) == 0)
tmp = 3;
else
tmp = 4;
}
else if (value & 0x0000ff00)
tmp = 2;


switch (tmp)
{
case 4:
ISQRT_INNER(15);
ISQRT_INNER(14);
ISQRT_INNER(13);
ISQRT_INNER(12);
case 3:
ISQRT_INNER(11);
ISQRT_INNER(10);
ISQRT_INNER( 9);
ISQRT_INNER( 8);
case 2:
ISQRT_INNER( 7);
ISQRT_INNER( 6);
ISQRT_INNER( 5);
ISQRT_INNER( 4);
case 1:
ISQRT_INNER( 3);
ISQRT_INNER( 2);
ISQRT_INNER( 1);
ISQRT_INNER( 0);
}
#undef ISQRT_INNER
return root;
}

因为平方根是在一个非常敏感的地方计算的,所以我的任务就是找到一种方法使它变得更快。这种小型重构将执行时间减少了三分之一(对于所使用的硬件和编译器的组合,YMMV) :

unsigned long isqrt(unsigned long value)
{
unsigned long tmp = 1, root = 0;
#define ISQRT_INNER(shift) \
{ \
if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
{ \
root += 1 << shift; \
value -= tmp; \
} \
}


ISQRT_INNER (15);
ISQRT_INNER (14);
ISQRT_INNER (13);
ISQRT_INNER (12);
ISQRT_INNER (11);
ISQRT_INNER (10);
ISQRT_INNER ( 9);
ISQRT_INNER ( 8);
ISQRT_INNER ( 7);
ISQRT_INNER ( 6);
ISQRT_INNER ( 5);
ISQRT_INNER ( 4);
ISQRT_INNER ( 3);
ISQRT_INNER ( 2);
ISQRT_INNER ( 1);
ISQRT_INNER ( 0);


#undef ISQRT_INNER
return root;
}

当然,有更快和更好的方法来做到这一点,但我认为这是一个相当简洁的悲观的例子。

编辑: 仔细想想,展开的循环实际上也是一个简单的悲观主义。通过对版本控制的深入研究,我还可以介绍重构的第二个阶段,它的表现甚至比上面提到的还要好:

unsigned long isqrt(unsigned long value)
{
unsigned long tmp = 1 << 30, root = 0;


while (tmp != 0)
{
if (value >= root + tmp) {
value -= root + tmp;
root += tmp << 1;
}
root >>= 1;
tmp >>= 2;
}


return root;
}

这是完全相同的算法,虽然实现略有不同,所以我认为它符合条件。

另一个花哨的表演魔术:)

if (!loadFromDb().isEmpty) {
resultList = loadFromDb();
// do something with results
}

对于一个额外的数据库命中的小代价,您节省了所有的时间做大约10行代码,这可能不会做很多的空列表无论如何。像这样的东西在代码中到处都是:)

我有一个同事,他试图智胜我们的 C 编译器的优化器和例程重写代码,只有他可以阅读。他最喜欢的技巧之一是改变一个可读的方法,比如(编写一些代码) :

int some_method(int input1, int input2) {
int x;
if (input1 == -1) {
return 0;
}
if (input1 == input2) {
return input1;
}
... a long expression here ...
return x;
}

变成这样:

int some_method() {
return (input == -1) ? 0 : (input1 == input2) ? input 1 :
... a long expression ...
... a long expression ...
... a long expression ...
}

也就是说,一次可读方法的第一行将成为“ return”,所有其他逻辑将被深度嵌套的三元表达式替换。当您试图讨论这是如何不可维护时,他会指出这样一个事实,即他的方法的汇编输出比汇编指令短三到四条汇编指令。它不一定是任何 再快点,但它总是一个 很小位短。这是一个嵌入式系统,内存使用有时确实很重要,但是可以进行比这更容易的优化,从而使代码可读。

然后,在这之后,出于某种原因,他认为 ptr->structElement太难读了,所以他开始把所有这些都改成 (*ptr).structElement,理论上它也更容易读和更快。

将可读的代码转换成不可读的代码,最多可以获得1% 的改进,有时甚至会减慢代码的速度。

这并不完全符合我的问题,但我还是要提到它,一个警示故事。我当时正在开发一个运行缓慢的分布式应用程序,飞到华盛顿参加一个主要是为了解决这个问题的会议。项目负责人开始概述旨在解决延误的重新架构。我自告奋勇说,我已经在周末进行了一些测量,将瓶颈隔离到一个单一的方法。原来在本地查找中缺少一条记录,导致应用程序必须在每个事务上转到远程服务器。通过将记录添加回本地存储,消除了延迟-问题解决了。注意,重新架构不会修复这个问题。

在一个老项目中,我们继承了一些(其他方面都很优秀的)嵌入式系统程序员,他们有大量的 Z-8000经验。

我们的新环境是32位 Sparc Solaris。

其中一个家伙去把所有的整型变成短型来加快我们的代码,因为从 RAM 抓取16位比抓取32位更快。

我不得不编写一个演示程序来展示在32位系统上获取32位值比获取16位值更快,并解释说为了获取16位值,CPU 必须进行32位宽内存访问,然后屏蔽或移动16位值不需要的位。

许多程序员不知道或不想知道 SQL,所以他们找到了一些“技巧”来避免真正使用 SQL,这样他们就可以将数据放入数组中。数组让一些人感到快乐。(我喜欢游标和数组。可口可乐和百事可乐。)我在一些面向对象程序员的代码中发现了这两个代码块,他们抱怨关系数据库速度太慢。(答案不是更多的内存或更多的处理器。)

本例中的表是一个巨大的表,uniqueid _ coll 是唯一的 id 或唯一的行。

将此数据加载到 arrayX 中(因为数组必须更快)

Select uniqueid_col, col2, col3
from super_big_tbl

(假代码)


Loop
arrayX.next_record
if uniqueid_col = '829-39-3984'
return col2
end if
end loop

(我的答案在底部。)

下面这个简单的错误我也看到了。这个想法是你永远不会得到一个复制品:

Select uniqueid_col, col2, col3
from super_big_tbl
group by uniqueid_col, col2, col3
having uniqueid_col = '829-39-3984'

正确的语法应该是

Select uniqueid_col, col2, col3
from super_big_tbl
where uniqueid_col = '829-39-3984'

我知道这个帖子写得很晚,但我最近看到了这个:

bool isFinished = GetIsFinished();


switch (isFinished)
{
case true:
DoFinish();
break;


case false:
DoNextStep();
break;


default:
DoNextStep();
}

以防布尔值有额外的值。

这并不是过早的优化——但肯定是误入歧途——这是从 BBC 网站上一篇讨论 Windows 7的文章中读到的。

Curran 先生说,微软的 Windows 团队已经仔细研究了操作系统的每一个方面来进行改进。 “我们可以通过轻微调整 WAV 文件关闭音乐,将关闭时间缩短400毫秒。

现在,我还没有尝试过 Windows 7,所以我可能是错的,但我愿意打赌,有其他问题在那里比多久才关闭更重要。毕竟,一旦我看到“关闭 Windows”的信息,显示器就会关闭,我就会离开——这400毫秒对我有什么好处呢?

我一次又一次在内部软件中遇到的最大的一次:

由于“可移植性”的原因,没有使用 DBMS 的特性,因为“我们可能希望稍后切换到另一个供应商”。

看我的嘴唇。对于任何内部工作: 这将不会发生!

我不认为悲观是罕见的。根据我从事性能调优的经验,很多糟糕的性能是由于“良好的编程实践”以“效率”的名义进行辩护造成的。例子:

  • 地图集或“字典”
    它们通常使用某种散列编码,因此它们将具有 O (1)性能,但只有在填充了比通常使用的多得多的项时才能实现收支平衡。

  • 迭代器
    当很少检查它们是否真的被优化为有效的内联代码时,这些代码可能被优化为有效的内联代码。

  • 通知和事件处理作为保持数据一致性的一种方式
    由于很少对数据结构进行规范化,因此必须管理不一致性,通知是常用的方法,因为通知应该“立即”解决问题。 然而,即时性和效率之间有很大的区别。 另外,在获取或设置时,鼓励“属性”深入到数据结构中,以保持其一致性。这些“短链”方法会导致大量的计算浪费。“长链接”方法,比如周期性地循环数据结构以“修复”它,可能不那么“立即”,但是效率高得多。

例子

我有一个故意的... 我曾经实现了通过回溯排序... 只是作为一个概念证明;)不用说,它的性能是可怕的。

var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();

这是我见过的最好的 StringBuilder 应用。

POBI ——显然是有意的悲观?

我在90年代的同事已经厌倦了被首席执行官踢屁股,仅仅因为首席执行官花了第一天的每个 ERP 软件(一个定制的)发布定位性能问题在新的功能。即使新的功能占用了几十亿字节,使不可能的事情成为可能,他总是能找到一些细节,甚至是看似重大的问题来抱怨。他相信自己对编程了如指掌,并通过痛扁程序员来获得乐趣。

由于批评的无能(他是 CEO,而不是 IT 人员) ,我的同事从来没有做对过。如果你没有性能问题,你不能消除它..。

直到有一个版本,他在新代码中放入了大量的延迟(200)函数调用(是 Delphi)。 上线后仅仅20分钟,他就被命令到首席执行官的办公室亲自接受他逾期未还的侮辱。

到目前为止,唯一不同寻常的是,当他回来时,他是哑巴,微笑着,开玩笑着,出去吃一两个巨无霸,而他通常会踢桌子,抨击 CEO 和公司,然后在剩下的时间里被拒之门外。

很自然,我的同事现在在他的办公桌前休息了一两天,提高了他在《雷神之锤》中的瞄准技巧——然后在第二天或第三天,他删除了延迟呼叫,重建并发布了一个“紧急补丁”,他散布消息说,他花了两天一夜的时间来修复性能漏洞。

这是第一次(也是唯一一次)邪恶的首席执行官对他说“干得好!”这才是最重要的,不是吗?

这是真正的 POBI。

但这也是一种社会过程优化,所以100% 没问题。

我觉得。

作为一名成熟的开发人员,我的第一份工作之一是接管一个遇到伸缩问题的程序的项目。它在小型数据集上工作得相当好,但是在给定大量数据时会完全崩溃。

当我深入研究的时候,我发现最初的程序员试图通过并行化分析来加快速度——为每个额外的数据源启动一个新的线程。然而,他犯了一个错误,所有线程都需要一个共享资源,而这个资源是死锁的。当然,并发的所有好处都消失了。此外,它使大多数系统崩溃,启动100多个线程,只有一个线程被锁定。我的强大的开发机器是一个例外,因为它在大约6小时内处理了150个源数据集。

为了解决这个问题,我删除了多线程组件,清理了 I/O。没有其他变化,150个源数据集的执行时间在我的机器上降到了10分钟以下,在普通公司机器上从无限降到了半小时以下。

从数据库中删除了所有外键约束,否则将会出现很多错误。

我部门的一个人曾经写过一个字符串类,一个类似于 CString的接口,但是没有 Windows 的依赖性。

他们所做的一个“优化”是 没有分配任何超过必要的内存。显然没有意识到像 std::string这样的类之所以分配多余的内存,是因为 +=操作序列可以在 O (n)时间内运行。

相反,每一通 +=电话强制重新分配,这使得重复的附加变成了 O (n2) 画家施莱米尔的算法

我多年前作为顾问访问过的一家公司编写了一个类似于下面这样的排序函数:

procedure sort(string[] values, string direction)
begin
while not sorted do
begin
for every value in values
begin
if direction="Ascending" then
begin
... swap values in ascending order
end
else if direction="Descending" then
begin
... swap values in descending order
end
end;
end;
end;

这是一个在内部循环中使用 方向字符串比较的冒泡排序算法(其本身效率很低) !我简直不敢相信自己的眼睛,并解释说,是的,我可能在这里做一些速度改进(他们毕竟是我的客户,所以我不得不委婉地说,这是我见过的最不理想的代码:)

while true; do echo 3 > /proc/sys/vm/drop_caches; sleep 3600; done

这导致内核花费时间清除磁盘缓存,一旦成功,所有内容都会慢慢运行,直到缓存被重新填充。由于误解磁盘缓存阻止内存可供应用程序使用造成的。