XFELReader: a deflated frame is inflated and converted a piece at a time, while it is in the cache
The reader inflated a whole deflated chunk (4 MB for a 1 Mpx int32 frame, 2 MB for its uint16 per-frame mask) into a buffer and then converted it in a second pass; with 16 reading threads those buffers never stay in the cache, so the conversion ran at memory speed. A deflated, unshuffled chunk is now inflated in 128 kB pieces (InflateInPieces, the zlib stream API - the stream's checksum is checked as before), and each piece is converted into the image, or marked in the mask, before the next is inflated. Shuffled, bitshuffle/LZ4, HDF5-read and single-panel (placed) frames are converted as a whole, as before. Bit-identical: the FNV pixel hashes of 2000 frames on the five reader configurations of the previous commit are unchanged, and a new test reads 300 x 300 frames (several pieces, a final partial one) with a per-frame mask and a gain pixel by pixel. Reader alone, 16 threads, 3000 frames: 2.43 -> 1.73 s (the previous commit's state -> this). rugnux on the 14,445-frame CXI run, against rc178 (A/B, two runs each): geometry refinement sample 11.6 -> 5.1 s, image pass 27.7 -> 12.9 s, whole run 67.7 -> 46.2 s; the geometry refinement, module placement and tilt log lines are identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
1 parent
d345deaecd
commit
f5a990dc8d
3 files changed
+185
-49
No files matched your search
@@ -4,6 +4,7 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
@@ -383,6 +384,78 @@ bad_p1/panel = p1
|
||||
std::filesystem::remove_all(dir);
|
||||
}
|
||||
|
||||
TEST_CASE("JFJochXFELReader_DeflatedInPieces", "[portable]") {
|
||||
// Frames large enough to be inflated in several pieces (a 300 x 300 int32 frame and its uint16 per-frame
|
||||
// mask, deflated without the shuffle), read with a gain: every pixel is the stored value in photons,
|
||||
// rounded half away from zero, or no measurement where the per-frame mask marks it.
|
||||
const auto dir = std::filesystem::path("xfel_reader_in_pieces");
|
||||
std::filesystem::create_directories(dir);
|
||||
const std::string name = (dir / "frames.cxi").string();
|
||||
constexpr size_t side = 300, n = side * side;
|
||||
std::vector<int32_t> frames(2 * n);
|
||||
for (size_t i = 0; i < frames.size(); i++)
|
||||
frames[i] = static_cast<int32_t>(i % 1001) - 500;
|
||||
std::vector<uint16_t> mask(2 * n, 0);
|
||||
for (size_t i = 0; i < mask.size(); i += 997)
|
||||
mask[i] = 0x4;
|
||||
{
|
||||
const hid_t f = H5Fcreate(name.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
|
||||
for (const char *g: {"entry_1", "entry_1/data_1", "entry_1/instrument_1", "entry_1/instrument_1/source_1"})
|
||||
H5Gclose(H5Gcreate2(f, g, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT));
|
||||
WriteFrames(f, "entry_1/data_1/data", H5T_NATIVE_INT32, frames.data(), {2, side, side}, false);
|
||||
WriteFrames(f, "entry_1/data_1/mask", H5T_NATIVE_UINT16, mask.data(), {2, side, side}, false);
|
||||
WriteVector(f, "entry_1/instrument_1/source_1/energy", std::vector<double>(2, 9000.0 * 1.602176634e-19));
|
||||
H5Fclose(f);
|
||||
}
|
||||
const std::string geom = (dir / "frames.geom").string();
|
||||
std::ofstream(geom) << R"(
|
||||
clen = 0.1
|
||||
res = 10000
|
||||
dim0 = %
|
||||
dim1 = ss
|
||||
dim2 = fs
|
||||
data = /entry_1/data_1/data
|
||||
mask = /entry_1/data_1/mask
|
||||
mask_good = 0x0
|
||||
mask_bad = 0xffff
|
||||
p0/min_fs = 0
|
||||
p0/max_fs = 149
|
||||
p0/min_ss = 0
|
||||
p0/max_ss = 299
|
||||
p0/fs = x
|
||||
p0/ss = y
|
||||
p0/corner_x = -150
|
||||
p0/corner_y = -150
|
||||
p1/min_fs = 150
|
||||
p1/max_fs = 299
|
||||
p1/min_ss = 0
|
||||
p1/max_ss = 299
|
||||
p1/fs = x
|
||||
p1/ss = y
|
||||
p1/corner_x = 0
|
||||
p1/corner_y = -150
|
||||
)";
|
||||
|
||||
JFJochXFELReader reader;
|
||||
REQUIRE_NOTHROW(reader.ReadFiles({name}, geom, 2.0f));
|
||||
REQUIRE(reader.GetNumberOfImages() == 2);
|
||||
for (size_t image = 0; image < 2; image++) {
|
||||
auto raw = reader.GetRawImage(static_cast<int64_t>(image));
|
||||
REQUIRE(raw);
|
||||
REQUIRE(raw->image.GetWidth() * raw->image.GetHeight() == n);
|
||||
const auto *pixels = reinterpret_cast<const int32_t *>(raw->image_buffer.data());
|
||||
size_t wrong = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const size_t k = image * n + i;
|
||||
const int32_t expected = mask[k] ? INT32_MIN : static_cast<int32_t>(std::lround(frames[k] / 2.0f));
|
||||
wrong += pixels[i] != expected;
|
||||
}
|
||||
CHECK(wrong == 0);
|
||||
}
|
||||
reader.Close();
|
||||
std::filesystem::remove_all(dir);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// An LCLS-I XTC stream of the shape JFJochXTCReader reads: a Configure datagram, then one event (an
|
||||
|
||||
Reference in new issue
Block a user