<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>