Path: news.cs.au.dk!not-for-mail From: Peter Andersen Newsgroups: comp.lang.beta Subject: Re: Garbage Collector vs. cStruct, ExternalRecord, @@ Date: 11 Feb 1999 10:27:31 -0000 Organization: Mjolner Informatics ApS Lines: 163 Approved: mailtonews@cs.au.dk Distribution: world Message-ID: <19990211102731.4091.qmail@noatun.mjolner.dk> Reply-To: Peter Andersen NNTP-Posting-Host: daimi.cs.au.dk X-Trace: xinwen.cs.au.dk 918728867 12415 255.255.255.255 (11 Feb 1999 10:27:47 GMT) X-Complaints-To: news@cs.au.dk NNTP-Posting-Date: 11 Feb 1999 10:27:47 GMT Xref: news.cs.au.dk comp.lang.beta:11817 Karl Waclawek writes: > > > Can anybody tell me how the GC in BETA works with respect > to cStruct patterns and the @@ operator? Basically, my > question is about the difference between passing a reference > (pointer) to a cStruct to an external procedure or passing > the address directly (using the @@ operator). The @@ operator is not an official feature, and the use of it is discouraged. Unless you have detailed knowledge of the allocation strategies and how they may trigger garbage collection, the result of using @@ may easily be a segmentation fault. In your code below you ask why the code using cStruct is slower than the code using @@. The answer lies in the fact that ... -> TestBuf.putByte currently is implemented as ... -> &TestBuf.putByte i.e. NOT being inlined. This means that you will get one million objects allocated during the for-loop. If you instead use pb: @TestBuf.putByte; do (for ... repeat ... -> pb; for) you will get a significant speedup, since the same putByte object is reused for each iteration. I tried it with approximately 35% speed-up as a result. However, there is one flaw: Inside the implementation of cStruct.PutByte there is an invocation of a virtual named ChkBounds (which implements index check - notice that raw use of @@ does not do this). The invocation of this virtual will also instantiate one object per call of putByte. Unfortunately this is a bit harder to work around - actually you have to change the file basiclib/betaenv.bet (causing recompilation of everything thereafter). The change could be to declare one static instance of ChkBounds: chk: @ChkBounds; and then using this in the implementation of the put- and get-operations in basiclib/external.bet. Feel free to try this in your local installation. It may require a recursive change of permissions to allow the compiler to generate new .ast(L) and ..o files. I tried it in my local installation, and combined with the optimization mentioned above, I gained a 70% speedup. > > Example with cStruct (buffer initialization is very slow): > > ORIGIN '~beta/basiclib/v1.6/betaenv'; > INCLUDE '~beta/basiclib/v1.6/external' > '~beta/basiclib/v1.6/numberio'; > LIBFILE nti '$/crc.lib'; > -- program: Descriptor -- > (# > BufType: cStruct (# byteSize::< (# do 1000000->Value #) #); > IntegerStruct: cStruct > (# byteSize::< (# do 4->Value #); > val: Long (# pos::< (# do 0->Value #) #); > enter val > exit val > #); > GetCRC32: external > (# BufP: ^BufType; ByteCount: @Integer; CRC: ^IntegerStruct; > enter (BufP[],ByteCount,CRC[]) > do callStd > #); > TestBuf: @BufType; > TestCRC: @IntegerStruct; > do (* initialize Buffer with some data - very slow *) > (for I: TestBuf.byteSize repeat (I-1,I mod 256) -> TestBuf.putByte; for); > 16xFFFFFFFF->TestCRC; (* initialize CRC *) > (TestBuf[],TestBuf.byteSize,TestCRC[])->GetCRC32; > (16,TestCRC)->putBaseD; (* print result *) > #) > > Example with @@ (buffer initialization is fast): > > ORIGIN '~beta/basiclib/v1.6/betaenv'; > INCLUDE '~beta/basiclib/v1.6/external' > '~beta/basiclib/v1.6/numberio'; > LIBFILE nti '$/crc.lib'; > -- program: Descriptor -- > (# > BufType: (# R: [1000000] @Char #); > GetCRC32: external > (# BufPtr, ByteCount, CRCPtr: @Integer; > enter (BufPtr,ByteCount,CRCPtr) > do callStd > #); > TestBuf: @BufType; > TestCRC: @Integer; > > do (* initialize Buffer - is a lot faster than above *) > (for I: 1000000 repeat (I mod 256) -> TestBuf.R[I]; for); > 16xFFFFFFFF->TestCRC; (* initialize CRC *) > (@@TestBuf.R[1],1000000,@@TestCRC)->GetCRC32; > (16,TestCRC)->putBaseD; > #) > > This code looks simpler (and is faster), but could the garbage collector > potentially free the TestBuf pattern? > Especially in this case (BufPtr, CRCPtr declared as @Integer): > ... > @@TestBuf.R[1] -> BufPtr; > @@TestCRC -> CRCPtr; > 16xFFFFFFFF->TestCRC; > (BufPtr,1000000,@@TestCRC)->GetCRC32; > ... > the GC might assume that TestBuf and TestCRC are not use anymore after > the first two lines in the code sample above, and might therefore mark > them for deletion, possibly causing access violations in the external call. The TestBuf is declared static in the Program SLOT. So as long as the Program object exists (i.e. the lifetime of your program), the TestBuf will exist. But as said, the use of @@ may easily cause access violations for other reasons. And since GC almost never happens at exact the same spots in the program, the behaviour may actually turn out to be non-deterministic. > > Can anybody shed some light on this? > Is the purpose of cStruct to prevent problems with the GC? Exactly. The idea is to encapsulate the "greasy" lowlevel stuff in a high level construct. Ideally it should be (almost) as efficient as using the low-level approach directly, but in this case unfortunately the index- check has not been proporly implemented. It will be fixed in a later release. > > Karl > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > Karl Waclawek > KD Soft Inc. > * Phone: (905) 579-3443 > * E-Mail: waclawek@idirect.com > BTW: Sorry for the late answer. Sincerely, Peter Andersen, Mjolner Informatics