Skip to content

Commit

Permalink
optimise reading/writing for contiguous arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
KrisThielemans committed May 17, 2024
1 parent 715ede8 commit 10f61f0
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/include/stir/IO/read_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define __stir_IO_read_data_H__
/*
Copyright (C) 2004- 2007, Hammersmith Imanet Ltd
Copyright (C) 2024, University College London
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0
Expand Down
5 changes: 5 additions & 0 deletions src/include/stir/IO/read_data.inl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright (C) 2004- 2007, Hammersmith Imanet Ltd
Copyright (C) 2024, University College London
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -34,6 +35,10 @@ template <int num_dimensions, class IStreamT, class elemT>
inline Succeeded
read_data_help(is_not_1d, IStreamT& s, Array<num_dimensions, elemT>& data, const ByteOrder byte_order)
{
if (data.is_contiguous())
return read_data_1d(s, data, byte_order);

// otherwise, recurse
for (typename Array<num_dimensions, elemT>::iterator iter = data.begin(); iter != data.end(); ++iter)
{
if (read_data(s, *iter, byte_order) == Succeeded::no)
Expand Down
8 changes: 4 additions & 4 deletions src/include/stir/IO/read_data_1d.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ namespace detail
This function might propagate any exceptions by std::istream::read.
*/
template <class elemT>
inline Succeeded read_data_1d(std::istream& s, Array<1, elemT>& data, const ByteOrder byte_order);
template <int num_dimensions, class elemT>
inline Succeeded read_data_1d(std::istream& s, Array<num_dimensions, elemT>& data, const ByteOrder byte_order);

/* \ingroup Array_IO_detail
\brief This is the (internal) function that does the actual reading from a FILE*.
\internal
*/
template <class elemT>
inline Succeeded read_data_1d(FILE*&, Array<1, elemT>& data, const ByteOrder byte_order);
template <int num_dimensions, class elemT>
inline Succeeded read_data_1d(FILE*&, Array<num_dimensions, elemT>& data, const ByteOrder byte_order);

} // end namespace detail
END_NAMESPACE_STIR
Expand Down
28 changes: 14 additions & 14 deletions src/include/stir/IO/read_data_1d.inl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ namespace detail

/***************** version for istream *******************************/

template <class elemT>
template <int num_dimensions, class elemT>
Succeeded
read_data_1d(std::istream& s, Array<1, elemT>& data, const ByteOrder byte_order)
read_data_1d(std::istream& s, Array<num_dimensions, elemT>& data, const ByteOrder byte_order)
{
if (!s || (dynamic_cast<std::ifstream*>(&s) != 0 && !dynamic_cast<std::ifstream*>(&s)->is_open())
|| (dynamic_cast<std::fstream*>(&s) != 0 && !dynamic_cast<std::fstream*>(&s)->is_open()))
Expand All @@ -41,9 +41,9 @@ read_data_1d(std::istream& s, Array<1, elemT>& data, const ByteOrder byte_order)
// note: find num_to_read (using size()) outside of s.read() function call
// otherwise Array::check_state() in size() might abort if
// get_data_ptr() is called before size() (which is compiler dependent)
const std::streamsize num_to_read = static_cast<std::streamsize>(data.size()) * sizeof(elemT);
s.read(reinterpret_cast<char*>(data.get_data_ptr()), num_to_read);
data.release_data_ptr();
const std::streamsize num_to_read = static_cast<std::streamsize>(data.size_all()) * sizeof(elemT);
s.read(reinterpret_cast<char*>(data.get_full_data_ptr()), num_to_read);
data.release_full_data_ptr();

if (!s)
{
Expand All @@ -53,8 +53,8 @@ read_data_1d(std::istream& s, Array<1, elemT>& data, const ByteOrder byte_order)

if (!byte_order.is_native_order())
{
for (int i = data.get_min_index(); i <= data.get_max_index(); ++i)
ByteOrder::swap_order(data[i]);
for (auto iter = data.begin_all(); iter != data.end_all(); ++iter)
ByteOrder::swap_order(*iter);
}

return Succeeded::yes;
Expand All @@ -63,9 +63,9 @@ read_data_1d(std::istream& s, Array<1, elemT>& data, const ByteOrder byte_order)
/***************** version for FILE *******************************/
// largely a copy of above, but with calls to stdio function

template <class elemT>
template <int num_dimensions, class elemT>
Succeeded
read_data_1d(FILE*& fptr_ref, Array<1, elemT>& data, const ByteOrder byte_order)
read_data_1d(FILE*& fptr_ref, Array<num_dimensions, elemT>& data, const ByteOrder byte_order)
{
FILE* fptr = fptr_ref;
if (fptr == NULL || ferror(fptr))
Expand All @@ -77,9 +77,9 @@ read_data_1d(FILE*& fptr_ref, Array<1, elemT>& data, const ByteOrder byte_order)
// note: find num_to_read (using size()) outside of s.read() function call
// otherwise Array::check_state() in size() might abort if
// get_data_ptr() is called before size() (which is compiler dependent)
const std::size_t num_to_read = static_cast<std::size_t>(data.size());
const std::size_t num_read = fread(reinterpret_cast<char*>(data.get_data_ptr()), sizeof(elemT), num_to_read, fptr);
data.release_data_ptr();
const std::size_t num_to_read = static_cast<std::size_t>(data.size_all());
const std::size_t num_read = fread(reinterpret_cast<char*>(data.get_full_data_ptr()), sizeof(elemT), num_to_read, fptr);
data.release_full_data_ptr();

if (ferror(fptr) || num_to_read != num_read)
{
Expand All @@ -89,8 +89,8 @@ read_data_1d(FILE*& fptr_ref, Array<1, elemT>& data, const ByteOrder byte_order)

if (!byte_order.is_native_order())
{
for (int i = data.get_min_index(); i <= data.get_max_index(); ++i)
ByteOrder::swap_order(data[i]);
for (auto iter = data.begin_all(); iter != data.end_all(); ++iter)
ByteOrder::swap_order(*iter);
}

return Succeeded::yes;
Expand Down
8 changes: 4 additions & 4 deletions src/include/stir/IO/write_data_1d.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ namespace detail
This function does not throw any exceptions. Exceptions thrown by std::ostream::write
are caught.
*/
template <class elemT>
template <int num_dimensions, class elemT>
inline Succeeded
write_data_1d(std::ostream& s, const Array<1, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data);
write_data_1d(std::ostream& s, const Array<num_dimensions, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data);
/*! \ingroup Array_IO_detail
\brief This is an internal function called by \c write_data(). It does the actual writing
to \c FILE* using stdio functions.
This function does not throw any exceptions.
*/
template <class elemT>
template <int num_dimensions, class elemT>
inline Succeeded
write_data_1d(FILE*& fptr_ref, const Array<1, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data);
write_data_1d(FILE*& fptr_ref, const Array<num_dimensions, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data);
} // namespace detail

END_NAMESPACE_STIR
Expand Down
48 changes: 24 additions & 24 deletions src/include/stir/IO/write_data_1d.inl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ namespace detail

/***************** version for ostream *******************************/

template <class elemT>
template <int num_dimensions, class elemT>
inline Succeeded
write_data_1d(std::ostream& s, const Array<1, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data)
write_data_1d(std::ostream& s, const Array<num_dimensions, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data)
{
if (!s || (dynamic_cast<std::ofstream*>(&s) != 0 && !dynamic_cast<std::ofstream*>(&s)->is_open())
|| (dynamic_cast<std::fstream*>(&s) != 0 && !dynamic_cast<std::fstream*>(&s)->is_open()))
Expand All @@ -44,40 +44,40 @@ write_data_1d(std::ostream& s, const Array<1, elemT>& data, const ByteOrder byte
/*
if (!byte_order.is_native_order())
{
Array<1, elemT> a_copy(data);
Array<num_dimensions, elemT> a_copy(data);
for(int i=data.get_min_index(); i<=data.get_max_index(); i++)
ByteOrder::swap_order(a_copy[i]);
return write_data(s, a_copy, ByteOrder::native, true);
}
*/
if (!byte_order.is_native_order())
{
Array<1, elemT>& data_ref = const_cast<Array<1, elemT>&>(data);
for (int i = data.get_min_index(); i <= data.get_max_index(); ++i)
ByteOrder::swap_order(data_ref[i]);
Array<num_dimensions, elemT>& data_ref = const_cast<Array<num_dimensions, elemT>&>(data);
for (auto iter = data_ref.begin_all(); iter != data_ref.end_all(); ++iter)
ByteOrder::swap_order(*iter);
}

// note: find num_to_write (using size()) outside of s.write() function call
// otherwise Array::check_state() in size() might abort if
// get_const_data_ptr() is called before size() (which is compiler dependent)
const std::streamsize num_to_write = static_cast<std::streamsize>(data.size()) * sizeof(elemT);
const std::streamsize num_to_write = static_cast<std::streamsize>(data.size_all()) * sizeof(elemT);
bool writing_ok = true;
try
{
s.write(reinterpret_cast<const char*>(data.get_const_data_ptr()), num_to_write);
s.write(reinterpret_cast<const char*>(data.get_const_full_data_ptr()), num_to_write);
}
catch (...)
{
writing_ok = false;
}

data.release_const_data_ptr();
data.release_const_full_data_ptr();

if (!can_corrupt_data && !byte_order.is_native_order())
{
Array<1, elemT>& data_ref = const_cast<Array<1, elemT>&>(data);
for (int i = data.get_min_index(); i <= data.get_max_index(); ++i)
ByteOrder::swap_order(data_ref[i]);
Array<num_dimensions, elemT>& data_ref = const_cast<Array<num_dimensions, elemT>&>(data);
for (auto iter = data_ref.begin_all(); iter != data_ref.end_all(); ++iter)
ByteOrder::swap_order(*iter);
}

if (!writing_ok || !s)
Expand All @@ -92,9 +92,9 @@ write_data_1d(std::ostream& s, const Array<1, elemT>& data, const ByteOrder byte
/***************** version for FILE *******************************/
// largely a copy of above, but with calls to stdio function

template <class elemT>
template <int num_dimensions, class elemT>
inline Succeeded
write_data_1d(FILE*& fptr_ref, const Array<1, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data)
write_data_1d(FILE*& fptr_ref, const Array<num_dimensions, elemT>& data, const ByteOrder byte_order, const bool can_corrupt_data)
{
FILE* fptr = fptr_ref;
if (fptr == 0 || ferror(fptr))
Expand All @@ -109,33 +109,33 @@ write_data_1d(FILE*& fptr_ref, const Array<1, elemT>& data, const ByteOrder byte
/*
if (!byte_order.is_native_order())
{
Array<1, elemT> a_copy(data);
Array<num_dimensions, elemT> a_copy(data);
for(int i=data.get_min_index(); i<=data.get_max_index(); i++)
ByteOrder::swap_order(a_copy[i]);
return write_data(s, a_copy, ByteOrder::native, true);
}
*/
if (!byte_order.is_native_order())
{
Array<1, elemT>& data_ref = const_cast<Array<1, elemT>&>(data);
for (int i = data.get_min_index(); i <= data.get_max_index(); ++i)
ByteOrder::swap_order(data_ref[i]);
Array<num_dimensions, elemT>& data_ref = const_cast<Array<num_dimensions, elemT>&>(data);
for (auto iter = data_ref.begin_all(); iter != data_ref.end_all(); ++iter)
ByteOrder::swap_order(*iter);
}

// note: find num_to_write (using size()) outside of s.write() function call
// otherwise Array::check_state() in size() might abort if
// get_const_data_ptr() is called before size() (which is compiler dependent)
const std::size_t num_to_write = static_cast<std::size_t>(data.size());
const std::size_t num_to_write = static_cast<std::size_t>(data.size_all());
const std::size_t num_written
= fwrite(reinterpret_cast<const char*>(data.get_const_data_ptr()), sizeof(elemT), num_to_write, fptr);
= fwrite(reinterpret_cast<const char*>(data.get_const_full_data_ptr()), sizeof(elemT), num_to_write, fptr);

data.release_const_data_ptr();
data.release_const_full_data_ptr();

if (!can_corrupt_data && !byte_order.is_native_order())
{
Array<1, elemT>& data_ref = const_cast<Array<1, elemT>&>(data);
for (int i = data.get_min_index(); i <= data.get_max_index(); ++i)
ByteOrder::swap_order(data_ref[i]);
Array<num_dimensions, elemT>& data_ref = const_cast<Array<num_dimensions, elemT>&>(data);
for (auto iter = data_ref.begin_all(); iter != data_ref.end_all(); ++iter)
ByteOrder::swap_order(*iter);
}

if (num_written != num_to_write || ferror(fptr))
Expand Down

0 comments on commit 10f61f0

Please sign in to comment.