BBC BASIC
« Writing files »

Welcome Guest. Please Login or Register.
Jan 20th, 2018, 6:04pm


Cross-platform BBC BASIC (Win32, Linux x86, Android, Mac OS-X, Raspberry Pi)

« Previous Topic | Next Topic »
Pages: 1  Notify Send Topic Print
 thread  Author  Topic: Writing files  (Read 417 times)
Leo
New Member
Image


member is offline

Avatar




PM


Posts: 10
xx Writing files
« Thread started 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%
 
User IP Logged

Richard Russell
Administrator
ImageImageImageImageImage


member is offline

Avatar




Homepage PM


Posts: 689
xx Re: Writing files
« Reply #1 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.
User IP Logged

Leo
New Member
Image


member is offline

Avatar




PM


Posts: 10
xx Re: Writing files
« Reply #2 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.
User IP Logged

Pages: 1  Notify Send Topic Print
« Previous Topic | Next Topic »

Donate $6.99 for 50,000 Ad-Free Pageviews!


This forum powered for FREE by Conforums ©
Sign up for your own Free Message Board today!
Terms of Service | Privacy Policy | Conforums Support | Parental Controls