对话#23:产生真正的hash对象

类别:VC语言 点击:0 评论:0 推荐:
[声明]:本英文资料源自于Herb Sutter 创建的“Conversation”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter、Jim Hyslop同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。

文章来源:http://www.gotw.ca
版权归属:Herb Sutter and Jim Hyslop
译    者:csdnfriend(CSDN)

对话#23:产生真正的hash对象

    当我们着陆时, 我有点晕, 但是自由了。在返回到地球上,滞留了一个多月后, 我们被释放了,那时所有的紧张都消除了。对外星技术的控制将由一个新组织监视,并由来自于全球所有主要派别的民兵进行保护。

    我们正在等电梯。珍妮咕哝着:“我好困。 好象加速永远不会停止。”

   “我们以前就习惯了,我们将再次习惯。” 我坐回到椅子里,站这么长时间太辛苦了。“多长时间这个该死的东西会下来?”

    “我认为她们在优化电梯计划以得到最小平均等待时间方面犯了一个错误。” 珍妮也坐下来,“不幸的是,偏偏让我们碰到了最糟的等待时间。”

    “不过我还是很高兴回到家。”

    我们之间安静了一会儿, 当我们在人群中等待时, 我回想起过去两年的考验,我知道珍妮也在想它们。 “我们的确把事情弄得一团糟(hash),”最后她说。我疲惫的笑了。 “哦,” 她笑着,“那使你想起了一个故事, 是吗? 好的,继续,有些什么,不要吊我的胃口。”

    我就开始讲那个故事,正好我可以继续讲给新年里要来的其他人,在我们的新生活期间...

    就是电梯和"hash"这个词让我想起了那天, 那时我正从事我的第一份编程工作,多年以前。

              - - - - - - - - - - - - - - - - - - - - - - - - -

    “哦, 你听到鲍勃在干什么?”温迪边说边笑,眼睛里闪着愉快的光芒,“新老板对标准很看重. 因此鲍勃觉得时光要变好了, 在最近的代码中他要做一个金发的遵循标准的男孩."

    “但那是一件好事吗?”我问,迷惑。

    温迪笑着走了。 一句 “等着看他的代码吧...”飘了过来,当她转到拐角走进她的工作间后,我耸耸肩,回去工作。

第二天,第一次爆发开始了,声音好象是从鲍勃的隔间里传过来,我走过去,看看发生了什么。我发现鲍勃对显示器怒目而视,嘴里骂骂咧咧。旁边放着一杯被遗忘了的咖啡,已经冷了,就在他肘边。

   “怎么了?”我天真的问。我把自己放在了一个危险的环境了, 但是我想潜在的幽默价值值得我冒险。

    鲍勃继续骂了几句, 我想我听到了几个词 "标准" 和"愚蠢" 和 "慢"。“威廉姆斯一点也不明白怎样写出有效率的代码,”他抱怨着,现在听的更清楚了,“最初他要我用标准里的东西, 现在看这些。”

    我从鲍勃的肩膀看过去,看见一封从QA部门来的email。我很快扫了一眼,那封信里抱怨鲍勃前天交上去代码的执行速度。“他们抱怨什么?”我问。

    “速度,”鲍勃说, 挠着自己。 “速度。那个吹毛求疵的测试部门一点也不在意我的代码能正常工作,只是说它不够快,不能达到规格的要求。”

    “规格是什么?”

哦,平均常规查找时间。我告诉他们那些愚蠢的STL容器达不到要求。” 在另一个窗口中,我看到了一部分代码:

typedef map<string, Employee> SearchIndex; // ... SearchIndex::iterator result = ind.find( empname );

    “有多少个这样map?”我问。

    “哦, 许多,”Bob含糊的说。

    啪! 突然合上书的声音把我们吓了一跳。Guru又一次悄悄的出现在我们的背后,严厉地看了一眼。她指着那杯冷咖啡, 然后是鲍勃房间的内部装置,问道:“你怎么这样吃东西?”我忍住笑,鲍勃脸红了,吸了一口气, 但是Guru在鲍勃做出反应前继续问:“这个性能问题并不是标准摸板库的问题。是那个用了错的容器的程序员的错。为什么你要用map?”

    “因为那就是用于查找的该死的标准容器!”鲍勃愤怒地发牢骚,“为什么你想我把我的头发都拔光了, 甜心? 让那些设计STL的呆在象牙塔里的人知道他们一点都不明白我们在现实世界里需要什么。这种有内部树结构的容器总是使用对数查找, 那对我们来说太慢了。”

    “而且,在这种情况下,  只要平均查找时间保持稳定,偶尔某个查找时间长点又有什么关系呢?”Guru甜甜地问道。她的风度告诉我她已经知道答案了。

鲍勃也明白这点:”不, 我们只关心能表现成千上万次查找的压力测试基准。你应该知道,规格是你制订的!”

    “那是,”她说,“是我写的。你需要的解决方法只是在你的代码中加上5个字符,并使用最遵循标准的容器,而且,它们本身也是标准的一部分。” "

    “嗯?” 鲍勃和我一起说道。

     作为回答, Guru改动了鲍勃的源代码中的一行:

typedef hash_map<string, Employee> SearchIndex;

    “现在你有了恒定时间的查找, 在平均情形下,” 她说,“在产品代码中, 当你想用关联容器(associative container)时, 你几乎总是需要以hash为基础的(hash-based)容器, 而不是以树为基础(tree-based)的. 注意hash_map不是标准的, 但是在我们使用的所有平台上都可用 [1].当然你必须改变包含的头文件的名字。同时你还应当检查一下缺省的hash函数是否适合你的数据,如果不合适,你自己定制一个。测试、计时,如果可行的话就大胆采纳。”

    还没等我们发问,她又打开那本大书,扬长而去。我觉得现在确实是离开的适当时机,于是在鲍勃回过神来之前也离开了。

    那天的晚些时候, 在冷水机边, 我得知鲍勃根据建议作了改动, 它的确解决了问题.  QA部门很高兴, 我们的经理彼得.威廉姆斯也很高兴,鲍勃却不怎么高兴,不过他就是这样子,我想事情应该告一段落。

事实并非如此。

    两天后第二次爆发开始了。我冲进鲍勃的小房间里,看见了几乎同样的一幕: 一杯冷咖啡,一个头脑发热的鲍勃以及一封来自QA部门的EMail,这次, 我快速扫了一眼, QA又抱怨鲍勃昨天晚上编写并加入到版本控制的另一段代码的执行速度。

   “嗨,鲍勃,” 我天真的说,“现在又有什么问题?”

    “速度,”鲍勃说, 挠着自己,“速度。它们想从我这儿得到什么? 我的代码能起作用! 我甚至还用了hash_map以达到它们苛刻的编码要求. 现在那群QA的白痴们又神经兮兮的抱怨我的代码的执行速度无法满足规格的要求。”

    “这代码是干什么的?”

    “哦,”鲍勃无精打采的挥着手,“我想他们把我的程序用在实时中断处理或是别的。”我看了他的代码一眼:

typedef hash_map<Descriptor, Resource> Lookup; // ... Lookup::iterator result = res.find( d );

   “有多少个map?”我又问。

   “哦, 太多了。”鲍勃还是无精打采的挥着手。

    啪! 她又来了, 尽管我早就有点怀疑Guru在附近,但我还是吃了一惊,鲍勃也是。“'一种尺寸适合所有人' 是吗?”她平静的问,“为什么你在这儿用hash_map?”

    鲍勃的脸胀得通红。我退了几步,觉得鲍勃今天的恼怒底气不足,很快就泄气了。“因为,”他小心翼翼地说道,“就在两天前,你告诉我应该始终使用那些非标准的基于hash容器!”

    Guru悲哀的摇摇头,“我不是这样说的。” 鲍勃几乎肺都气炸了,但是她继续说: “喂,你说,我有没有说他总应该使用其于hash容器。” 她转过头来问我。

   我最不想的就是卷入他们之间的纠纷中。“呃,是吗?”我尽可能地含糊其词。

    我们之间沉默了一会儿, 又一会儿,然后:“你有没有听说过, 我的孩子,” 头头着急的问道,“曾经有人被三尺深的河水淹死?”

    我脑子里灵光一闪。“啊,”我点头,“还有人在平均等待时间只有一分钟的电梯旁等待了十五分钟。通常的容器都基于树,它们的查找速度一般是对数级的,不管是最好情况还是最坏情况。如果选择了合适的hash函灵敏,基于hash容器的平均查找时间要短一些。”我的回答机敏而迅速,赢得了Guru的微笑和颔首。“但在最坏的情况下,情况可能比基于树的容器还要差一点,是不是这样?”

    “是的,我的孩子,”她强调道,“永远不要混淆平均和最坏两种情况。”Guru重新打开她那本大书,翻到另一页。“这里,”她指了一下,说道,“听着,先知Musser,”她透过书的边缘看了看我,又看了看鲍勃,接着又把目光集中到书上,读道,“‘hash表操作的最坏执行效率可能非常差,可能是线性的N,在有些情况下就会这样。但那些基于平衡二叉树的容器始终保持在O(logN)。’”[2]

    “对,”我赶紧说, 渴望表现我的理解能力比鲍勃强,“两天前问题的实质是成千上万次查找的总时间, 因此一小撮慢没有关系, 只有平均情形最重要。但是对一个服务程序...”我停下声。

    “...你对可能花费的时间有一个严格的上限,”她确认,“最坏的情形则成了运用的一个重要的判据。”

    鲍勃只是看看我们中的这个,又看看那个, 好像他在看一场网球比赛。“够了!”他吼道,很快的站了起来,以至于椅子差点翻了过来, 然后气愤地朝着咖啡供应点的方向走去。

    “的确,”Guru继续说, 也转过身来准备走,“hash为插入和查找提供了恒定平均时间的优点, 而以树为基础的查找提供了可靠的性能。选择你所需要的,象我说的,” 她淘气的笑了一会儿,“‘在成品代码中, 当你需要关联容器时, 你几乎总是选择hash表为基础的容器...’”

- - - - - - - - - - - - - - - - - - - - - - - - -

      几个月后我们的婚礼举行了, 一个平静的家庭事件, 一个新的开始。一些政治斗争还在整个世界继续, 国家之间为了外星技术的地位和优点继续互相欺诈, 但是这和我再也没有关系。和我们都没有关系。我们和他们从此绝缘。

    婚礼过后几周的一个晴朗的下午, 珍妮坐在我们的新房子里,翻着一本书。

    “那是什么?”我问,她让我看了看书名,又向你展示她正在读的那一段。 “哦,”我说,“当然。好的,我们的道路会一直继续吗,你想了是吗?”

    “'从门下面开始,'”她同意了,笑着,“而且我认为我们的书的第一卷也有了一个结束。”‘在接下来的日子里,他们的生活幸福圆满’,详情见第二卷。

    我笑了, 我们都笑了, 这是真的。这就是我的部分生活故事的一个结束,就是我和珍妮怎样相遇和到达木星, 有一些美妙的发现和可怕的探险, 然后安全返回。但是我们幸福的生活还在继续,而且你可以相信在我们以后的生活里,我们会经常想起年轻年代的事情,比我们相识还要早得多的时候,我将告诉珍妮许多更加有趣、感人以及危险和值得记忆的跟温迪、鲍勃和Guru在一起时的故事。

[参考文献]

[1] 典型的基于hash容器是hash_set, hash_multiset, hash_map,和 hash_multimap。你可以在一些流行的C++编译器的工具箱内找到它们,包括Comeau(它使用了SGI的STL或Dinkuware标准库的修订版)、Metrowerks(这实现了自己的版本,最初是基于Modena,但进行了彻底的重设计和重编写)和Microsoft(它使用了Dinkumware标准库)。它们同时也包含于一些流行的第三方标准库实现方案中,你可能可以用你现有的编译器来使用它们,包括Dinkumware,SGI STL和STLport(它以SGI STL为基础)。有些实现方案略有出入,请参阅你所用的库的文档。

[2] D. Musser et al. STL Tutorial and Reference Guide, Second Edition (Addison-Wesley, 2001).

[关于作者]

Herb Sutter

是个独立顾问,也是ISO/ANSI C++标准委员会的秘书。你可通过[email protected].联系他

Jim Hyslop

Leitch Technology International Inc.资深的软件设计师,你可通过[email protected]联系他

本文地址:http://com.8s8s.com/it/it2480.htm