Author |
Topic: Writing files (Read 536 times) |
|
Leo
New Member
member is offline


Posts: 10
|
 |
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%
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline


Posts: 803
|
 |
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.
|
|
Logged
|
|
|
|
Leo
New Member
member is offline


Posts: 10
|
 |
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.
|
|
Logged
|
|
|
|
|