From 54d9e826ff22b49ab503215035d8352a8b6b58d2 Mon Sep 17 00:00:00 2001 From: 2004huwa <1098990084@qq.com> Date: Mon, 7 Sep 2026 22:21:40 +0800 Subject: [PATCH] Perf: buffer shared text CSR output and avoid AtomPair copies --- .../source_base/module_out/sparse_matrix.cpp | 13 +-- .../source_base/test/sparse_matrix_test.cpp | 101 ++++++++++++++++++ .../module_hcontainer/output_hcontainer.cpp | 2 +- .../test/test_hcontainer_output.cpp | 78 ++++++++++++++ 4 files changed, 187 insertions(+), 7 deletions(-) diff --git a/source/source_base/module_out/sparse_matrix.cpp b/source/source_base/module_out/sparse_matrix.cpp index c6d02495d54..535f2bb93b6 100644 --- a/source/source_base/module_out/sparse_matrix.cpp +++ b/source/source_base/module_out/sparse_matrix.cpp @@ -41,23 +41,23 @@ void SparseMatrix::printToCSR(std::ostream& ofs, int precision) size_t count1 = 0; for (const auto &element : elements) { - if(count1%6==0) ofs << std::endl; + if(count1%6==0) ofs << '\n'; count1++; ofs << " " << element.second; } - ofs << std::endl; + ofs << '\n'; // print the CSR column indices ofs << " # CSR column indices"; size_t count2 = 0; for (const auto &element : elements) { - if(count2%16==0) ofs << std::endl; + if(count2%16==0) ofs << '\n'; count2++; ofs << " " << element.first.second; int row = element.first.first; csr_row_ptr[row + 1]++; } - ofs << std::endl; + ofs << '\n'; // Compute the row pointers for (int i = 1; i <= _rows; i++) @@ -69,10 +69,11 @@ void SparseMatrix::printToCSR(std::ostream& ofs, int precision) ofs << " # CSR row pointers"; for (int i = 0; i < csr_row_ptr.size(); i++) { - if(i%16==0) ofs << std::endl; + if(i%16==0) ofs << '\n'; ofs << " " << csr_row_ptr[i]; } - ofs << std::endl << std::endl; + // Keep the completed CSR payload visible to callers without flushing each line. + ofs << '\n' << std::endl; } /** diff --git a/source/source_base/test/sparse_matrix_test.cpp b/source/source_base/test/sparse_matrix_test.cpp index f1f49e5c3b8..583ddddd2f9 100644 --- a/source/source_base/test/sparse_matrix_test.cpp +++ b/source/source_base/test/sparse_matrix_test.cpp @@ -1,6 +1,8 @@ #include "source_base/module_out/sparse_matrix.h" #include +#include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -41,6 +43,105 @@ class SparseMatrixTest : public ::testing::Test using MyTypes = ::testing::Types>; TYPED_TEST_SUITE(SparseMatrixTest, MyTypes); +namespace +{ +class CountingBuffer : public std::stringbuf +{ + public: + int sync_count = 0; + bool fail_sync = false; + + protected: + int sync() override + { + ++sync_count; + return fail_sync ? -1 : std::stringbuf::sync(); + } +}; + +double csr_value(double value) +{ + return value; +} + +std::complex csr_value(std::complex value) +{ + return value + std::complex(0.0, -0.25); +} +} // namespace + +TYPED_TEST(SparseMatrixTest, BufferedCSRPreservesWrappingPrecisionAndFinalFlush) +{ + // Cross both wrapping boundaries (6 values, 16 indices) with an empty row. + ModuleIO::SparseMatrix matrix(18, 18); + std::vector values; + for (int row = 16; row >= 0; --row) + { + matrix.insert(row, 17 - row, csr_value(TypeParam(row + 0.125))); + } + matrix.insert(17, 0, TypeParam(1e-10)); // Equality to the threshold stays absent. + for (int row = 0; row < 17; ++row) + { + values.push_back(csr_value(TypeParam(row + 0.125))); + } + const auto original = matrix.getElements(); + for (const int precision : {2, 8, 16}) + { + CountingBuffer buffer; + std::ostream output(&buffer); + matrix.printToCSR(output, precision); + + std::ostringstream expected; + expected << std::scientific << std::setprecision(precision) << " # CSR values"; + for (int row = 0; row < 17; ++row) + { + if (row == 0 || row == 6 || row == 12) expected << '\n'; + expected << ' ' << values[row]; + } + expected << "\n # CSR column indices\n"; + for (int col = 17; col > 0; --col) + { + if (col == 1) expected << '\n'; + expected << ' ' << col; + } + expected << "\n # CSR row pointers\n"; + for (int row = 0; row <= 18; ++row) + { + if (row == 16) expected << '\n'; + expected << ' ' << (row < 17 ? row : 17); + } + expected << "\n\n"; + EXPECT_EQ(buffer.str(), expected.str()); + EXPECT_EQ(buffer.sync_count, 1); + EXPECT_EQ(matrix.getElements(), original); + EXPECT_TRUE(output.good()); + + matrix.printToCSR(output, precision); + EXPECT_EQ(buffer.str(), expected.str() + expected.str()); + EXPECT_EQ(buffer.sync_count, 2); + } +} + +TYPED_TEST(SparseMatrixTest, EmptyCSRAndFlushFailure) +{ + CountingBuffer buffer; + std::ostream output(&buffer); + this->sm.printToCSR(output, 8); + EXPECT_EQ(buffer.str(), " # CSR values\n # CSR column indices\n # CSR row pointers\n 0 0 0 0 0\n\n"); + EXPECT_EQ(buffer.sync_count, 1); + + CountingBuffer failing_buffer; + failing_buffer.fail_sync = true; + std::ostream failing_output(&failing_buffer); + this->sm.printToCSR(failing_output, 8); + EXPECT_TRUE(failing_output.bad()); + EXPECT_EQ(failing_buffer.sync_count, 1); + + std::ostream throwing_output(&failing_buffer); + throwing_output.exceptions(std::ios::badbit); + EXPECT_THROW(this->sm.printToCSR(throwing_output, 8), std::ios_base::failure); +} + TYPED_TEST(SparseMatrixTest, Insert) { // Add a value to the matrix with row and column indices diff --git a/source/source_hamilt/module_hcontainer/output_hcontainer.cpp b/source/source_hamilt/module_hcontainer/output_hcontainer.cpp index 454b6985d27..474a0483758 100644 --- a/source/source_hamilt/module_hcontainer/output_hcontainer.cpp +++ b/source/source_hamilt/module_hcontainer/output_hcontainer.cpp @@ -123,7 +123,7 @@ void Output_HContainer::write_single_R(int rx, int ry, int rz) for (int iap = 0; iap < this->_hcontainer->size_atom_pairs(); ++iap) { - auto atom_pair = this->_hcontainer->get_atom_pair(iap); + const auto& atom_pair = this->_hcontainer->get_atom_pair(iap); const int r_index = atom_pair.find_R(rx, ry, rz); if (r_index < 0) continue; auto tmp_matrix_info = atom_pair.get_matrix_values(r_index); diff --git a/source/source_hamilt/module_hcontainer/test/test_hcontainer_output.cpp b/source/source_hamilt/module_hcontainer/test/test_hcontainer_output.cpp index 3e82bd571c3..443841ffc54 100644 --- a/source/source_hamilt/module_hcontainer/test/test_hcontainer_output.cpp +++ b/source/source_hamilt/module_hcontainer/test/test_hcontainer_output.cpp @@ -1,6 +1,9 @@ #include "source_hamilt/module_hcontainer/hcontainer.h" #include "source_hamilt/module_hcontainer/output_hcontainer.h" #include "source_cell/unitcell.h" +#include +#include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -159,3 +162,78 @@ TEST_F(OutputHContainerTest, Write) EXPECT_THAT(output, testing::HasSubstr(" 2 3 3")); EXPECT_THAT(output, testing::HasSubstr(" 0 0 0 2 3")); } + +template +class OutputHContainerPreservationTest : public testing::Test +{ +}; + +using OutputTypes = testing::Types>; +TYPED_TEST_SUITE(OutputHContainerPreservationTest, OutputTypes); + +TYPED_TEST(OutputHContainerPreservationTest, RepeatedWritesPreserveAllRBlocksAndBackingStorage) +{ + Parallel_Orbitals para; + para.set_serial(4, 4); + const int atom_begin[] = {0, 2}; + para.set_atomic_trace(atom_begin, 2, 4); + std::vector first = {TypeParam(1), TypeParam(2), TypeParam(3), TypeParam(4)}; + std::vector second = {TypeParam(5), TypeParam(6), TypeParam(7), TypeParam(8)}; + std::vector empty(4, TypeParam(1e-12)); + const auto first_before = first; + const auto second_before = second; + const auto empty_before = empty; + hamilt::HContainer matrix(¶); + matrix.insert_pair(hamilt::AtomPair(0, 0, 1, 0, 0, ¶, first.data())); + matrix.insert_pair(hamilt::AtomPair(0, 0, -1, 0, 0, ¶, second.data())); + matrix.insert_pair(hamilt::AtomPair(1, 1, 0, 0, 0, ¶, empty.data())); + auto* pair = matrix.find_pair(0, 0); + ASSERT_NE(pair, nullptr); + const auto first_r = pair->get_R_index(0); + const auto second_r = pair->get_R_index(1); + const auto* first_pointer = pair->get_pointer(0); + const auto* second_pointer = pair->get_pointer(1); + + std::ostringstream once; + hamilt::Output_HContainer writer(&matrix, once, 1e-10, 8); + writer.write(); + EXPECT_THAT(once.str(), testing::HasSubstr(" -1 0 0 4\n")); + EXPECT_THAT(once.str(), testing::HasSubstr(" 0 0 0 0\n # CSR values\n\n # CSR column indices\n\n")); + EXPECT_LT(once.str().find(" -1 0 0 4\n"), once.str().find(" 1 0 0 4\n")); + const std::string first_output = once.str(); + writer.write(); + EXPECT_EQ(once.str(), first_output + first_output); + + std::ostringstream block; + hamilt::Output_HContainer single(&matrix, block, 1e-10, 8); + single.write(1, 0, 0); + single.write(-1, 0, 0); + EXPECT_EQ(matrix.size_atom_pairs(), 2); // No R remains fixed after writing. + EXPECT_EQ(matrix.size_R_loop(), 3); + EXPECT_EQ(matrix.find_pair(0, 0), pair); + EXPECT_EQ(pair->get_R_index(0), first_r); + EXPECT_EQ(pair->get_R_index(1), second_r); + EXPECT_EQ(pair->get_pointer(0), first_pointer); + EXPECT_EQ(pair->get_pointer(1), second_pointer); + for (int element = 0; element < 4; ++element) + { + EXPECT_EQ(first_pointer[element], first_before[element]); + EXPECT_EQ(second_pointer[element], second_before[element]); + } + EXPECT_EQ(first, first_before); + EXPECT_EQ(second, second_before); + EXPECT_EQ(empty, empty_before); + + // The same container must still be usable by a subsequent gamma-only calculation. + matrix.fix_gamma(); + EXPECT_EQ(matrix.size_R_loop(), 1); + for (int element = 0; element < 4; ++element) + { + EXPECT_EQ(matrix.find_pair(0, 0)->get_pointer(0)[element], + first_before[element] + second_before[element]); + } + std::ostringstream gamma; + hamilt::Output_HContainer gamma_writer(&matrix, gamma, 1e-10, 8); + gamma_writer.write(); + EXPECT_THAT(gamma.str(), testing::HasSubstr(" 0 0 0 4\n")); +}