Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

СУПРАЦЬ Не павінен СУПРАЦЬ сваіх аргументаў, частка II: Чейні на MTA

Original on http://home.pipeline.com/~hbaker1/CheneyMTA.html
ПРАЕКТ для comp.lang.scheme.c 4 лютага 1994

ACM SIGPLAN Апавяшчэнні 30, 9 (верасень 1995), 17-20.
Генры Г. Бэйкер

Спрытны Computer Corporation, 16231 Медоу-Рідж-Уэй, Энсіно, Каліфорнія 91436
(818) 986-1436 (818) 986-1360 (факс)
Copyright (C) 1994 ад Спрытны карпарацыі кампутар. Усе правы абаронены.

Анатацыя

Папярэдняя схем для рэалізацыі поўнага хваставой рэкурсіі пры кампіляцыі ў C патрабуюць той ці іншай форме "трамплін" для поп-стэка. Мы прапануем рашэнні хваставой рэкурсіі праблема ў тым жа парадку, як Standard ML Нью-Джэрсі, вылучыўшы ўсе кадры ў (збор смецця) кучы. Схема праграмы перакладзены на працяг абыход стылі, так што функцыі мэтавых C ніколі не вяртацца. З паказальніка стэка становіцца размеркаванне паказальнік для Чейні стылі капіявання схемы зборкі смецця. Наша схема можа выкарыстоўваць C выклікаў функцый, З аргументамі, Са зменнай арності функцый, так і асобныя кампіляцыі без комплексу блок-кампіляцыя ўсёй праграмы.

Уводзіны

IEEE Схема [IEEE90] патрабуе, каб усе функцыі належным чынам хваставой рэкурсіяй, з тым, што хвост-рэкурсіўных праграм не патрабуе неабмежаванай колькасці стэка. Некаторыя кампілятары Схема якія арыентаваны на мове З [Bartlett89], таму што C з'яўляецца эфектыўнай сістэмы праграмавання мовы, які даступны практычна на кожным кампутары, што забяспечвае мабільнасць скампіляваны код. Нажаль, C кампілятары не абавязаны быць правільна хваставой рэкурсіяй, і толькі кампілятараў GNU C [Stallman90] спроба дасягнуць хвост рэкурсіі ў іх рэалізацыі.

Папулярным метадам для дасягнення належнага рэкурсіі хвост не ў хваставой рэкурсіяй рэалізацыі З батуце. [2] батут вонкавая функцыя якога ітератівно званкі ўнутраная функцыя. Унутраная функцыя вяртае адрас іншай функцыі выкліку, а таксама вонкавая функцыя, тое заве гэту новую функцыю. Гэта значыць, калі ўнутраная функцыя жадаў бы звярнуць іншага ўнутраная функцыя хваста рэкурсіўна, яна вяртае адрас функцыі яна жадае, каб ператэлефанаваць на батуце, які затым выклікае вяртаецца функцыяй. Па вяртанні перад выклікам, стэк першы выскачыў так, што яна не расце неабмежавана на простай ітэрацыі. Нажаль, кошт такіх батуце выкліку функцыі ў 2-3 разу павольней, чым звычайны выклік C, і яна патрабуе, каб аргументы перадаюцца ў глабальных зменных [Tarditi92].

неапублікаваныя прапанова Аппеля для дасягнення належнага рэкурсіі хвост у C выкарыстоўвае значна больш фіксаванага памеру стэка, працяг абыход стылі, а таксама не ставіць якіх-небудзь аргументаў ці дадзеных на C стэка. Калі стэк пра перапаўненне, адрас наступнай функцыі выклік longjmp 'Эд (ці вяртанне "ЭД) на батуце. Метад Аппеля пазбягае прыняцці вялікага ліку дробных адмоў батуце час ад часу, каб саскочыць з Эмпайр Стейт Білдінг.

Прапанаваная схема

Мы прапануем скласці схему ператварэння яго ў працяг абыход стыль (CPS), а затым скампіляваць у выніку лямбда-выразаў у асобных функцый C. Аргументы перадаюцца як звычайныя аргументы C, і выклікі функцый звычайныя выклікі C. Працяг блакады і зачыненні асяроддзя перадаюцца ў якасці дадатковых аргументаў C. (Вядома, заклікі да зачыненне выканання выкліку на C код частка зачынення, і перадаць асяроддзе частка зачынення ў якасці дадатковага аргументу.) Такая схема ніколі не выконвае зварот C, так што стэк будзе гадуй і гадуй.

З Са "Стэк" ніколі не кантракты, мы можам вылучыць усе нашы замыканні і карыстацкіх дадзеных структур на гэты стэк, як аўтаматычная / дынамічная / лакальных дадзеных. Усе засаўкі і карыстацкія дадзеныя структуры, памеры якіх вядомыя падчас кампіляцыі статычна вылучана ў Са "Стэк" рамкі; дынамічных масіваў і іншых структур дадзеных, памер якіх невядомы падчас кампіляцыі могуць быць вылучаны па's ALLOCA З прымітыўныя (ці эквівалент), якія таксама атрымлівае месца, пачынальна са "Стэк". [3] Бо наш C "Стэк", таксама "кучы", не існуе адрозненні паміж стэка і кучы размеркавання.

Паколькі ніводная з нашых функцый C калі-небудзь вяртаецца, толькі жыць на гэтым кадры "Стэк" з'яўляецца верхняя. Аднак, у шматлікіх з мёртвых кадраў будзе знойдзены жыць замыканні і жыць карыстацкіх дадзеных аб'ектаў. Урэшце, Са "стэк" будзе перапаўненне прасторы, ускладзеныя на яго, і мы павінны выконваць збор смецця. Вываз смецця (GC) шляхам капіявання з'яўляецца адносна прамы наперад працэсу. Ёсць цэлы шэраг статычных карані, а таксама апошнія працяг зачынення, якая перадаецца ў ГК у якасці аргументу. (Фармаванне відавочнае зачыненне працягу для GC дазваляе пазбегнуць неабходнасці сканавання C кадраў стэка.) Жывыя аб'екты і жыць замыканні ўсіх скапіяваных (і тым самым кандэнсату) у іншую вобласць, так што пакаранне смерцю можа быць перазапушчаны "Сцёк" кадр Пачатак Са "Стэк" вылучэнне вобласці.

Ключавым момантам з'яўляецца тое, што бо толькі жывыя аб'екты прасочваюцца - гэта значыць, смецце (у тым ліку C кадраў, якія ўсе мёртвыя) не прасочваецца - ГК не павінны ведаць, фармат кадра стэка і можа быць запісаны ў сябе. З Чейні стылі сканар павінен ведаць фармат усіх tospace аб'ектаў, але мы капіюем толькі тыя аб'екты, - ніколі C кадраў. Калі GC будзе зроблена, ён стварае новыя рамкі для выканання яе працяг. GC завецца відавочна з кода C ці пасля праверкі паказальнік стэка дасягнула зададзенай мяжы. Хоць стэк-паказальнік праверкі такім чынам, можа запатрабавацца яшчэ некалькі каманд, чым калі б гэта было зроблена на асэмблеры, гэта яшчэ хутчэй, чым батуце выкліку будзе.

/* The following macro definition is machine-dependent.  */
#ifdef stack_grows_upward
#define stack_check(sp) ((sp) >= limit)
#else
#define stack_check(sp) ((sp) <= limit)
#endif
...
object foo(env,cont,a1,a2,a3) environment env; object cont,a1,a2,a3;
{int xyzzy; void *sp = &xyzzy; /* Where are we on the stack? */
 /* May put other local allocations here. */
 ...
 if (stack_check(sp)) /* Check allocation limit. */
    {closure5_type foo_closure; /* Locally allocate closure with 5 slots. */
     /* Initialize foo_closure with env,cont,a1,a2,a3 and ptr to foo code. */
     ...
     return GC(&foo_closure);} /* Do GC and then execute foo_closure. */
 /* Rest of foo code follows. */
 ...
}
Пасля GC вырабляецца капіяванне, ён павінен скінуць C сцёк паказальнік на пачатак вобласці размеркавання і выклікаць яго працяг аргумент. З GC сама выконвала з рамы ў fromspace, яна павінна выклікаць паказальніка стэка для скіду з вылучэннем вобласці ў tospace а затым выклікаць яго працяг. Адзін са спосабаў перасоўвання паказальніка стэка для GC тэлефанаваць ALLOCA функцыі з аргументам, які з'яўляецца розніца - дадатнае ці адмоўнае -! Паміж бягучага паказальніка стэка і жаданага паказальніка стэка. Тады, GC можа выклікаць яго працяг.

(Альтэрнатыўны метад, прапанаваны Аппель варта SML / NJ больш цесна -. Пасля аператыўныя дадзеныя былі скапіяваны ў іншым месцы гэта малаважныя калекцыі - GC можа longjmp на батуце (ці проста вярнуць яе аргумент!) І батут будзе перазагрузіць працяг паказальнік стэка вылучэння ў ніжняй частцы стэка зноў. Пры агульнай скапіяваныя дадзеныя ў другой вобласці перавышае вызначаную суму, асноўныя калекцыі ажыццяўляецца на другой вобласці.)

СУПРАЦЬ Не павінен СУПРАЦЬ сваіх аргументаў

У нашай схеме, кампілятар робіць усё consing выкарыстаннем стэку (дынамічная) лакальнае сховішча. Такім чынам, revappend функцыю зваротнага аднаго спісу на іншы спіс выглядае наступным кодам. (Аператар звароту C Цікаўна, бо мы насамрэч не калі-небудзь вярнуцца, калі мы выкарыстоўваем Метад Аппель; вяртанне проста кажа кампілятар C, што лакальныя зменныя ўсе мёртвыя (за выключэннем зваротны адрас!), І таму needn T захаваць іх "праз" выкліку.)
object revappend(cont,old,new) object cont,old,new;
{if (old == NIL)
  {clos_type *c = cont;
   /* Call continuation with new as result. */
   return (c->fn)(c->env,new);}
  {cons_type *o = old; cons_type newer; /* Should check stack here. */
   /* Code for (revappend (cdr old) (cons (car old) new)). */
   newer.tag = cons_tag; newer.car = o->car; newer.cdr = new;
   return revappend(cont,o->cdr,&newer);}}
Замыканні, памер якіх заўсёды вядомыя кампілятар, відавочна пабудаваных у мясцовых кадраў у аналагічным чынам. Вектары і масівы, памер якіх невядомы падчас кампіляцыі могуць быць вылучаны па тэлефоне ALLOCA (ці яго эквівалент), каб пашырыць бягучага кадра стэка. (Гл. таксама ніжэй частка, прысвечаны Malloc-вылучаных аб'ектаў.)

Зменнай арності функцый

-Арності Схема функцый зменнай могуць быць скампіляваны ў зменнай арності функцый З выкарыстаннем або Unix зменнай даўжыні ці ANSI C stdarg механізму. Выкарыстанне CPS, зменнай арності некалькі "вярнуўся" значэнні могуць быць аналагічным чынам апрацоўваецца.

Ітэрацыя

Адзіным механізмам Схемы для выраза ітэрацыі "хвост выкліку", такім чынам, схема кампілятары выконваюць мноства аптымізацый для забеспячэння эфектыўнасці гэтых "хвост рэкурсіі". З нашай схеме, вызначэнне ітэрацыйныя працэдура значна скараціўся - толькі тыя ітэрацый якія не размеркаванне памяці могуць быць канвертаваны ў ітэрацыі, бо ўсе захоўванні вылучаецца ў стэку. У прыватнасці, толькі самага нізкага ўзроўня функцыі могуць працаваць без вылучэння некаторых захоўванні - напрыклад, званкі ў бібліятэку працэдур C, што аднаўленне "Стэк" паказальнік можа быць зроблена на працягу ітэрацыі. Хваставой рэкурсіі Мясцовыя аптымізацыі [Bartlett89] Такім чынам, досыць атрымаць эфектыўныя ітэрацыі. Такім чынам, дасягненні духу, калі не літары, рэкурсіі закона ANSI Схема хвост.

Аптымізацыя схемы кампілятара

Схема кампілятары выконваюць шэраг аптымізацый, каб паменшыць кошт стварэння і зачыненні кошт выкліку функцыі - напрыклад, яны выкарыстоўваюць "вядома" функцыі пры кампіляцыі выклікі функцый. [4] "Lambda уздыму" [Пейтон-Jones87] можа быць скарыстаны для замены зачынення стварэння дадатковай перадачы аргументу; гэтай аптымізацыі з'яўляецца вельмі каштоўным, калі аргументы могуць быць у рэестры. Розныя выгляды выснова тыпу могуць быць скарыстаны аптымізацыі ўяўлення значэнняў і пазбегнуць неабходнасці для праверкі тыпу падчас выканання. Гэтыя аптымізацыі працягваюць быць каштоўнымі ў нашай стэк / кучу схеме - гэта значыць, яны артаганальныя палітыку кіравання памяццю.

Выкарыстанне Malloc

Калі Malloc размеркаваная захоўванні адрозныя па адрасе дыяпазоне ад стэк / кучу захоўвання, то Malloc можа быць скарыстаны для вылучэння (не перамяшчаны) аб'ектаў. [5] Гэтыя аб'екты павінны быць пералічоным, так што ГК можа першы след гэтых аб'ектаў ( яны павінны мець належны тэгаў), а затым разгорткі і відавочна вызваляць тых аб'ектаў, якія становяцца смеццем.

Паасобная кампіляцыя

Для таго каб атрымаць належнае хваставой рэкурсіі, якая існуе схема-на-C і ML-на-C кампілятары вялікай колькасці межпроцедурных аптымізацыі (у тым ліку блок складанак), якія перашкаджаюць паасобнай кампіляцыі, стварэнні вялікіх функцый C, і чыннікам доўгага складанак C. У нашай схеме, кожная функцыя C у прынцыпе можа быць у асобным файле.

Выклік звычайных функцый C

Выклік звычайных функцый C, якія вяртаюць трывіяльна, акрамя таго, што вы павінны забяспечыць досыць "Стэк" прастора загадзя, з тым, што C "Стэк" не перапаўненні падчас выканання функцый C. Гэтыя функцыі C не можа захоўваць паказальнікі на GC-стане аб'ектаў, за выключэннем "корань" месцах, яны не могуць "зваротны выклік" на незваротных Схема функцый.

Пытанні апаратнай архітэктуры

Аппель і Dybvig папярэджваюць, што наш напорысты схема можа зламацца "рэестр Windows", напрыклад, у архітэктуры SPARC.

Высновы

Адсутнасць хваста рэкурсіі ў C можа быць перавагай для ажыццяўлення хваставой рэкурсіяй, збор смецця ў мове, як схема. [6] Калі ўсе функцыі ператвараюцца ў працяг абыход стыль, калі аргументы перадаюцца ў якасці аргументаў C (нават для зменнай -арності функцый), калі C's "Стэк" размеркавання выкарыстоўваецца для выканання ўсіх асігнаванняў (у тым ліку зачыненне размеркавання), і калі калектар капіявання смецця выкарыстоўваецца, то мы можам дамагчыся рэалізацыі схемы на верхняй частцы З, якая падобная на рэалізацыю SML / NJ [Appel91], акрамя таго, што яна будзе значна больш партатыўнымі. У нашай схеме, увесь C "Стэк" эфектыўна маладога пакалення ў пакаленне зборшчык смецця!

Ключавой асаблівасцю нашай схемы з'яўляецца тое, што зборшчык смецця пазбаўляе ад неабходнасці адсочвання смецця, і, такім чынам, не трэба ведаць фармат гэтага смецця, які складаецца з усё C кадраў стэка. У кошт увесь час праверкі і пасля перасылання паказальнікі, мы маглі б зрабіць нашы схемы "рэальным часе" [Baker78]. папярэдняй працы[Baker92] таксама выступае вылучэнні аб'ектаў у стэку ў лакальнай сістэме C. Аднак, бягучая схема прасцей, бо яна не патрабуе, каб аб'екты перадаюцца дынамічна падчас выканання. У нас ёсць рукі-пераклад Бойер Benchmark у З выкарыстаннем метадаў гэтай працы [cboyer13.c] .

Падзякі

Вялікае дзякуй Андрэю Аппель, Джером Chailloux, Р. Кент Dybvig, і David Wise за іх каментары на ранніх чарнавікоў гэтага артыкула (і іх выпраўленні для маіх памылак!).

Спіс літаратуры

[Appel87] Appel, A.W. "Garbage collection can be faster than stack allocation". Information Processing Letters 25, 4 (1987), 275-279.

[Appel89] Appel, A.W. "Simple Generational Garbage Collection and Fast Allocation". Software--Practice and Experience 19, 2 (Feb. 1989), 171-183.

[Appel90] Appel, A.W. "A Runtime System". Lisp and Symbolic Computation 3, 4 (Nov. 1990), 343-380.

[Appel91] Appel, A.W., and MacQueen, D.B. "Standard ML of New Jersey". In Wirsing, M., ed. Third Int'l Symp on Programming Language Implementation and Logic Programming, Springer, August 1991.

[Baker78] Baker, H.G. "List Processing in Real Time on a Serial Computer". Comm. of the ACM 21, 4 (April 1978), 280-294.

[Baker92] Baker, H.G. "CONS Should Not CONS Its Arguments, or, a Lazy alloc is a Smart alloc". ACM Sigplan Not. 27, 3 (Mar. 1992), 24-34.

[Bartlett89] Bartlett, J. Scheme->C: A portable Scheme-to-C compiler. Tech. Rept., DEC West. Res. Lab., 1989.

Berry, D.M. "Block Structure: Retention vs. Deletion". Proc. 3rd Sigact Symposium Theory of Computation, Shaker Hgts., 1971.

Cheney, C.J. "A nonrecursive list compacting algorithm". Comm. of the ACM 13, 11 (Nov. 1970), 677-678.

Clinger, W., Hartheimer, A., and Ost, E. "Implementation strategies for continuations". Proc. Lisp and Functional Programming, 1988, 124-131.

Danvy, O. "Memory Allocation and Higher-Order Functions". Proc. Sigplan '87 Symposium on Interpretation and Interpretive Techniques, ACM Sigplan Not. 22, 7 (July 1987), 241-252.

Fairbairn, J., and Wray, S.C. "TIM: a simple abstract machine to execute supercombinators". Proc. 1987 Functional Programming and Computer Architecture.

Fischer, M.J. "Lambda Calculus Schemata". ACM Conf. Proving Assertions about Programs, ACM Sigplan Not. 7, 1 (Jan. 1972).

Friedman, D.P., and Wise, D.S. "CONS Should Not Evaluate Its Arguments". In Michaelson, S., and Milner, R., eds. Automata, Languages and Programming. Edinburgh U. Press, 1976, 257-284.

Hanson, C. "Efficient stack allocation for tail-recursive languages". Proc. Lisp and Functional Programming, June 1990.

[Rees] IEEE. IEEE Standard for the Scheme Programming Language. IEEE-1178-1990, IEEE, NY, Dec. 1990.

Peyton Jones, S.L. The Implementation of Functional Programming Languages. Prentice-Hall, New York, 1987.

Stallman, R.M. "Phantom Stacks: If you look too hard, they aren't there". AI Memo 556, MIT AI Lab., Sept. 1974.

Stallman, R.M. Using and Porting GNU CC. Free Software Foundation, Inc. February, 1990.

Steiner, J., and Hawkes, B. "The M.T.A." On the album, The Kingston Trio At Large, released June 8, 1959, reached number 15 on June 15, 1959. Copyright Atlantic Music 1956-57.

[Tarditi92] Tarditi, D., and Lee, P. "No assembly required: Compiling standard ML to C". ACM Letters on Programming Languages and Systems 1, 2 (1992), 161-177.

[1] спасылка на 1959 песня Чарлі на MTA [Steiner56] запісаны Kingston Trio. Тэксты ўключаюць прыпеў:

Ах, ці будзе ён калі-небудзь вярнуцца?
Не, ён ніколі не вярнуся,
і яго лёс дагэтуль вывучыўся.
Ён будзе ездзіць вечна,
"Ніт вуліцах Бостана,
Ён чалавек, які ніколі не вярнуся.
Чарлі не мог выйсці мітрапаліт Transit Authority (цяпер Масачусецкага заліва Transit Authority "MBTA") мятро (труба) цягнік, таму што яму бракавала грошай, каб аплаціць праезд. (Дарэчы, "Чарлі" таксама вайскоўцы назва літары "З".)

[2] Іншыя назвы батуце з'яўляюцца дыспетчара ці аператара, які павінен перадаваць вашы тэлефонныя званкі для вас.

[3] Максімальна партатыўных рэалізацыі могуць пазбягаць ALLOCA, бо яна не патрабуе ні ANSI C ці Unix.

[4] Джером Chailloux кажа мне, што IBM RS6000 і HP 9700 C кампілятары ствараюць павольней "заглушкі" для ўскосна-выкліканых функцый C - напрыклад, нашы замыканняў. Схема-на-C кампілятары, такім чынам, утвораць званкі на "вядомыя" функцыі па меры магчымасці.

[5] Максімальна партатыўных рэалізацыі могуць аддаюць перавагу Malloc да ALLOCA для аб'ектаў, памер якіх не вядомы падчас кампіляцыі.

[6] З, які робяць хвост рэкурсіі спробы - напрыклад, Gnu C - можа быць аштрафаваны, калі яны аднаўляюць задзірлівым захаванні рэгістраў!

Published (Last edited): 16-02-2011