<html>
<head>
<style>
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
FONT-SIZE: 10pt;
FONT-FAMILY:Tahoma
}
</style>
</head>
<body class='hmmessage'>There are a surprising number of options here.<BR>
<BR>What I describe below is ok.<BR>
<BR>Other options include:<BR>
<BR>The heuristic I define below is not bad.<BR>
If a command has none of $%^\*?()&|=;<>{}[]'"`~ then no sh wrapper is needed. Right?<BR>
Heck -- define the set as anything but [a-z0-9-/ ]?<BR>
<BR>What about shell builtins? Are they useful?<BR>Code that requires a sh wrapper is veering into non-portability, but<BR>some of the sh semantics are the same on NT and Posix.<BR>Specifically <>|&.<BR>
<BR>If code absolutely needed a sh wrapper, and wasn't portable anyway, it<BR>could be provided manually: exec("sh -c \"echo foo\"")<BR>
<BR>As well, a new prefix character could be used for "direct" or "fast" exec.<BR>
<BR>As well, since m3cg and as are the most interesting by far, they could by<BR>default be implemented in Modula-3, without a sh wrapper, and only if<BR>a Quake function is provided, use it.<BR>
<BR>As well, "half" the gain could be had by adding a switch to cm3cg to have it<BR>run as itself. Since this is only half the gain, not worthwhile.<BR>
<BR>As well, all of this would be gated on some global like Process.IsSpawnFaster()<BR>and/or Process.IsShWrapperSlow(), so that the default behavior would be unchanged.<BR>
<BR>As well, one thing my measurements (or reporting) failed to separately point out<BR>is if it is the sh vs. no-sh or vfork/exec vs. spawn that is slow.<BR>I'll have to test those separately.<BR>Likely that they each add slowness, but separate measure would be good.<BR>
<BR>ok, I ran a bunch more tests.<BR>A small "combinatorial explosion".<BR>
<BR>The sh wrapper is the larger hit, but spawn is still measurably faster than fork/exec.<BR>ash and zsh are also faster than sh. But no matter, I'll use none.<BR>Ubuntu uses ash for /bin/sh.<BR>There are some surprising cases where "Cyg" is faster than "MS", but not where it matters here.<BR>cygwin system and "crt0.o" seem faster, at least in this context.<BR>I'm suspicious of those numbers though.<BR>Could be something is slowing down the MS paths here.<BR>
<BR>The test code is attached.<BR>
<BR>Relevant excerpts from the data are:<BR>
<BR>(NOTE this is a different machine than the other numbers, and on XP.)<BR>
100 iterations:<BR>
 <BR>
current behavior:<BR>vfork/exec(sh -c ./cygnothing) 7.489000<BR>
 <BR>
change to spawn but keep sh wrapper:<BR>spawn(nowait, sh -c ./cygnothing)/waitpid 5.323000<BR>
 <BR>
keep fork/exec but lose sh wrapper:<BR>vfork/exec(.\cygnothing) 4.133000<BR>
 <BR>
lose sh wrapper and fork/exec<BR>spawn(nowait, .\cygnothing)/waitpid 1.946000<BR>
 <BR>
some of the fastest cases:<BR> spawn(wait, .\cygnothing) 2.009000<BR> msvcrt_system(.\cygnothing) 0.313000<BR> spawn(nowait, .\ms1nothing)/waitpid 0.188000<BR> spawn(wait, .\ms1nothing) 0.062000<BR> msvcrt_system(.\ms1nothing) 0.280000<BR> msvcrt_system(.\ms2nothing) 0.156000<BR> spawn(nowait, .\ms3nothing)/waitpid 0.094000<BR> spawn(wait, .\ms3nothing) 0.203000<BR> msvcrt_system(.\ms3nothing) 0.265000<BR>
<BR>I hadn't previously seen such much faster numbers<BR>than I was going to easily achieve, so let's try with 500 iterations:<BR>(if you don't like the numbers, run again... :) )<BR>
 <BR>
current behavior:<BR> vfork/exec(sh -c ./cygnothing) 37.415000<BR>
 <BR>
replace fork/exec with spawn<BR> spawn(nowait, sh -c ./cygnothing)/waitpid 26.851000<BR> spawn(wait, sh -c ./cygnothing) 27.017000 (why is this slower?)<BR>
 <BR>
cygwin1 startup code in child process measurably slow!<BR> vfork/exec(sh -c ./ms1nothing) 28.208000<BR> spawn(nowait, sh -c ./ms1nothing)/waitpid 17.549000<BR> spawn(wait, sh -c ./ms1nothing) 17.506000<BR>
<BR>remove sh wrapper, cut cost by /roughly/ 50% <BR> vfork/exec(.\cygnothing) 20.321000<BR>
 <BR>
remove sh wrapper and fork/exe, another /roughly/ 50%<BR> spawn(nowait, .\cygnothing)/waitpid 9.933000<BR>
 <BR>
and again there are some much faster options, all around 1 second: <BR> msvcrt_system(.\cygnothing) 1.125000<BR> spawn(nowait, .\ms1nothing)/waitpid 0.766000<BR> spawn(wait, .\ms1nothing) 0.733000<BR> msvcrt_system(.\ms1nothing) 1.157000<BR> spawn(nowait, .\ms2nothing)/waitpid 0.875000<BR> spawn(wait, .\ms2nothing) 0.938000<BR> msvcrt_system(.\ms2nothing) 1.142000<BR> spawn(nowait, .\ms3nothing)/waitpid 1.204000<BR> spawn(wait, .\ms3nothing) 0.265000          The best, but doesn't make sense. <BR> msvcrt_system(.\ms3nothing) 1.187000<BR>
<BR>I think ultimately the thing to do is try using the Win32 code, and then<BR>
<BR> <A href="http://cygwin.com/cygwin-api/cygwin-functions.html#func-cygwin-attach-handle-to-fd">http://cygwin.com/cygwin-api/cygwin-functions.html#func-cygwin-attach-handle-to-fd</A> <BR>
<BR> to get back child handles for stdin/out/err <BR>
<BR> and <A href="http://cygwin.com/cygwin-api/func-cygwin-winpid-to-pid.html">http://cygwin.com/cygwin-api/func-cygwin-winpid-to-pid.html</A> <BR>
<BR> to get a Cygwin pid, if that works having run CreateProcess.<BR>
<BR> That is, with the above numbers, my plan so far would get from 37 seconds to 10 seconds, <BR> but getting down to 1 second or less may be viable. <BR>
<BR>I should run these tests on MacOSX/PowerPC and Linux/PowerPPC too..<BR>
<BR>Since that .265 number is wierd, let's run again:<BR>
<BR>current behavior:<BR> vfork/exec(sh -c ./cygnothing) 37.669000<BR>
 <BR>
lose fork/exec, keep sh wrapper<BR> spawn(nowait, sh -c ./cygnothing)/waitpid 26.823000<BR>
 <BR>
similarly, lose cygwin startup code (hm!)<BR> vfork/exec(sh -c ./ms1nothing) 27.941000<BR>
 <BR>
lose fork/exec again (again notice cygwin1.dll dependency hurts!)<BR> spawn(nowait, sh -c ./ms1nothing)/waitpid 17.595000<BR>
 <BR>
lose sh wrapper<BR> vfork/exec(.\cygnothing) 20.612000<BR>
 <BR>
lose sh wrapper and fork/exec<BR> spawn(nowait, .\cygnothing)/waitpid 9.965000<BR>
 <BR>
and again faster cases of 1 second or less:<BR> msvcrt_system(.\cygnothing) 1.172000<BR> spawn(nowait, .\ms1nothing)/waitpid 0.531000<BR> spawn(wait, .\ms1nothing) 0.594000<BR> msvcrt_system(.\ms1nothing) 1.297000<BR> spawn(nowait, .\ms2nothing)/waitpid 0.999000<BR> spawn(wait, .\ms2nothing) 0.860000<BR> msvcrt_system(.\ms2nothing) 1.047000<BR> spawn(nowait, .\ms3nothing)/waitpid 0.562000<BR> spawn(wait, .\ms3nothing) 0.688000<BR> msvcrt_system(.\ms3nothing) 1.203000<BR>
 <BR>
Lastly, let's try CreateProcess directly.<BR>Ok, CreateProcess gets under .4 seconds consistently running cygnothing, but cygwin_winpid_to_pid does not work.<BR>
<BR>I'll see what I can do. It'd be nice to get to the 1 or .5 second case, and not just the 10 second case.<BR>Though 10 is still preferable to 37.<BR>
<BR>Btw, notice, no more annoying ads in my email! :)<BR>
<BR> - Jay<BR><BR><BR><BR><BR>
<BLOCKQUOTE>
<HR id=EC_stopSpelling>
From: jayk123@hotmail.com<BR>To: m3devel@elegosoft.com<BR>Subject: RE: possible cygwin createprocess/fork cost measurements..<BR>Date: Mon, 17 Mar 2008 14:21:05 +0000<BR><BR>
<META content="Microsoft SafeHTML" name=Generator>
<STYLE>
.ExternalClass .EC_hmmessage P
{padding:0px;}
.ExternalClass EC_body.hmmessage
{font-size:10pt;font-family:Tahoma;}
</STYLE>
I believe cm3 is affected by this.<BR>I don't have numbers yet.<BR> <BR>I propose some fairly obvious/small/simple/safe changes in order to likely achieve a large speed up in NT386GNU.<BR>I am skeptical that existing functions can be changed in a compatible enough way.<BR> <BR>So I propose, roughly:<BR> <BR>Add this to Process.i3:<BR> <BR>PROCEDURE Spawn(cmd: Pathname.T; READONLY params: ARRAY OF TEXT) : T RAISES {OSError.E};<BR>(* A restricted form of Create that is much faster on Cygwin. *)<BR> <BR>The name is very iffy.<BR>It could be in fact not be in the public interface, but merely notice if wd = stdin = stdout = stderr = nil.<BR>It could probably be in be less limited than shown.<BR>Probably all of the parameters are settable, by altering the parent's globals, within a critical section.<BR>Environment certainly is settable.<BR>It is tempting to leave it limited like this though, such as to be implementable perhaps with system.<BR>(It turns out Cygwin system is slower than spawnve; surprising since system is the most limited of the exec/spawn variants -- I think related to it having an implied sh wrapper but the others do not.)<BR><BR>The intent is simple and obvious -- some path to spawnve or spawnvpe.<BR>p has path search.<BR> <BR>On all but Cygwin, this limited Create/Spawn will just call the normal Create. (Even on Win32).<BR>On Cygwin it will call spawnvpe (or spawnve if people really want, but "p" seems "better").<BR> <BR>Now, in Quake, all the existing exec variants wrap the command line in either sh or cmd or command.com.<BR>Changing that is probably very dangerous, even with an attempt to discern if the wrapper buys anything, on a command line by command line basis.<BR>For example, if all of the characters * ? | < > % $ & ; ( ) are absent from the command, the shell wrapper probably doesn't buy anything and could be removed from existing paths. However that's not true -- for example system("echo foo") depends on a shell wrapper to run the builtin "echo" (at least on Windows, there is no echo.exe).<BR> <BR>I think there's no choice but to add a new Quake function, spawn, or limited_exec, or fast_exec, or process_runfast, exec_noshell, or something.<BR>Again I'm not sure what to call it, but it'd simply call Process.Spawn, or Process.Create but with right the right parameters to get into the Cygwin fast path.<BR><BR>For now I'm going with Process.Spawn and fast_exec.<BR>I hope to have numbers "soon" as to the perf change.<BR> <BR>Another good option here, that I tried in the past but failed, and is partly not difficult to solve, but also partly, is to implemet Quake exec using Win32 CreateProcess instead of Cygwin spawn/exec. There are at least two sets of problems here. One is that the existing code returns a File.T, and for that there is the Posix and Win32 types, Cygwin uses Posix. You'd have to warp the code somehow here. I gave up on that point without much trying. It's not that much code though. Cygwin is using CreateProcess of course.<BR>The other problem is on the input, the interpretation of the command line. Again this is the value that presently Cygwin provides (albeit sometimes with great cost).<BR> <BR>Of course another angle is to work on Cygwin to make vfork efficient. It is presently implemented by calling fork.<BR>There is #ifdef'ed code for another path but it appears not enabled.<BR> <BR>I know polluting the system just for the sake of Cygwin isn't great, however:<BR> - I expect the win is quite large <BR> - "spawn*" is a pretty old thing, nothing new/controversial here, long known as an often viable replacement for fork+exec at least on Windows.<BR>    It's in msvc*.dll for example.<BR> <BR>There may even be wins to be had on other Posix systems by avoiding the sh wrapper?<BR> <BR>"batching" where cm3cg is run once per directory seems like a very good idea and worth trying; the problem is, that still leaves the assembler.<BR>Perhaps the assembler could be linked in statically to cm3cg? Probably, but not particularly easily and probably unpopular upstream...<BR>Unless maybe some nice gcc perf gains would be demonstrated?<BR> <BR> - Jay<BR>
<BLOCKQUOTE>
<HR id=EC_EC_stopSpelling>
From: jayk123@hotmail.com<BR>To: m3devel@elegosoft.com<BR>Subject: possible cygwin createprocess/fork cost measurements..<BR>Date: Mon, 17 Mar 2008 10:14:50 +0000<BR><BR>
<STYLE>
.ExternalClass .EC_hmmessage P
{padding:0px;}
.ExternalClass EC_body.hmmessage
{font-size:10pt;font-family:Tahoma;}
</STYLE>
 I ran some mostly scientific measures of Cygwin. <BR> On one machine, no reboots, one OS, one set of files.<BR> x86, single proc, Windows 2000 (I'll go back to XP soon). <BR> It shows that..well, at least that wrapping Cygwin processes with sh is VERY expensive. <BR> Like, the data isn't yet complete, but this could cut building Cygwin libm3 <BR> from around 100 seconds to around 20 seconds. Not counting the Modula-3 front end time. <BR> Just cm3cg+as.<BR><BR> cd libm3\NT386GNU <BR> having already built successfully, all the *.ic *.mc files are present <BR> <BR><BR> cm3cg not wrapped with sh (F1) <BR> Repeated runs. <BR>  28 seconds (other stuff running on machine)  <BR>  16 seconds <BR>  13 seconds (13.?) <BR>  13.8 seconds <BR>  14.01 seconds <BR>  13.3 seconds <BR>   now add the -o flag <BR>  13.64 seconds <BR>  14.07 seconds <BR>   now without echoing <BR>  13.22 seconds <BR>  13.18 seconds  <BR> <BR><BR> cm3cg wrapped with sh (F2) <BR> 51 seconds <BR> 51.35 seconds <BR> 51.19 seconds <BR> 50.88 seconds <BR>  now add the -o flag <BR> 51.76 seconds  <BR>  now without echoing <BR> 51.05 seconds <BR> These runs did NOT have -o flags, but subsequent runs with -o were about the same. <BR> I added -o so I could run the as variations. <BR> now the same with .s <BR> note that due to the way the above worked, I just have *.s files, and <BR> not the usual *.is and *.ms  <BR> <BR><BR> as not wrapped with sh (F3) <BR>  5.6 seconds <BR>  5.28 seconds <BR>    now remove echo <BR>  5.08 seconds <BR>  5.08 seconds <BR>  5.04 seconds <BR>   forgot -o flag, oh well, enough data <BR> as wrapped with sh (F4) <BR>  43 seconds <BR>  43.56 seconds <BR>   forgot -o flag, oh well, enough data  <BR> What is not yet confirmed is: <BR>  1) Does cm3 wrap everything with sh? <BR>  2) Does calling m3cg/as from cm3 have these costs? <BR> Very clear: <BR>   Wrapping stuff with sh on Cygwin is expensive! <BR> <BR> <BR> Actions: <BR>   Confirm this cost is being paid by cm3.  <BR>   Either:  <BR>    1) implement some "batch modes" in cm3 and/or cm3cg  <BR>    2) or maybe, um, just make sure that cm3 does not wrap with sh, and  <BR>      if cm3 itself causes this slowdown, because of how Cygwin works, try  <BR>      interposing a small Win32 helper app. I think Cygwin handles runnig  <BR>      Win32 apps and being run from Win32 apps differently than Cygwin running  <BR>      Cygwin -- i.e. not slowly. I'll see. Could be that creating twice the number <BR>      of processes, in order to avoid Cygwin running Cygwin, could be faster. Not yet known. <BR> <BR> Maybe use system() instead vfork() + exec()? Odd, though, vfork instead of fork is supposed to help.<BR><BR>  Here is the test code, you edit it to run one case or another: <BR> <BR> <BR>@if not "%1" == "" goto :%1<BR>@rem \cygwin\bin\time cmd /c %~f0 F1<BR>@rem \cygwin\bin\time cmd /c %~f0 F2<BR>@rem \cygwin\bin\time cmd /c %~f0 F3<BR>@\cygwin\bin\time cmd /c %~f0 F4<BR>@goto :eof<BR> <BR> <BR>:F1<BR>@echo off<BR>@del *s *o<BR>for %%a in (*.ic *.mc) do cm3cg -quiet %%a -o %%a.s"<BR>goto :eof<BR> <BR> <BR>:F2<BR>@echo off<BR>@del *s *o<BR>for %%a in (*.ic *.mc) do sh -c "cm3cg -quiet %%a -o %%a.s"<BR>goto :eof<BR> <BR> <BR>:F3<BR>@del *o<BR>@echo off<BR>for %%a in (*.s) do as %%a<BR>goto :eof<BR> <BR> <BR>:F4<BR>@del *o<BR>for %%a in (*.s) do sh -c "as %%a"<BR>goto :eof<BR> <BR><BR> - Jay<BR><BR><BR><BR>
<HR>
Helping your favorite cause is as easy as instant messaging. You IM, we give. <A href="http://im.live.com/Messenger/IM/Home/?source=text_hotmail_join" target=_blank>Learn more.</A> </BLOCKQUOTE><BR>
<HR>
Connect and share in new ways with Windows Live. <A href="http://www.windowslive.com/share.html?ocid=TXT_TAGHM_Wave2_sharelife_012008" target=_blank>Get it now!</A> </BLOCKQUOTE></body>
</html>