BBC BASIC
General >> Support and Promote >> Writing files
http://bbcbasic.conforums.com/index.cgi?board=support&action=display&num=1480795793

Writing files
Post by Leo on Dec 3rd, 2016, 7:09pm

I'm a relative newcomer to BB4W (although I'm a professional developer) but I'm having a blast - more fun than I had for years,
so I thought I would post something, even if it's more 'interesting' than 'useful'.

I came across a post in one of the other compiler forums that set a performance challenge:
- write 100 files
- each file containing 1000000 different random bytes
- max file buffer allowed = 1000 bytes
- as fast as possible.

so I thought I'd translate it to BB4W.

A very simple implementation ran in 30.96 sec (which doesn't sound too bad for an interpreter).
However, I tried to speed things up and the version using Windows API runs in 2.75 sec.
If I disregard the buffer size limit and set the buffer = 10000 bytes, it does it in 1.78 sec.
Just shows what an interpreter can be made to do.

The code contains both the simple version and the faster one. Change source where noted to run either version.
I expect this can be improved by the more experienced BB4W hands here. Looking forward to it.

Code:
      REM Filewrite performance benchmark 03-dec-2016      REM Test specification:      REM Files written to contain random bytes      FILESTOWRITE% = 100      FILESIZE% = 1000000      BUFFSIZE% = 1000      REM Only used with the fast method      REM ------------------------------      REM!WC      CRYPT_SILENT = 64      PROV_RSA_FULL = 1      DIM Buff% BUFFSIZE%+1      hProvider% = 0      REM ------------------------------      REM IF 1 runs the simple version, IF 0 runs the faster one      IF 0 THEN        Start=TIME        REM 30.96 sec        FOR I% = 1 TO FILESTOWRITE%          f = OPENOUT("f"+STR$(I%)+".dum")          FOR Cnt%=1 TO FILESIZE%            BPUT#f,RND(256)-1          NEXT          CLOSE#f        NEXT      ELSE        REM Making bufsize larger further reduces the time. Bufsize = 10000 gives 1.78 sec        Start=TIME        REM 2.75 sec        SYS "GetModuleHandle", "ADVAPI32" TO ADV%        SYS "GetProcAddress", ADV%, "CryptGenRandom" TO `CryptGenRandom`        SYS "GetModuleHandle", "KERNEL32" TO K32%        SYS "GetProcAddress", K32%, "WriteFile" TO `WriteFile`        IF FN_GetContext = 0 PRINT "No context": END        FOR I% = 1 TO FILESTOWRITE%          f = OPENOUT("f"+STR$(I%)+".dum")          C% = FILESIZE%          B% = BUFFSIZE%          WHILE C% > 0            IF C% < B% B%=C%            IF FN_RandData(B%) = 0 PRINT "Data failed": END            SYS `WriteFile`, @hfile%(f), Buff%, B%, ^temp%, 0            C% -= B%          ENDWHILE          CLOSE#f        NEXT        PROC_RelContext        SYS "FreeLibrary", ADV%        SYS "FreeLibrary", K32%      ENDIF      PRINT TIME-Start      END      DEF FN_GetContext      LOCAL res%      SYS "CryptAcquireContext", ^hProvider%, 0, 0, PROV_RSA_FULL, CRYPT_SILENT TO res%      = res%      REM NOTE: The last 2 params to CryptAcquireContext are really: PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT OR CRYPT_SILENT      REM CRYPT_VERIFY_CONTEXT = 0, CRYPT_SILENT = 0x40, PROV_RSA_FULL = 1      DEF PROC_RelContext      SYS "CryptReleaseContext", hProvider%, 0      ENDPROC      DEF FN_RandData(nbytes%)      LOCAL res%      SYS `CryptGenRandom`, hProvider%, nbytes%, Buff% TO res%      = res% 

Re: Writing files
Post by Richard Russell on Dec 3rd, 2016, 9:17pm

on Dec 3rd, 2016, 7:09pm, Leo wrote:
I expect this can be improved by the more experienced BB4W hands here.

Not an improvement, but a simplification. I appreciate that you don't want to call WriteFile and CryptGenRandom 'conventionally' (by name) because calling by address is faster. But there's an easier way to get their addresses than using GetModuleHandle and GetProcAddress, you can use this function:

Code:
DEF FN_addr(n$):LOCAL P%:DIM P% LOCAL 4:[OPT 0:CALL n$:]= P% + P%!-4 

So in the case of your program you can get the addresses you want as follows:

Code:
        `CryptGenRandom` = FN_addr("CryptGenRandom")        `WriteFile` = FN_addr("WriteFile") 

Richard.

Re: Writing files
Post by Leo on Dec 4th, 2016, 5:32pm

Richard, thank you for the smart function which indeed simplifies the code, but also for the bit of education that it resulted in. I guess the following may be known to BB4W old-timers but it's new to me.

I understood the P%!-4 part - getting the target address from the CALL instruction - but I was intially intrigued by the addition of P%. I noted that the manual mentions relative addressing jumps etc so that kicked off a bit of rummaging around.
I verified that the CALL uses an E8 opcode which takes an address relative to the next instruction (where P% ends up after planting the CALL xxxx) and all became clear. To get the absolute address I needed for SYS, add the next instruction address (P%) to the relative CALL target.

Excellent.