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

Try using fseek before reading a file twice. #1499

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

branc116
Copy link

@branc116 branc116 commented Jun 1, 2024

I was testing something with vulkan and noticed that some files are being read twice.
I was supprised by that. I started digging and I found this:

commit 992d9cb45f181e747a1a942c6aa09bef94fd9428
Author: Lenny Komow <lenny@lunarg.com>
Date:   Tue Dec 1 10:49:01 2020 -0700

    loader: Remove SEEK_END usage
    
    Change-Id: I699caaf048d70756649d9f6a2c7dfb012c6d2342

     }
-    fseek(file, 0, SEEK_END);
+    // NOTE: We can't just use fseek(file, 0, SEEK_END) because that isn't guaranteed to be supported on all systems
+    do {
+        // We're just seeking the end of the file, so this buffer is never used
+        char buffer[256];
+        fread(buffer, 1, sizeof(buffer), file);
+    } while (!feof(file));
     len = ftell(file);

And then I found this:

Contributor cdotstout commented on Oct 9, 2020
On Fuchsia, at one point we observed a problem where this fseek failed, from loader_get_json in loader.c:

fseek(file, 0, SEEK_END);

We fixed the fseek on Fuchsia so this is not a pressing issue, but we considered switching to fstat instead.

I noted here:
http://www.cplusplus.com/reference/cstdio/fseek/

"Library implementations are allowed to not meaningfully support SEEK_END (therefore, code using it has no real standard portability)"

At least, the fseek return code should probably be checked and an error/warning logged if it fails.

So because of new experimental os, everybody using this loader on linux, bsd, windows, ... has to read each json file 2 times.
Idk, in this pull request I'd just like to use fseek and then if that fails, use double read.

I also did a quick benchmark:

NEW 3924 - 4235 - 18022 (min - avg - max microsecs)
OLD 4440 - 4810 - 19318 (min - avg - max microsecs)
#include <stdio.h>
#include <vulkan/vulkan.h>
#include <stdlib.h>

#define HS (1 << 24)
union {
  size_t data[HS];
  char c_data[HS << 3];
} heap;
size_t heap_index = 0;

void* my_alloc(void* ud, size_t size, size_t alignment, VkSystemAllocationScope allocation_scope) {
  void* ret = &heap.data[heap_index];
  heap_index += (size >> 3) + 1;
  return ret;
}

void* my_realloc(void* ud, void* p, size_t size, size_t alignment, VkSystemAllocationScope allocation_scope) {
  void* ret = &heap.data[heap_index];
  heap_index += (size >> 3) + 1;
  return ret;
}

void my_free(void* ud, void* p) {}
void alloc_notif(void* ud, size_t size, VkInternalAllocationType allocation_type, VkSystemAllocationScope allocation_scope) {}
void free_notif(void* ud, size_t size, VkInternalAllocationType allocation_type, VkSystemAllocationScope allocation_scope) {}

VkAllocationCallbacks allocator = {
  .pUserData = 0,
  .pfnAllocation = my_alloc,
  .pfnReallocation = my_realloc,
  .pfnFree = my_free
};

int main(int argc, char** argv) {
  int n = 1;
  if (argc > 1) {
    n = atoi(argv[1]);
  }
  unsigned long long total = 0;
  unsigned long long m = 1LL<<60LL, M = 0;
  for (int i = 0; i < n; ++i) {
    VkInstanceCreateInfo create_info = {0};
    VkInstance instance = {0};
    unsigned long long cur = 0;
    unsigned long long start = __builtin_ia32_rdtsc();
    vkCreateInstance(&create_info, &allocator, &instance);
    unsigned long long end = __builtin_ia32_rdtsc();
    cur = end - start;
    total += cur;
    m = m < cur ? m : cur;
    M = M < cur ? cur : M;
    heap_index = 0;
  }
  printf("%llu - %llu - %llu\n", m / 1000, total / n / 1000, M / 1000);
  return 0;
}

// cc -L $PWD/build/loader -lvulkan test.c && strace ./a.out 2> tmp
// cc -O3 -L $PWD/build/loader -lvulkan ../test.c && LD_LIBRARY_PATH="$PWD/loader" ./a.out

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@CLAassistant
Copy link

CLAassistant commented Jun 1, 2024

CLA assistant check
All committers have signed the CLA.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build queued with queue ID 198556.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2597 running.

Copy link
Collaborator

@charles-lunarg charles-lunarg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good PR! Yes, I concur doing a double read is a bit pointless if not performance intensive.

Definitely something which looks like a good suggestion. There is a current hole in the CI coverage for Windows platforms. But that can be ignored because this code is inside UNIX only codepaths.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2597 passed.

@charles-lunarg
Copy link
Collaborator

Ah yes, about that "hole in CI". The fix for the failing windows CI checks is to rebase on main. The fix for the clang-format check is to run your change through it (or just copy what the CI check expected the code to be formatted as, since that will be the same result).

@gpx1000
Copy link

gpx1000 commented Jun 10, 2024

FWIW, can probably just fall back on using fstat?

@branc116
Copy link
Author

FWIW, can probably just fall back on using fstat?

Initially I changed it this function to use fstat, but then googled and found out that fstat is posix standard and Idk what are the differences between Bsds and linux.
Then I looked for inspiration in other peoples code. Specifically, stb... His implementation also uses fseek, and who am I to question him. xD

Ah yes, about that "hole in CI". The fix for the failing windows CI checks is to rebase on main.

Did this.

The fix for the clang-format check is to run your change through it (or just copy what the CI check expected the code to be formatted as, since that will be the same result).

Also did this. I haven't noticed 4 spaces is used for indentation...

but that can be ignored because this code is inside unix only codepaths.

I don't think it is. It looks like windows is also calling to this. There is an ifdef Windows in the same function -> that means someone expected for this function to be called on windows. Or am I missing something?

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build queued with queue ID 199158.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2598 running.

@charles-lunarg
Copy link
Collaborator

Yep, it is I who misread the code. Yes, this code runs on all platforms, so the lack of windows CI coverage is a bit worrysome.
Hmm if there isn't great consensus on how to do this, I'll need to do a little research before pulling the merge trigger. Just as a sanity check for my own knowledge.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2598 passed.

@gpx1000
Copy link

gpx1000 commented Jun 11, 2024

fstat works in all bsds that I'm aware of including FreeBSD which is Very similar to OSX and why it works in Apple too without change.
Windows would be done with GetFileSizeEx.

However, another option is to use: https://en.cppreference.com/w/cpp/filesystem/file_size

@charles-lunarg
Copy link
Collaborator

fstat would appear to me as the most straight forward path.

I can unilaterally state that anything requiring C++ is a no-go. Not out of ideological issues, but because of requiring C++ would change the required dynamic libraries. In the future, that might change, but not for this type of change.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@branc116
Copy link
Author

With my first commit my goal was primarily to keep compatibility across os-es and to improve performance ( reduce syscalls )
But now you prompted me to reduce syscall even further and to keep compatibility across os-es more or less the same.
In this new commit I introduce new function called read_entire_file.
This function should read the entire file optimally ( using least number of syscalls possible. )
I tested this on my linux machine and it works.

First question is if windows implementation will work

  • I think it should work, but windows unit tests should be enabled and someone should test if it works on windows.
    • I don't really have access to windows machine. And I don't know if vulkan and vm work together...

Second question is if this is optimal windows implementation...

  • I have even less insight into that..

Third question is if this will work on other COMMON_UNIX_PLATFORMS

  • I don't know what are all the targets that Vulkan-Loader targets
  • Posix function that need checking is:
    • open
    • fstat
    • read
  • It'd be cool if we had a table what posix functions are implemented in any posix os-es.

I'd not be comfortable with you pushing this until windows ci is working and until we find posixOS/function table to check everything works...

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build queued with queue ID 199730.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2601 running.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2601 failed.

@branc116
Copy link
Author

Damn, didn't expect so many failed test cases.. I'll look at it tomorrow.

Good tests :)

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build queued with queue ID 200294.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2603 running.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

5 similar comments
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

Copy link
Collaborator

@charles-lunarg charles-lunarg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More style changes but the meat of the code looks good otherwise. Style is to not mix assignment and comparisons in the same line. Just makes everything more consistent.

loader/cJSON.c Outdated
assert(json != NULL);

*json = NULL;
if (VK_SUCCESS != (res = loader_read_entire_file(inst, filename, &json_buf))) goto out;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (VK_SUCCESS != (res = loader_read_entire_file(inst, filename, &json_buf))) goto out;
res = loader_read_entire_file(inst, filename, &json_buf);
if (VK_SUCCESS != res) {
goto out;
}

loader/cJSON.c Outdated

// Can't be a valid json if the string is of length zero
if (len == 0) {
if (NULL == (file = fopen(filename, "rb"))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (NULL == (file = fopen(filename, "rb"))) {
file = fopen(filename, "rb");
if (NULL == file) {

loader/cJSON.c Outdated
res = VK_ERROR_INITIALIZATION_FAILED;
goto out;
}
if (NULL == (*out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (NULL == (*out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND))) {
*out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (NULL == *out_buff) {

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build queued with queue ID 204134.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2615 running.

@ci-tester-lunarg
Copy link

CI Vulkan-Loader build # 2615 failed.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg
Copy link

Author branc116 not on autobuild list. Waiting for curator authorization before starting CI build.

@branc116
Copy link
Author

I changed it :)

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

Successfully merging this pull request may close these issues.

None yet

5 participants