Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UBENCH_F -- just cant make it work? #6

Closed
DBJDBJ opened this issue Oct 18, 2020 · 10 comments
Closed

UBENCH_F -- just cant make it work? #6

DBJDBJ opened this issue Oct 18, 2020 · 10 comments

Comments

@DBJDBJ
Copy link

DBJDBJ commented Oct 18, 2020

The fixture mechanism just does not work in this file ...

UBENCH_F goes into endless recursion or whatever similar to that, UBENCH_F_SETUP does not get called? UBENCH in the same file works.

There is a little Win framework in that project, that catches everything and generates a minidump from where one can pinpoint the problem. And the problem is stack exhaustion upon entering the UBENCH_F

that is VS Code build using CL

@sheredom
Copy link
Owner

If it goes into endless recursion you'd get a crash with stack overflow - so I'm not sure it is that!

@DBJDBJ
Copy link
Author

DBJDBJ commented Oct 18, 2020

if looking into test11.cpp it all works and looks simple .and very similar to my file ..

this is a windows project catching Structured Exceptions. nothing gets past that. minidump is generated and one can use it with Visual Studio to see what and where exactly has happened,

It is a stack overflow

image

@sheredom
Copy link
Owner

Ah nice! Let me try and work it out 😄

@DBJDBJ
Copy link
Author

DBJDBJ commented Oct 18, 2020

Line 599 is where the call happens. As soon as it goes into that function properly created function __chkstk() intrinsic gets called and kicks-the-bucket ..

image

in that file, there are some static globals but all is innocently looking? And the same stuff works blisfully in the same file but from a simple UBENCH

@sheredom
Copy link
Owner

So I tried on a whim to call the setup code again in the UBENCH_F and everything worked.

Remember that the setup code is called once for more than one run of the UBENCH_F - so at a guess the hash table is being filled up and then its looping forever in there somehow? If I reset the hash data it seems to work.

@DBJDBJ
Copy link
Author

DBJDBJ commented Oct 18, 2020

I hope you are building using MSVC aka CL.exe and running on Win10. My setup code is never called. Not once or more than once.

Yes, that is my bad logic as that is not "set up once use always" kind of setup. Perhaps this episode deserves some more text in the readme :) But after we are sure technically this all works. On the WIN10 machine compile with the CL.exe.

As far as I can see while in a debugger something is wrong and hidden behind a UBENCH_F macro. Debugger stops before calling it but the next step provokes a response from __chkstk() function intrinsic to the cl.exe

@DBJDBJ
Copy link
Author

DBJDBJ commented Oct 18, 2020

So I have created a version with what macros are expanding to. It compiles and shows the stack is not functioning before even going into the first call of this benchmark loop. Stack exception is raised upon a first call of ubench_f_testu_dbj_data_dbj_uniques_arr

this is the file :

// (c) dbj.org -- DBJ_LICENSE -- https://dbj.org/dbj_license
#include <stdint.h>
#include <time.h>

#include "../ubench.h/ubench.h"

#ifndef _MSC_VER
#error This is Windows build only
#endif

//
// the way of 'fixtures' but without macros
// so that one can see what is happening
// on windows build
//

// https://codingforspeed.com/c-coding-example-using-hash-algorithm-to-remove-duplicate-number-by-on/

enum { TESTU_HASHSIZE = 1048576 }; // 20MB
static const int TESTU_HASH = TESTU_HASHSIZE - 1;
static int hasharr[TESTU_HASHSIZE];

// the data numbers can't be the value of TESTU_HASHSIZE,
enum { FREE_SLOT_MARK = TESTU_HASHSIZE };
enum { TESTU_MAXN = 0xFFFF };

#ifndef _WIN64
static int hashKey(long x) {
  x = ((x >> 16) ^ x) * 0x45d9f3b;
  x = ((x >> 16) ^ x) * 0x45d9f3b;
  x = (x >> 16) ^ x;
  return x;
}
#else  // _WIN64
static uint64_t hashKey(uint64_t x) {
  x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
  x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
  x = x ^ (x >> 31);
  return x;
}
#endif // _WIN64
/******************************************************************/
// this is the 'fixture'
struct testu_dbj_data {
  int TESTU_HASH /* = TESTU_HASHSIZE - 1 */;
  int hasharr[TESTU_HASHSIZE];
  long data[TESTU_MAXN] /* = { 0L }*/;
};
/******************************************************************/

static void *produce_uniques(struct testu_dbj_data *ubench_fixture) {
  int count = 0;
  for (int i = 0; i < TESTU_MAXN; i++) {
    int key = hashKey(ubench_fixture->data[i]); // unbound hash key
    key &= TESTU_HASH;                          // bound it to hash index
    for (;;) {
      if (ubench_fixture->hasharr[key] == FREE_SLOT_MARK) { // slot is available
        ubench_fixture->hasharr[key] = i; // means the number appears first time
        break;
      } else if (ubench_fixture->data[hasharr[key]] ==
                 ubench_fixture->data[i]) { // same number
        count++;
        break;
      } else {
        key = (key + 1) & TESTU_HASH; // different number, collision happens
      }
    }
  }

  return ubench_fixture;
}

// this is called once from the loop function
#if 0
UBENCH_F_SETUP(testu_dbj_data)
#else
static void ubench_f_setup_testu_dbj_data(struct testu_dbj_data *ubench_fixture)
#endif
{
  ubench_fixture->TESTU_HASH = TESTU_HASHSIZE - 1;
  // make random data
  srand(time(NULL));
  for (int i = 0; i < TESTU_MAXN; i++)
    ubench_fixture->data[i] = rand() % TESTU_MAXN;
  // init the hash table by using the special value
  for (int i = 0; i < TESTU_HASHSIZE; i++)
    ubench_fixture->hasharr[i] = FREE_SLOT_MARK;

  printf("\nLeaving %s", __FUNCSIG__);
}

#if 0
UBENCH_F_TEARDOWN(testu_dbj_data)
#else
static void
ubench_f_teardown_testu_dbj_data(struct testu_dbj_data *ubench_fixture)
#endif
{
  memset(ubench_fixture->data, 0L, __crt_countof(ubench_fixture->data));
  memset(ubench_fixture->hasharr, 0, __crt_countof(ubench_fixture->hasharr));
}

#if 0
UBENCH_F(testu_dbj_data, dbj_uniques_arr ) 
{
    UBENCH_F_SETUP(testu_dbj_data) ;
    produce_uniques( ubench_fixture ) ;
}

#else

extern struct ubench_state_s ubench_state;

static void ubench_f_setup_testu_dbj_data(struct testu_dbj_data *);

static void ubench_f_teardown_testu_dbj_data(struct testu_dbj_data *);

static void ubench_run_testu_dbj_data_dbj_uniques_arr(struct testu_dbj_data *);

/*
in here is the loop
this is registered as a benchmark function
*/
static void ubench_f_testu_dbj_data_dbj_uniques_arr
(ubench_int64_t *const ns, const ubench_int64_t size) 
{
  ubench_int64_t i = 0;
  struct testu_dbj_data fixture;
  memset(&fixture, 0, sizeof(fixture));
  /* setup is called from here -- once */
  ubench_f_setup_testu_dbj_data(&fixture);
  for (i = 0; i < size; i++) {
    ns[i] = ubench_ns();
    ubench_run_testu_dbj_data_dbj_uniques_arr(&fixture);
    ns[i] = ubench_ns() - ns[i];
  }
  ubench_f_teardown_testu_dbj_data(&fixture);
}

/*
this is the code for registering the benchmark
*/
static void __cdecl ubench_register_testu_dbj_data_dbj_uniques_arr(void);

__pragma(comment(linker, "/include:"
                         "ubench_register_testu_dbj_data_dbj_uniques_arr"
                         "_"));

__declspec(allocate(".CRT$XCU")) void(
    __cdecl *ubench_register_testu_dbj_data_dbj_uniques_arr_)(void) =
    ubench_register_testu_dbj_data_dbj_uniques_arr;

/*
this is the registration function itself
this *is* called before main starts
*/
static void __cdecl ubench_register_testu_dbj_data_dbj_uniques_arr(void) {
  const size_t index = ubench_state.benchmarks_length++;
  const char *name_part = "testu_dbj_data"
                          "."
                          "dbj_uniques_arr";
  const size_t name_size = strlen(name_part) + 1;
  char *name = ((char *)malloc(name_size));
  ubench_state.benchmarks = ((struct ubench_benchmark_state_s *)realloc(
      ((void *)ubench_state.benchmarks),
      sizeof(struct ubench_benchmark_state_s) *
          ubench_state.benchmarks_length));
  ubench_state.benchmarks[index].func =
      &ubench_f_testu_dbj_data_dbj_uniques_arr;
  ubench_state.benchmarks[index].name = name;
  _snprintf_s(name, name_size, name_size, "%s", name_part);
}

/*
this is called from the loop
*/
void ubench_run_testu_dbj_data_dbj_uniques_arr(
    struct testu_dbj_data *ubench_fixture) {
  // UBENCH_F_SETUP(testu_dbj_data) ;
  produce_uniques(ubench_fixture);
}

#endif // ! 0

@sheredom
Copy link
Owner

I think the issue might be that in the UBENCH_F I stack allocate the fixture struct, and your fixture struct is massive because of the big arrays in it, so it stack overflows. Can you try heap allocating those members instead?

@DBJDBJ
Copy link
Author

DBJDBJ commented Oct 19, 2020

Probably you are right. But after almost 12 hours of fiddling-diddling :) I have decided to give up and I am going to use (again) your ubench and utest as submodules. Not my forks.

Going forward, as we have vividly seen (I think) the "fixture" feature just complicates the things and should be removed. Furthermore, utest and ubench might be better of as one header as they share common "things".

One more "detail" -- the whole thing (or is it now the whole new thing) can be implemented with no heap used. That will win a lot of new followers.

@DBJDBJ
Copy link
Author

DBJDBJ commented Oct 19, 2020

Roling back :) Just add this somewhere inside your main for the time being

#ifdef _MSC_VER
// DBJ: switch on VT100
// horrible but works on WIN10
// on other versions has no visible effect
system(" ");
#endif // _MSC_VER

:)

@DBJDBJ DBJDBJ closed this as completed Oct 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants