<html>
<head>
<style><!--
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
font-size: 10pt;
font-family:Tahoma
}
--></style>
</head>
<body class='hmmessage'>
Yes, gcc is slow and using as is wasteful.<br>Yes this should all be multi-threaded.<br>However it seems to me that m3front has many globals and therefore<br>really making things multi-threaded isn't going to be easy.<br>I think you are mostly wasting your time on user threads though.<br><br><br>Have you tried the NT/x86 system?<br>It is very fast.<br><br><br>In the gcc system, each .mc file is run through m3cgc1 to produce an .ms file,<br>which is run through as to produce an object file .mo.<br><br><br>In the NT/x86 system, cm3 outputs .obj files directly.<br>No .mc files. No m3cgc1 or as.<br>Granted, I'd just as soon output .c files, and hope for a C compiler that doesn't run a separate assembler.<br><br><br>My timing might loosen up a bit and I hope to spend some on the thread stress tester using pthreads. Next weekend.<br>Am I right though that Darwin works fine?<br>Solaris? FreeBSD? NetBSD? OpenBSD? Linux? I know, not Linux.<br>You generally don't need any of these systems.<br>All of the x86/AMD64 systems can run in a virtual machine.<br>Solaris/sparc we have good access.<br><br><br> - Jay<br><br><br>> To: m3devel@elegosoft.com<br>> Date: Sat, 12 Feb 2011 23:04:14 -0800<br>> From: mika@async.caltech.edu<br>> CC: jay.krell@cornell.edu<br>> Subject: [M3devel] More on threading<br>> <br>> <br>> Hi again m3devel,<br>> <br>> Well I thought I would relate another story having to do with threading<br>> and CM3. It's half-baked at the moment, but it only reflects a few hours'<br>> work on my part, sorry... I'm about to give up now because I don't know<br>> if the problem is in my code, or in threading, or what... maybe it is<br>> the atfork problem Jay talked about?<br>> <br>> Programmers spend a lot of time waiting for their compilers to run, so<br>> it would be nice if the compilation process were faster. After perusing<br>> the source code for the CM3 I realized that there is no feedback at all<br>> from the back-end and assembler steps to the rest of the compiler, and<br>> I believe that with the GCC back-end the vast majority of the runtime<br>> of the compiler is spent in these stages.<br>> <br>> So.... how hard could this be to parallelize? I decided to try making<br>> the two stages (cm3cg1 and as) into "promises" rather than running them<br>> on the spot. The QMachine adds a promise to call the back-end to a<br>> RefSeq.T called "promises", to which the Builder can also add promises.<br>> Then, after making all the promises, collect them and run them ("force<br>> them" a true Schemer would say) in parallel.<br>> <br>> Of course something goes wrong somewhere. With 10 threads I am able to<br>> run 31 seconds of CPU time in 19 seconds of wallclock time (using an<br>> average 160% of CPU) but then something goes wrong and my process<br>> is stuck in state "uxmxn". This is using user threads on AMD64_FREEBSD<br>> on a machine with four CPUs.<br>> <br>> Mika<br>> <br>> P.S. diff attached, it's a bit ugly, but maybe it works on some other<br>> system.... the code works just fine if you run one thread at a time.<br>> I.e., if you change<br>> <br>> IF threads.size() > 10 THEN EVAL Thread.Join(threads.remlo()) END;<br>> <br>> to<br>> <br>> IF threads.size() > 0 THEN EVAL Thread.Join(threads.remlo()) END;<br>> <br>> <br>> <br>> Index: cm3/src/Builder.m3<br>> ===================================================================<br>> RCS file: /usr/cvs/cm3/m3-sys/cm3/src/Builder.m3,v<br>> retrieving revision 1.36<br>> diff -c -r1.36 Builder.m3<br>> *** cm3/src/Builder.m3 24 Aug 2010 05:24:24 -0000 1.36<br>> --- cm3/src/Builder.m3 13 Feb 2011 06:47:40 -0000<br>> ***************<br>> *** 15,20 ****<br>> --- 15,22 ----<br>> IMPORT QIdent;<br>> FROM Target IMPORT M3BackendMode_t, BackendAssembly, BackendModeStrings;<br>> FROM M3Path IMPORT OSKind, OSKindStrings;<br>> + IMPORT Pathname;<br>> + IMPORT RefSeq;<br>> <br>> TYPE<br>> UK = M3Unit.Kind;<br>> ***************<br>> *** 131,136 ****<br>> --- 133,139 ----<br>> link_coverage : TEXT; (* coverage library *)<br>> m3_front_flags: Arg.List; (* configuration options for the front *)<br>> m3_options : Arg.List; (* misc. user options for the frontend *)<br>> + delayBackend := FALSE;<br>> END;<br>> <br>> TYPE<br>> ***************<br>> *** 932,937 ****<br>> --- 935,956 ----<br>> <br>> (*------------------------------------------------------------ compilation --*)<br>> <br>> + TYPE MarkerPromise = QMachine.Promise BRANDED OBJECT OVERRIDES fulfil := FulfilNothing END;<br>> + <br>> + PROCEDURE FulfilNothing(<*UNUSED*>mp : MarkerPromise) = BEGIN END FulfilNothing;<br>> + <br>> + TYPE SeqClosure = Thread.Closure OBJECT seq : RefSeq.T; OVERRIDES apply := SeqApply END;<br>> + <br>> + PROCEDURE SeqApply(cl : SeqClosure) : REFANY =<br>> + BEGIN<br>> + FOR i := 0 TO cl.seq.size()-1 DO<br>> + WITH p = NARROW(cl.seq.get(i),QMachine.Promise) DO<br>> + p.fulfil()<br>> + END<br>> + END;<br>> + RETURN NIL<br>> + END SeqApply;<br>> + <br>> PROCEDURE CompileEverything (s: State; schedule: SourceList) =<br>> VAR u: M3Unit.T;<br>> BEGIN<br>> ***************<br>> *** 941,948 ****<br>> <br>> (* compile all the sources using the initial schedule *)<br>> FOR i := 0 TO LAST (schedule^) DO<br>> ! CompileOne (s, schedule[i]);<br>> END;<br>> FlushPending (s);<br>> <br>> (* recompile any interfaces where we goofed on the exports *)<br>> --- 960,1000 ----<br>> <br>> (* compile all the sources using the initial schedule *)<br>> FOR i := 0 TO LAST (schedule^) DO<br>> ! s.delayBackend := TRUE;<br>> ! TRY<br>> ! CompileOne (s, schedule[i]);<br>> ! FINALLY<br>> ! s.delayBackend := FALSE;<br>> ! END;<br>> ! <br>> ! s.machine.promises.addhi(NEW(MarkerPromise));<br>> ! <br>> ! END;<br>> ! <br>> ! VAR<br>> ! curSeq := NEW(RefSeq.T).init();<br>> ! threads := NEW(RefSeq.T).init();<br>> ! BEGIN<br>> ! FOR i := 0 TO s.machine.promises.size()-1 DO<br>> ! WITH p = s.machine.promises.get(i) DO<br>> ! curSeq.addhi(p);<br>> ! IF i = s.machine.promises.size()-1 OR ISTYPE(p,MarkerPromise) THEN<br>> ! WITH cl = NEW(SeqClosure, seq := curSeq) DO<br>> ! threads.addhi (Thread.Fork(cl));<br>> ! <br>> ! IF threads.size() > 10 THEN EVAL Thread.Join(threads.remlo()) END;<br>> ! <br>> ! curSeq := NEW(RefSeq.T).init()<br>> ! END<br>> ! END<br>> ! END<br>> ! END;<br>> ! WHILE threads.size() > 0 DO EVAL Thread.Join(threads.remlo()) END;<br>> END;<br>> + <br>> + EVAL s.machine.promises.init();<br>> + <br>> + <br>> FlushPending (s);<br>> <br>> (* recompile any interfaces where we goofed on the exports *)<br>> ***************<br>> *** 1151,1156 ****<br>> --- 1203,1227 ----<br>> END;<br>> END CompileM3;<br>> <br>> + TYPE<br>> + NotePromise = QMachine.Promise OBJECT<br>> + nam : Pathname.T;<br>> + OVERRIDES<br>> + fulfil := FulfilNP;<br>> + END;<br>> + <br>> + RemovePromise = QMachine.Promise OBJECT<br>> + nam : Pathname.T;<br>> + OVERRIDES<br>> + fulfil := FulfilRP;<br>> + END;<br>> + <br>> + PROCEDURE FulfilNP(np : NotePromise) = <br>> + BEGIN Utils.NoteTempFile(np.nam) END FulfilNP;<br>> + <br>> + PROCEDURE FulfilRP(rp : RemovePromise) = <br>> + BEGIN Utils.Remove(rp.nam) END FulfilRP;<br>> + <br>> PROCEDURE PushOneM3 (s: State; u: M3Unit.T): BOOLEAN =<br>> VAR<br>> tmpC, tmpS: TEXT;<br>> ***************<br>> *** 1191,1208 ****<br>> <br>> <br>> | 3 => (* -bootstrap, +m3back, +asm *)<br>> tmpC := TempCName (u);<br>> tmpS := TempSName (u);<br>> IF (NOT s.keep_files) THEN Utils.NoteTempFile (tmpC) END;<br>> IF (NOT s.keep_files) THEN Utils.NoteTempFile (tmpS) END;<br>> IF RunM3 (s, u, tmpC) THEN<br>> ! IF RunM3Back (s, tmpC, tmpS, u.debug, u.optimize)<br>> ! AND RunAsm (s, tmpS, u.object) THEN<br>> END;<br>> need_merge := TRUE;<br>> END;<br>> IF (NOT s.keep_files) THEN Utils.Remove (tmpC) END;<br>> IF (NOT s.keep_files) THEN Utils.Remove (tmpS) END;<br>> <br>> | 6, (* +bootstrap, +m3back, -asm *)<br>> 7 => (* +bootstrap, +m3back, +asm *)<br>> --- 1262,1318 ----<br>> <br>> <br>> | 3 => (* -bootstrap, +m3back, +asm *)<br>> + IF s.delayBackend THEN<br>> tmpC := TempCName (u);<br>> tmpS := TempSName (u);<br>> + (*<br>> IF (NOT s.keep_files) THEN Utils.NoteTempFile (tmpC) END;<br>> IF (NOT s.keep_files) THEN Utils.NoteTempFile (tmpS) END;<br>> + *)<br>> + IF (NOT s.keep_files) THEN <br>> + s.machine.promises.addhi(NEW(NotePromise, nam := tmpC)) <br>> + END;<br>> + IF (NOT s.keep_files) THEN <br>> + s.machine.promises.addhi(NEW(NotePromise, nam := tmpS)) <br>> + END;<br>> + <br>> IF RunM3 (s, u, tmpC) THEN<br>> ! s.machine.record(TRUE);<br>> ! TRY<br>> ! IF RunM3Back (s, tmpC, tmpS, u.debug, u.optimize)<br>> ! AND RunAsm (s, tmpS, u.object) THEN<br>> ! END;<br>> ! FINALLY<br>> ! s.machine.record(FALSE)<br>> END;<br>> + <br>> need_merge := TRUE;<br>> END;<br>> + (*<br>> IF (NOT s.keep_files) THEN Utils.Remove (tmpC) END;<br>> IF (NOT s.keep_files) THEN Utils.Remove (tmpS) END;<br>> + *)<br>> + IF (NOT s.keep_files) THEN <br>> + s.machine.promises.addhi(NEW(RemovePromise, nam := tmpC)) <br>> + END;<br>> + IF (NOT s.keep_files) THEN <br>> + s.machine.promises.addhi(NEW(RemovePromise, nam := tmpS)) <br>> + END;<br>> + <br>> + ELSE<br>> + tmpC := TempCName (u);<br>> + tmpS := TempSName (u);<br>> + IF (NOT s.keep_files) THEN Utils.NoteTempFile (tmpC) END;<br>> + IF (NOT s.keep_files) THEN Utils.NoteTempFile (tmpS) END;<br>> + IF RunM3 (s, u, tmpC) THEN<br>> + IF RunM3Back (s, tmpC, tmpS, u.debug, u.optimize)<br>> + AND RunAsm (s, tmpS, u.object) THEN<br>> + END;<br>> + need_merge := TRUE;<br>> + END;<br>> + IF (NOT s.keep_files) THEN Utils.Remove (tmpC) END;<br>> + IF (NOT s.keep_files) THEN Utils.Remove (tmpS) END;<br>> + END<br>> <br>> | 6, (* +bootstrap, +m3back, -asm *)<br>> 7 => (* +bootstrap, +m3back, +asm *)<br>> Index: cm3/src/m3makefile<br>> ===================================================================<br>> RCS file: /usr/cvs/cm3/m3-sys/cm3/src/m3makefile,v<br>> retrieving revision 1.24<br>> diff -c -r1.24 m3makefile<br>> *** cm3/src/m3makefile 8 Dec 2010 07:30:57 -0000 1.24<br>> --- cm3/src/m3makefile 13 Feb 2011 06:47:42 -0000<br>> ***************<br>> *** 29,34 ****<br>> --- 29,35 ----<br>> end<br>> module ("Arg")<br>> module ("Builder")<br>> module ("Dirs")<br>> module ("M3Build")<br>> module ("M3Loc")<br>> Index: m3quake/src/QMachine.i3<br>> ===================================================================<br>> RCS file: /usr/cvs/cm3/m3-sys/m3quake/src/QMachine.i3,v<br>> retrieving revision 1.6<br>> diff -c -r1.6 QMachine.i3<br>> *** m3quake/src/QMachine.i3 4 Sep 2009 10:24:07 -0000 1.6<br>> --- m3quake/src/QMachine.i3 13 Feb 2011 06:47:43 -0000<br>> ***************<br>> *** 8,13 ****<br>> --- 8,14 ----<br>> <br>> IMPORT Thread, Wr, QValue, QCode;<br>> FROM Quake IMPORT Machine, Error, ID, IDMap;<br>> + IMPORT RefSeq;<br>> <br>> REVEAL<br>> T <: T_;<br>> ***************<br>> *** 15,20 ****<br>> --- 16,22 ----<br>> T = Machine;<br>> T_ = OBJECT<br>> map: IDMap := NIL; (* READONLY *)<br>> + promises : RefSeq.T;<br>> METHODS<br>> init (map: IDMap): T;<br>> evaluate (s: QCode.Stream) RAISES {Error, Thread.Alerted};<br>> ***************<br>> *** 37,42 ****<br>> --- 39,46 ----<br>> set_wr (wr: Wr.T);<br>> exec_echo (b: BOOLEAN): BOOLEAN;<br>> trace (b: BOOLEAN);<br>> + <br>> + record(on : BOOLEAN); (* instead of performing certain acts, promise *)<br>> END;<br>> <br>> PROCEDURE PushBool (t: T; b: BOOLEAN);<br>> ***************<br>> *** 51,54 ****<br>> --- 55,63 ----<br>> <br>> PROCEDURE GetEnv (default, v0, v1, v2, v3, v4: TEXT := NIL): TEXT;<br>> <br>> + TYPE Promise = OBJECT METHODS fulfil() RAISES { Error } END;<br>> + <br>> END QMachine.<br>> + <br>> + <br>> + <br>> Index: m3quake/src/QMachine.m3<br>> ===================================================================<br>> RCS file: /usr/cvs/cm3/m3-sys/m3quake/src/QMachine.m3,v<br>> retrieving revision 1.35<br>> diff -c -r1.35 QMachine.m3<br>> *** m3quake/src/QMachine.m3 3 Aug 2010 09:40:04 -0000 1.35<br>> --- m3quake/src/QMachine.m3 13 Feb 2011 06:47:44 -0000<br>> ***************<br>> *** 16,21 ****<br>> --- 16,22 ----<br>> IMPORT TextUtils, FSUtils, System, DirStack; (* sysutils *)<br>> IMPORT Compiler;<br>> IMPORT M3Path;<br>> + IMPORT RefSeq;<br>> <br>> CONST<br>> OnUnix = (Compiler.ThisOS = Compiler.OS.POSIX);<br>> ***************<br>> *** 44,49 ****<br>> --- 45,52 ----<br>> shell : TEXT := NIL;<br>> sh_option : TEXT := NIL;<br>> tmp_dir : TEXT := NIL;<br>> + <br>> + doRecord := FALSE;<br>> OVERRIDES<br>> init := Init;<br>> evaluate := Evaluate;<br>> ***************<br>> *** 66,73 ****<br>> --- 69,81 ----<br>> set_wr := SetWr;<br>> exec_echo := ExecEcho;<br>> trace := Trace;<br>> + <br>> + record := Record;<br>> END;<br>> <br>> + PROCEDURE Record(t : T; on : BOOLEAN) = <br>> + BEGIN t.doRecord := on END Record;<br>> + <br>> TYPE<br>> Registers = RECORD<br>> cp : QCode.Stream := NIL; (* code pointer *)<br>> ***************<br>> *** 139,144 ****<br>> --- 147,154 ----<br>> t.globals := NEW (IntRefTbl.Default).init ();<br>> t.default_wr := Stdio.stdout;<br>> <br>> + t.promises := NEW(RefSeq.T).init();<br>> + <br>> InitOSEnv (t);<br>> InitBuiltins (t);<br>> <br>> ***************<br>> *** 1555,1564 ****<br>> END;<br>> ELSE<br>> FlushIO ();<br>> ! Process.GetStandardFileHandles (stdin, stdout, stderr);<br>> ! handle := Process.Create (t.shell, SUBARRAY (args, 0, n_shell_args),<br>> ! stdin := stdin, stdout := stdout,<br>> ! stderr := stderr);<br>> END;<br>> EXCEPT<br>> | Thread.Alerted =><br>> --- 1565,1594 ----<br>> END;<br>> ELSE<br>> FlushIO ();<br>> ! IF t.doRecord THEN<br>> ! handle := NIL;<br>> ! WITH a = NEW(REF ARRAY OF TEXT, n_shell_args) DO<br>> ! a^ := SUBARRAY(args,0,n_shell_args);<br>> ! VAR wrx : Wr.T; BEGIN<br>> ! IF echo OR t.do_echo THEN<br>> ! wrx := wr<br>> ! ELSE<br>> ! wrx := NIL<br>> ! END;<br>> ! t.promises.addhi(NEW(ExecPromise,<br>> ! cmd := t.shell,<br>> ! wr := wrx,<br>> ! args := a,<br>> ! t := t,<br>> ! ignore_errors := ignore_errors))<br>> ! END<br>> ! END<br>> ! ELSE<br>> ! Process.GetStandardFileHandles (stdin, stdout, stderr);<br>> ! handle := Process.Create (t.shell, SUBARRAY (args, 0, n_shell_args),<br>> ! stdin := stdin, stdout := stdout,<br>> ! stderr := stderr);<br>> ! END;<br>> END;<br>> EXCEPT<br>> | Thread.Alerted =><br>> ***************<br>> *** 1573,1579 ****<br>> END;<br>> <br>> (* wait for everything to shutdown... *)<br>> ! exit_code := Process.Wait (handle);<br>> END;<br>> <br>> IF onlyTry THEN<br>> --- 1603,1613 ----<br>> END;<br>> <br>> (* wait for everything to shutdown... *)<br>> ! IF handle = NIL THEN<br>> ! exit_code := 0 <br>> ! ELSE<br>> ! exit_code := Process.Wait (handle);<br>> ! END; (* else we're only promising *)<br>> END;<br>> <br>> IF onlyTry THEN<br>> ***************<br>> *** 1589,1594 ****<br>> --- 1623,1664 ----<br>> <br>> END ExecCommand;<br>> <br>> + TYPE <br>> + ExecPromise = Promise OBJECT<br>> + cmd : TEXT;<br>> + args : REF ARRAY OF TEXT;<br>> + t : T;<br>> + wr : Wr.T;<br>> + ignore_errors : BOOLEAN;<br>> + OVERRIDES<br>> + fulfil := FulfilExecPromise;<br>> + END;<br>> + <br>> + PROCEDURE FulfilExecPromise(ep : ExecPromise) RAISES { Error } = <br>> + VAR<br>> + stdin, stdout, stderr: File.T;<br>> + BEGIN<br>> + Process.GetStandardFileHandles (stdin, stdout, stderr);<br>> + TRY<br>> + IF ep.wr # NIL THEN<br>> + Wr.PutText (ep.wr, ep.args[1]);<br>> + Wr.PutText (ep.wr, Wr.EOL);<br>> + FlushIO ();<br>> + END;<br>> + WITH handle = Process.Create (ep.cmd, ep.args^,<br>> + stdin := stdin, stdout := stdout,<br>> + stderr := stderr),<br>> + exit_code = Process.Wait(handle) DO<br>> + IF exit_code # 0 AND NOT ep.ignore_errors THEN<br>> + Err (ep.t, Fmt.F("exit %s: %s", Fmt.Int(exit_code), ep.cmd))<br>> + END<br>> + END<br>> + EXCEPT<br>> + OSError.E (ec) =><br>> + Err (ep.t, Fmt.F ("exec failed%s *** %s", OSErr (ec), ep.cmd));<br>> + END<br>> + END FulfilExecPromise;<br>> + <br>> PROCEDURE KillProcess (handle: Process.T) =<br>> BEGIN<br>> IF (handle # NIL) THEN<br>> <br> </body>
</html>