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

dsi*_*cha 145 optimization performance

我们都知道,过早优化是所有邪恶的根源,因为它会导致不可读/不可维护的代码.更糟糕的是悲观化,当有人实施"优化",因为他们认为它会更快,但它最终会变慢,而且变得越来越慢,不可维护等等.你看到的最荒谬的例子是什么? ?

For*_*ker 206

我认为"过早优化是所有邪恶的根源"这句话是方式,过度使用.对于许多项目而言,它已成为在项目后期才考虑绩效的借口.

这句话通常是人们避免工作的拐点.当人们真正说出"哎呀,我们真的没有想到这一点并且现在没有时间处理它"时,我看到了这句话.

我看到了更多"荒谬"的愚蠢性能问题的例子,而不是由于"悲观化"引入的问题的例子

  • 在程序启动期间读取相同的注册表项数千(或十万)次.
  • 加载相同的DLL数百或数千次
  • 通过不必要地保留文件的完整路径来浪费大量字节的内存
  • 不组织数据结构,因此它们占用的内存超出了他们的需要
  • 调整存储文件名或路径到MAX_PATH的所有字符串
  • 对有事件,回调或其他通知机制的事件进行无偿轮询

我认为更好的说法是:"没有测量和理解的优化根本不是优化 - 它只是随机变化".

良好的性能工作非常耗时 - 通常更多的是功能或组件本身的开发.

  • "早产"是该引言的关键词.你将它改为"没有测量和理解的优化"似乎并没有改变一点意义.这正是Knuth的意思. (46认同)
  • @Foredecker:马上.太多人忘记了上下文,这使得这句话坚定地反对*微观 - 优化.在实现问题之前分析问题以选择正确的算法并不是为时过早,但通常会引用引用来证明最懒惰,最低效的解决方案. (13认同)
  • -1:"优化"和正确设计之间存在差异.对于那些无法分辨的人来说,一个好的经验法则是"优化"会使代码更难以阅读,但更快或更有效.更好的设计将使代码更容易阅读(或至少不会更糟)*和*更高效. (12认同)
  • 这实际上取决于个别情况,有些情况下过早优化成为问题,而不适当的优化计划成为问题 (5认同)
  • 如果它过度使用,那么在SO上提问的人群就会对异常值进行大量加权.:d (5认同)
  • @Foredecker:我明白你对过度使用这句话的意思.如果你大声说出来,这可能表明你需要衡量你的代码.:) (2认同)

dkr*_*etz 114

数据库是悲观化的游戏.

收藏夹包括:

  • 将表拆分为多个(按日期范围,字母范围等),因为它"太大".
  • 为退役记录创建归档表,但继续使用生产表UNION它.
  • 通过(部门/客户/产品/等)复制整个数据库
  • 抵制向索引添加列,因为它太大了.
  • 创建大量汇总表,因为从原始数据重新计算太慢.
  • 使用子字段创建列以节省空间.
  • 非规范化为字段作为数组.

这是我的头脑.

  • 我补充说:将数据库分成几个不同的数据库(客户交流,客户df等) (3认同)
  • 是的,我认识一个为美国一家大型石油公司工作的人,他们几乎所有的表都有一个相关的存档表,而且大多数查询都选择了UNION这些表的视图.性能如您所料! (2认同)

Min*_*ark 87

我认为没有绝对的规则:有些东西最好在前期优化,有些则不是.

例如,我在一家公司工作,我们收到了来自卫星的数据包.每个数据包都需要花费很多钱,因此所有数据都经过了高度优化(即打包).例如,纬度/经度不是作为绝对值(浮动)发送的,而是作为相对于"当前"区域的"西北"角的偏移.在使用之前我们必须解压缩所有数据.但我认为这不是悲观,而是智能优化以降低通信成本.

另一方面,我们的软件架构师决定将解压缩的数据格式化为一个非常易读的XML文档,并将其存储在我们的数据库中(而不是将每个字段存储在相应的列中).他们的想法是"XML是未来","磁盘空间便宜","处理器便宜",所以没有必要优化任何东西.结果是我们的16字节数据包变成了存储在一列中的2kB文档,即使是简单的查询,我们也必须在内存中加载兆字节的XML文档!我们每秒收到超过50个数据包,所以你可以想象这个表现有多糟糕(BTW,公司破产).

再说一次,没有绝对的规则.是的,有时过早优化是一个错误.但有时候"cpu /磁盘空间/内存便宜"的座右铭是所有邪恶的真正根源.

  • 我同意"cpu /磁盘空间/内存便宜"是所有邪恶的真正根源.+1 (37认同)
  • @ksuralta:"Cpu /磁盘空间/内存便宜"是避免思考的一个方便借口.避免思想是所有邪恶的想象根源. (19认同)
  • 我听说过XML也是如此.另一家油轮公司. (5认同)

Mar*_*son 81

在一个旧项目中,我们继承了一些(非常优秀的)具有大量Z-8000经验的嵌入式系统程序员.

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

其中一个人把所有的注册改为短路以加速我们的代码,因为从RAM中获取16位比抓取32位更快.

我不得不编写一个演示程序,以显示在32位系统上获取32位值比抓取16位值更快,并解释为了获取16位值,CPU必须使32位宽存储器访问然后屏蔽掉或移位16位值不需要的位.

  • 嘿,你在哪里学习数学?具有1个高速缓存/ RAM访问的2个指令明显比具有1个高速缓存/ RAM访问的1个指令快! (16认同)
  • @RazorStorm在带宽和缓存更为宝贵的更高版本的计算机上,情况恰恰相反。位掩码/移位很便宜,但是您希望尽可能多地容纳在高速缓存中,同时还要使带宽最小化。 (2认同)

Joh*_*nFx 75

哦,主啊,我想我已经看过他们了.通常情况下,这是一个努力解决性能问题的人,他们过于懒惰,无法解决这些性能问题的原因,甚至研究是否确实存在性能问题.在许多这种情况下,我想知道这不仅仅是那个想要尝试特定技术并且拼命寻找适合他们闪亮的新锤子的钉子的情况.

这是最近的一个例子:

数据架构师向我提出了一个精心设计的建议,即在一个相当大而复杂的应用程序中垂直分区密钥表.他想知道为适应变化需要哪种类型的开发工作.谈话是这样的:

我:你为什么这么想?你想解决的问题是什么?

他:表X太宽,我们因性能原因对其进行分区.

我:是什么让你觉得它太宽了?

他:顾问说,在一张桌子上有太多的栏目.

我:这会影响性能吗?

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

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

他:这是XYZ模块使用的关键表,它就像200列.一定是问题所在.

我(解释):但模块XYZ特别使用该表中的大多数列,并且它使用的列是不可预测的,因为用户配置应用程序以显示他们想要从该表显示的数据.很可能95%的时间我们最终将所有桌子连接在一起,这会损害性能.

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

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

他:嗯,我们还没有雇用他们.这是他们提供的提案的一部分,但他们坚持认为我们需要重新构建这个数据库.

我:嗯嗯.因此,销售数据库重新设计服务的顾问认为我们需要重新设计数据库....

谈话继续这样下去.之后,我又看了一下有问题的表,并确定它可能会通过一些简单的规范化来缩小,而不需要异乎寻常的分区策略.当我调查性能问题(之前未报告过)并将其跟踪到两个因素时,这当然是一个有争议的问题:

  1. 缺少几个关键列的索引.
  2. 一些流氓数据分析师通过直接使用MSAccess查询生产数据库来定期锁定密钥表(包括"太宽"的表).

当然,架构师仍然在推动桌子的垂直分区,这个分区挂在"太宽"的元问题上.他甚至通过获得另一位数据库顾问的提议来支持他的案例,该顾问能够确定我们需要对数据库进行重大设计更改,而无需查看应用程序或运行任何性能分析.

  • "我听说紫红色的RAM最多" (35认同)
  • 我们有一份类似的工作,但它已经被废弃了。公平地说,Access 不是问题,它只是让新手可以轻松创建/运行性能不佳的查询。 (2认同)

小智 58

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

完全悲观!

  • +1因为我的显示器还需要清洁;-) (17认同)
  • 也许他们试图设置磁通电容器 (10认同)
  • 因此,基本上所涉及的唯一新原理是由导体和磁通的相对运动产生的功率,而不是由磁阻和电容实验的模态相互作用产生的? (6认同)

Bil*_*ard 53

我承认,没有什么是惊天动地的,但是我发现人们使用StringBuffer来连接Java中循环之外的字符串.这很像转弯一样简单

String msg = "Count = " + count + " of " + total + ".";
Run Code Online (Sandbox Code Playgroud)

StringBuffer sb = new StringBuffer("Count = ");
sb.append(count);
sb.append(" of ");
sb.append(total);
sb.append(".");
String msg = sb.toString();
Run Code Online (Sandbox Code Playgroud)

过去常常在循环中使用该技术,因为它的速度要快得多.问题是,StringBuffer是同步的,所以如果你只连接几个字符串,实际上会有额外的开销.(更不用说这种差异在这个规模上绝对是微不足道了.)关于这种做法的另外两点:

  1. StringBuilder是不同步的,因此在无法从多个线程调用代码的情况下,应优先使用StringBuffer.
  2. 无论如何,现代Java编译器会将可读的字符串串联转换为优化的字节码.

  • @ MetroidFan2002:第二个例子中的字符串文字也是对象.正如我在答案中所说,这种差异在这个范围内是微不足道的. (4认同)
  • 第一:为什么不使用至少Java 5?第二:是的,你可以.在第一个例子中你怎么能算到5,而不是第二个例子?它使用与第一个相同的字符串文字.编写可读代码,让编译器决定何时在幕后使用StringBuffer. (3认同)
  • @Eric:String msg ="Count ="+ count +"of"+ total +"."; 经常用Java编译成String msg = new StringBuffer().append("Count").append(count).append("of").append(total).append(".").toString(); ......这正是第二个例子所做的. (3认同)
  • 瓦格纳先生,事情是你必须看看所有这些方法调用,而不是编译器.你必须写下它们并在以后理解它们.无论如何编译器都是这样做的.因此,在这种情况下,可读性更为重要. (3认同)

Dus*_*sda 47

我曾经看过一个使用'Root'表的MSSQL数据库.Root表有四列:GUID(uniqueidentifier),ID(int),LastModDate(datetime)和CreateDate(datetime).数据库中的所有表都是Root表的外键.每当在db 中的任何表中创建新行时,您必须使用几个存储过程在Root表中插入一个条目,然后才能到达您关心的实际表(而不是数据库执行的工作你有几个触发器简单的触发器).

这造成了一堆无用的无意中听到和头痛,需要在它上面写任何东西来使用sprocs(并且消除了我将LINQ引入公司的希望.这是可能的,但不值得头疼),并且最重要的是没有'甚至完成它应该做的事情.

选择此路径的开发人员在假设这节省了大量空间的情况下为其辩护,因为我们没有在表本身上使用Guids(但是...不是我们制作的每一行在Root表中生成的GUID?) ,以某种方式改进了性能,并使审计对数据库的更改变得"容易".

哦,数据库图看起来像是来自地狱的突变蜘蛛.


The*_*One 42

POBI怎么样 - 意图明显的悲观化?

90年代我的同事厌倦了被首席执行官罢工,因为首席执行官在每个ERP软件(定制版)发布的第一天花费了新功能中的定位性能问题.即使新的功能已经达到千兆字节并使不可能成为可能,他总是会发现一些细节,甚至是看似重大的问题.他相信对编程有很多了解,并通过踢程序员驴来解决问题.

由于批评的无能(他是首席执行官,而不是IT人),我的同事从来没有设法做到这一点.如果你没有性能问题,你就无法消除它......

直到一个版本,他把很多Delay(200)函数调用(它是Delphi)放入新代码中.上线后仅用了20分钟,他就被命令出现在首席执行官办公室,以便亲自接受他过期的侮辱.

到目前为止,只有不寻常的事情是我的同事们回来时微笑,开玩笑,出去买一两个BigMac,而他通常会踢桌子,对首席执行官和公司大加抨击,剩下的时间都会变成死亡.

当然,我的同事现在在他的办公桌休息了一两天,提高了他在Quake中的瞄准技能 - 然后在第二天或第三天他删除了延迟电话,重建并发布了一个"紧急补丁",他传播了这个词他花了2天1夜来修复表演漏洞.

这是邪恶的首席执行官说"干得好"的第一个(也是唯一的)时间.给他.这一切都很重要,对吗?

这是真正的POBI.

但它也是一种社交流程优化,所以100%可以.

我认为.

  • 我记得有人写过一个数据处理应用程序,这个应用程序在不同级别上销售,其中"Lite"每秒只能扼杀几个数据集,"超级数据"版本数千.唯一的源代码区别是Sleep(N). (10认同)

chr*_*ris 32

"数据库独立性".这意味着没有存储过程,触发器等 - 甚至没有任何外键.

  • 这种"独立性"在你远远高于数据库的意义上,你忘记了什么数据?对数据库进行不必要的抽象"避免迁移痛苦"是一件令人讨厌的事情.你不需要它. (8认同)
  • 差不多.建筑宇航员在工作.我一直在构建Web应用程序,因为有一个Web,并且在那段时间里我从未真正从一个数据库平台转移到另一个数据库平台. (8认同)
  • harpo,这是一个不同的情况 - 在这种情况下这是一个要求.我在谈论它不是一个要求,但AA决定它"可能"在某个时候. (6认同)
  • 我确信它会发生,但如果你围绕这种可能性设计你的架构,那么你是一个白痴就很难了. (5认同)
  • @All:数据库独立可能会花费你,是的,但是我们的产品是在出价选择数据库供应商的环境中运行的,我们基本上必须继续使用.一些开发人员无法获得垂直集成软件堆栈的奢侈品,并且必须尽力做到. (3认同)

小智 31

var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();
Run Code Online (Sandbox Code Playgroud)

最好的使用我见过的StringBuilder.

  • 谈论"概念不清楚"!哇! (9认同)
  • 凉."我的主管说如果我想连接字符串,我必须使用StringBuilder类.这就是我所做的.那么什么是错的?" 大声笑... (3认同)

Che*_*ian 26

当一个简单的string.split足够时,使用正则表达式来分割字符串

  • 但是在Java String.Split使用正则表达式! (25认同)
  • -1:如果你习惯了正则表达式,那么编写它是很自然的,而不是习惯于1001语言内部的字符串操纵器. (6认同)
  • @David Crawshaw:追捕微优化机会浪费了人类的时间;*编写*代码时使用,使用最不复杂的解决方案. (5认同)
  • 但故意追捕用于字符串拆分的正则表达式并将其替换为"简单"拆分函数听起来像是悲观化的完美示例.正则表达式库足够快. (2认同)

Dam*_*isa 26

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

bool isFinished = GetIsFinished();

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

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}
Run Code Online (Sandbox Code Playgroud)

你知道吗,以防布尔值有一些额外的值......

  • 真的,假的FileNotFound ofcourse (22认同)
  • @Oorang...你为什么要把它作为开关呢?它是一个布尔值——一个 if/else 就是所需要的。 (2认同)
  • 它是Nullable <Boolean> ... :) (2认同)

spo*_*son 25

最糟糕的例子我能想到的是我公司的一个内部数据库,其中包含所有员工的信息.它从HR获得每晚更新,并在顶部提供ASP.NET Web服务.许多其他应用程序使用Web服务来填充搜索/下拉字段等内容.

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

第二个悲观主义是开发经理不想触及它,因为害怕破坏依赖的应用程序,但是由于这样一个简单组件的设计很差,我们仍然在公司范围内零星地停止关键应用程序.

  • 最好的管理 - "不,让我们不要一次性地花80个程序员来修复这个应用程序,这太贵了.让我们保留它,所以它的错误每个月可以耗费200多个用户小时,加上每月10个程序员小时'保养'." AAAAAAAAAUGH! (28认同)

Dan*_*lau 25

似乎没有人提到排序,所以我会.

几个不同的时间,我发现有人手工制作了一个bubort,因为情况"不需要"调用已经存在的"过于花哨"的快速排序算法.当他们的手工制作的Bubbleort在他们用于测试的十行数据上运行良好时,开发人员感到满意.在客户添加了几千行之后,它并没有完全消失.

  • 当我确定通常n = 2时,我自己做了一次.后来的产品增强功能使我的前提无效,代码被替换为PDQ. (2认同)
  • 是的,但是现在每个人写一些*algorythm*很好.) (2认同)

Dou*_*rch 20

我曾经在一个充满这样代码的应用程序上工作过:

 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 }
Run Code Online (Sandbox Code Playgroud)

只需删除found,最后返回null,并将第六行更改为:

            return curr;
Run Code Online (Sandbox Code Playgroud)

应用程序性能翻倍.

  • 这里的返回曲线产生明显不同的行为.当你返回curr时,你最终得到第一个匹配,你粘贴的代码返回最后一个匹配. (12认同)
  • 那不是"悲观化",是吗?这只是一种等待发生的优化. (7认同)
  • @SoapBox:你是对的.@Dour High Arch:性能的提升与单一返回规则无关,因为flolo表示将循环条件追逐到(curr &&!found)会产生相同的效果.进入出口的GOTO非常糟糕,并且违背了单一返回指南的目的. (2认同)
  • 大家好评.在这种情况下,每个ID应该只有一个元组. (2认同)

Kit*_*YMG 20

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

public static String COMMA_DELIMINATOR=",";
public static String COMMA_SPACE_DELIMINATOR=", ";
public static String COLIN_DELIMINATOR=":";
Run Code Online (Sandbox Code Playgroud)

为了不同的目的,这些中的每一个在应用的其余部分中被多次使用.COMMA_DELIMINATOR在8个不同的软件包中占用了200多个代码.

  • Deliminator III:逗号的崛起 (53认同)
  • 另一方面,我很高兴看到科林斯正确划界.每个值得他盐的程序员都知道,如果有一件事你绝对必须正确分开,那就是该死的科林斯. (33认同)
  • 还 - Deliminator?我以为它拼写为'分隔符'.Deliminator听起来像90年代中期的一部糟糕的电影,不知何故获得了3部电影........... (12认同)
  • 做一个正确的查找和替换并不容易.因为每个用于不同的目的.任何优秀的程序员至少会做这样的事情:COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ...等 (2认同)

Pet*_*uer 19

我在内部软件中一次又一次地遇到的最重要的一次:

出于"可移植性"的原因不使用DBMS的功能,因为"我们可能希望以后切换到其他供应商".

读我的唇语.对于任何内部工作:它不会发生!

  • 它确实发生了.MySQL - > postgresql,所以我们没有*丢失任何东西. (9认同)

Edd*_*die 17

我有一个同事试图战胜我们的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;
}
Run Code Online (Sandbox Code Playgroud)

进入这个:

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

也就是说,一次可读方法的第一行将变为" return",所有其他逻辑将被深层嵌套的三元表达式替换.当你试图争论这是如何不可维护时,他会指出他的方法的汇编输出是三或四个汇编指令更短的事实.这不一定任何更快,但它始终是一个小小的有点短.这是一个嵌入式系统,其中内存使用偶尔会起作用,但是可以做出比这更容易的优化,这将使代码可读.

然后,在此之后,由于某种原因,他认为这ptr->structElement太难以理解,所以他开始将所有这些改变为(*ptr).structElement理论上它更具可读性和更快性.

将可读代码转换为不可读代码,最多可提高1%,有时实际上代码更慢.

  • @Michael:我不会,除非有测量表明它“更快”,而不仅仅是“更短”。 (2认同)
  • 这里的问题是采用整个函数并将其替换为单个语句(返回),从而用嵌套的三元数替换整个函数的所有逻辑。如果看到它,您将理解。这不是宗教上的“我讨厌三元运算符”。我不是说要在函数中使用单个`if`并将其替换为三进制。很好,而且通常更具可读性。我正在谈论用单个return语句和嵌套三元数替换整个30+行方法。没有人认为新代码更具可读性,但是一位开发人员则认为它更快。 (2认同)

Jef*_*ake 15

在我作为一名成熟的开发人员的第一份工作中,我接手了一个项目,该项目正在遭遇扩展问题.它在小数据集上运行得相当好,但在给定大量数据时会完全崩溃.

当我进入时,我发现原始程序员试图通过并行化分析来加快速度 - 为每个额外的数据源启动一个新线程.然而,他犯了一个错误,因为所有线程都需要一个共享资源,他们在这个资源上陷入僵局.当然,并发的所有好处都消失了.此外,它破坏了大多数系统以启动100多个线程,只有其中一个线程锁定.我的强劲开发机器是一个例外,因为它在大约6小时内通过150源数据集进行搅拌.

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


Chr*_*fer 14

我想我可以提供这个宝石:

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;
}
Run Code Online (Sandbox Code Playgroud)

由于平方根在一个非常敏感的地方计算,我的任务是寻找一种方法来使它更快.这种小型重构将执行时间减少了三分之一(对于所使用的硬件和编译器的组合,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;
}
Run Code Online (Sandbox Code Playgroud)

当然,有更快更好的方法来做到这一点,但我认为这是一个非常巧妙的悲观化例子.

编辑:想想看,展开的循环实际上也是一个整洁的悲观.通过版本控制挖掘,我也可以展示重构的第二阶段,其表现甚至比上面更好:

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;
}
Run Code Online (Sandbox Code Playgroud)

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


Gor*_*ley 11

这可能是你所追求的更高水平,但修复它(如果你被允许)也会带来更高的痛苦程度:

坚持手动滚动对象关系管理器/数据访问层,而不是使用已建立的,经过测试的成熟库之一(即使在他们被指出之后).

  • 如果没有一个已经建立的那些具有你需要的关键功能,那么手动滚动就是你唯一的选择. (4认同)
  • 实际上,鉴于上面的一些评论,还有一些更多的观点:另一种悲观情绪是尝试让ORM完成所有事情.它通常适用于95%以上的病例.对于最终的5%,更容易退出手工制作的持久性代码/直接存储过程调用等,以实现性能,简单性或两者兼而有之. (3认同)

Gug*_*uge 10

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


Che*_*try 8

在每次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
}
Run Code Online (Sandbox Code Playgroud)

我对这类代码的问题是,如果它不存在,似乎没有人关心它是什么?什么都不做?不要向用户提供反馈?

我同意Object expected错误是令人讨厌的,但这不是最好的解决方案.


Jac*_*ble 8

这不完全适合这个问题,但无论如何我都会提到一个警示故事.我正在研究一个运行缓慢的分布式应用程序,然后飞到DC参加主要旨在解决问题的会议.项目负责人开始概述旨在解决延迟的重新架构.我自告奋勇说我周末采取了一些测量方法,将瓶颈分离为单一方法.事实证明,本地查找中缺少记录,导致应用程序必须在每次事务处都转到远程服务器.通过将记录添加回本地商店,延迟被消除 - 问题得到解决.请注意,重新架构不会解决问题.


Dun*_*unk 7

YAGNI极端主义怎么样?这是一种过早悲观化的形式.似乎你在任何时候申请YAGNI,然后你最终需要它,导致添加它的努力是你在开始时添加它的10倍.如果你创建了一个成功的程序,那么你可能需要它.如果你习惯于创建生命快速耗尽的程序,那么继续练习YAGNI因为那时我想YAGNI.

  • 谢谢,我厌倦了这些蹩脚的"极端编程"缩略语以及人们如何使用它们来支持懒惰的,适得其反的做法. (3认同)

bel*_*bob 6

不完全是过早的优化 - 但肯定是错误的 - 这是在BBC网站上从一篇讨论Windows 7的文章中读到的.

Curran先生表示,Microsoft Windows团队一直在研究操作系统的各个方面以进行改进."通过略微修剪WAV文件关闭音乐,我们能够在关机时间内缩短400毫秒.

现在,我还没有尝试过Windows 7,所以我可能错了,但是我愿意打赌,其中有一些问题比关闭需要多长时间更重要.毕竟,一旦我看到"关闭Windows"消息,显示器就会关闭,我正在走开 - 400毫秒的时间对我有什么好处?


dan*_*n04 6

我部门的某个人曾经写过一个字符串类.一个接口CString,但没有Windows依赖.

他们所做的一个"优化"是分配超过必要的内存.显然没有意识到类这样的原因std::string会分配多余的内存,因此一系列+=操作可以在O(n)时间内运行.

相反,每一次+=调用都强制重新分配,而这又重复出现在一个O(n²)施莱米尔画家的算法中.


Jor*_*ril 5

我的一位前同事(实际上是一名员工)被指派为我们的Java ERP构建一个新模块,该模块应该收集并分析客户的数据(零售行业).他决定在其组件中分割每个日历/日期时间字段(秒,分钟,小时,日,月,年,星期几,bimester,三个月(!)),因为"我还会如何查询'每个星期一'?"

  • 这不是一个不成熟的优化,他认为他需要这样做是为了正确 (3认同)