Manual merge of the backend (peakfile reading) part of waveview branch.

Summary:
    * use mmap() for the whole peakfile instead of lots of small seek/reads
    * cache the computed peaks
    * where possible, open files with O_NOATIME.
This commit is contained in:
nick_m 2015-03-11 00:40:02 +11:00
parent 1199fe2f90
commit fb8bc4e8c9
2 changed files with 95 additions and 80 deletions

View file

@ -28,6 +28,7 @@
#include <glibmm/threads.h> #include <glibmm/threads.h>
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/scoped_array.hpp>
#include "ardour/source.h" #include "ardour/source.h"
#include "ardour/ardour.h" #include "ardour/ardour.h"
@ -164,6 +165,12 @@ class LIBARDOUR_API AudioSource : virtual public Source,
framecnt_t peak_leftover_size; framecnt_t peak_leftover_size;
Sample* peak_leftovers; Sample* peak_leftovers;
framepos_t peak_leftover_frame; framepos_t peak_leftover_frame;
mutable bool _first_run;
mutable double _last_scale;
mutable off_t _last_map_off;
mutable size_t _last_raw_map_length;
mutable boost::scoped_array<PeakData> peak_cache;
}; };
} }

View file

@ -34,10 +34,11 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <sys/mman.h>
#include <glib.h> #include <glib.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#include <glibmm/fileutils.h> #include <glibmm/fileutils.h>
@ -78,6 +79,10 @@ AudioSource::AudioSource (Session& s, string name)
, peak_leftover_cnt (0) , peak_leftover_cnt (0)
, peak_leftover_size (0) , peak_leftover_size (0)
, peak_leftovers (0) , peak_leftovers (0)
, _first_run (true)
, _last_scale (0.0)
, _last_map_off (0)
, _last_raw_map_length (0)
{ {
} }
@ -90,6 +95,10 @@ AudioSource::AudioSource (Session& s, const XMLNode& node)
, peak_leftover_cnt (0) , peak_leftover_cnt (0)
, peak_leftover_size (0) , peak_leftover_size (0)
, peak_leftovers (0) , peak_leftovers (0)
, _first_run (true)
, _last_scale (0.0)
, _last_map_off (0)
, _last_raw_map_length (0)
{ {
if (set_state (node, Stateful::loading_state_version)) { if (set_state (node, Stateful::loading_state_version)) {
throw failed_constructor(); throw failed_constructor();
@ -331,10 +340,14 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
PeakData::PeakDatum xmax; PeakData::PeakDatum xmax;
PeakData::PeakDatum xmin; PeakData::PeakDatum xmin;
int32_t to_read; int32_t to_read;
ssize_t nread; const int bufsize = sysconf(_SC_PAGESIZE);
framecnt_t zero_fill = 0; framecnt_t zero_fill = 0;
ScopedFileDescriptor sfd (::open (peakpath.c_str(), O_RDONLY)); #ifdef PLATFORM_WINDOWS
ScopedFileDescriptor sfd (::open (peakpath.c_str(), O_RDONLY | O_NONBLOCK));
#else
ScopedFileDescriptor sfd (::open (peakpath.c_str(), O_RDONLY | O_NOATIME | O_NONBLOCK));
#endif
if (sfd < 0) { if (sfd < 0) {
error << string_compose (_("Cannot open peakfile @ %1 for reading (%2)"), peakpath, strerror (errno)) << endmsg; error << string_compose (_("Cannot open peakfile @ %1 for reading (%2)"), peakpath, strerror (errno)) << endmsg;
@ -383,39 +396,45 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
} }
if (scale == 1.0) { if (scale == 1.0) {
off_t offset = 0;
off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData); off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData);
ssize_t bytes_to_read = sizeof (PeakData)* npeaks; size_t bytes_to_read = sizeof (PeakData)* npeaks;
/* open, read, close */ /* open, read, close */
DEBUG_TRACE (DEBUG::Peaks, "DIRECT PEAKS\n"); DEBUG_TRACE (DEBUG::Peaks, "DIRECT PEAKS\n");
offset = lseek (sfd, first_peak_byte, SEEK_SET); off_t map_off = first_peak_byte;
off_t read_map_off = map_off & ~(bufsize - 1);
off_t map_delta = map_off - read_map_off;
size_t map_length = bytes_to_read + map_delta;
if (offset != first_peak_byte) { if (_first_run || (_last_scale != samples_per_visual_peak) || (_last_map_off != map_off) || (_last_raw_map_length < bytes_to_read)) {
error << string_compose(_("AudioSource: could not seek to correct location in peak file \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; peak_cache.reset (new PeakData[npeaks]);
return -1; boost::scoped_array<PeakData> staging (new PeakData[npeaks]);
char* addr = (char*) mmap (0, map_length, PROT_READ, MAP_PRIVATE, sfd, read_map_off);
if (addr == MAP_FAILED) {
error << string_compose (_("map failed - could not mmap peakfile %1."), peakpath) << endmsg;
return -1;
}
memcpy ((void*)peak_cache.get(), (void*)(addr + map_delta), bytes_to_read);
munmap (addr, map_length);
if (zero_fill) {
memset (&peak_cache[npeaks], 0, sizeof (PeakData) * zero_fill);
}
_first_run = false;
_last_scale = samples_per_visual_peak;
_last_map_off = map_off;
_last_raw_map_length = bytes_to_read;
} }
nread = ::read (sfd, peaks, bytes_to_read); memcpy ((void*)peaks, (void*)peak_cache.get(), npeaks * sizeof(PeakData));
if (nread != bytes_to_read) {
DEBUG_TRACE (DEBUG::Peaks, string_compose ("[%1]: Cannot read peaks from peakfile! (read only %2 not %3 at sample %4 = byte %5 )\n"
, _name, nread, npeaks, start, first_peak_byte));
return -1;
}
if (zero_fill) {
memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
}
return 0; return 0;
} }
framecnt_t tnp;
if (scale < 1.0) { if (scale < 1.0) {
DEBUG_TRACE (DEBUG::Peaks, "DOWNSAMPLE\n"); DEBUG_TRACE (DEBUG::Peaks, "DOWNSAMPLE\n");
@ -430,20 +449,16 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks
*/ */
const framecnt_t chunksize = (framecnt_t) min (expected_peaks, 65536.0); const framecnt_t chunksize = (framecnt_t) expected_peaks; // we read all the peaks we need in one hit.
boost::scoped_array<PeakData> staging(new PeakData[chunksize]);
/* compute the rounded up frame position */ /* compute the rounded up frame position */
framepos_t current_frame = start; framepos_t current_stored_peak = (framepos_t) ceil (start / (double) samples_per_file_peak);
framepos_t current_stored_peak = (framepos_t) ceil (current_frame / (double) samples_per_file_peak); framepos_t next_visual_peak = (framepos_t) ceil (start / samples_per_visual_peak);
framepos_t next_visual_peak = (framepos_t) ceil (current_frame / samples_per_visual_peak);
double next_visual_peak_frame = next_visual_peak * samples_per_visual_peak; double next_visual_peak_frame = next_visual_peak * samples_per_visual_peak;
framepos_t stored_peak_before_next_visual_peak = (framepos_t) next_visual_peak_frame / samples_per_file_peak; framepos_t stored_peak_before_next_visual_peak = (framepos_t) next_visual_peak_frame / samples_per_file_peak;
framecnt_t nvisual_peaks = 0; framecnt_t nvisual_peaks = 0;
framecnt_t stored_peaks_read = 0; uint32_t i = 0;
framecnt_t i = 0;
/* handle the case where the initial visual peak is on a pixel boundary */ /* handle the case where the initial visual peak is on a pixel boundary */
@ -451,67 +466,60 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
/* open ... close during out: handling */ /* open ... close during out: handling */
while (nvisual_peaks < npeaks) { off_t map_off = (uint32_t) (ceil (start / (double) samples_per_file_peak)) * sizeof(PeakData);
off_t read_map_off = map_off & ~(bufsize - 1);
off_t map_delta = map_off - read_map_off;
size_t raw_map_length = chunksize * sizeof(PeakData);
size_t map_length = (chunksize * sizeof(PeakData)) + map_delta;
if (i == stored_peaks_read) { if (_first_run || (_last_scale != samples_per_visual_peak) || (_last_map_off != map_off) || (_last_raw_map_length < raw_map_length)) {
peak_cache.reset (new PeakData[npeaks]);
boost::scoped_array<PeakData> staging (new PeakData[chunksize]);
uint32_t start_byte = current_stored_peak * sizeof(PeakData); char* addr;
tnp = min ((framecnt_t)(_length/samples_per_file_peak - current_stored_peak), (framecnt_t) expected_peaks); addr = (char*) mmap (0, map_length, PROT_READ, MAP_PRIVATE, sfd, read_map_off);
to_read = min (chunksize, tnp); if (addr == MAP_FAILED) {
ssize_t bytes_to_read = sizeof (PeakData) * to_read; error << string_compose (_("map failed - could not mmap peakfile %1."), peakpath) << endmsg;
return -1;
DEBUG_TRACE (DEBUG::Peaks, string_compose ("reading %1 bytes from peakfile @ %2\n"
, bytes_to_read, start_byte));
off_t offset = lseek (sfd, start_byte, SEEK_SET);
if (offset != start_byte) {
error << string_compose(_("AudioSource: could not seek to correct location in peak file \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
return -1;
}
if ((nread = ::read (sfd, staging.get(), bytes_to_read)) != bytes_to_read) {
off_t fend = lseek (sfd, 0, SEEK_END);
DEBUG_TRACE (DEBUG::Peaks, string_compose ("[%1]: cannot read peak data from peakfile (%2 peaks instead of %3) (%4) at start_byte = %5 _length = %6 versus len = %7 expected maxpeaks = %8 npeaks was %9"
, _name, (nread / sizeof(PeakData)), to_read, g_strerror (errno), start_byte, _length, fend, ((_length - current_frame)/samples_per_file_peak), npeaks));
return -1;
}
i = 0;
stored_peaks_read = nread / sizeof(PeakData);
} }
xmax = -1.0; memcpy ((void*)staging.get(), (void*)(addr + map_delta), raw_map_length);
xmin = 1.0; munmap (addr, map_length);
while ((i < stored_peaks_read) && (current_stored_peak <= stored_peak_before_next_visual_peak)) { while (nvisual_peaks < npeaks) {
xmax = max (xmax, staging[i].max); xmax = -1.0;
xmin = min (xmin, staging[i].min); xmin = 1.0;
++i;
++current_stored_peak; while ((current_stored_peak <= stored_peak_before_next_visual_peak) && (i < raw_map_length)) {
--expected_peaks;
xmax = max (xmax, staging[i].max);
xmin = min (xmin, staging[i].min);
++i;
++current_stored_peak;
}
peak_cache[nvisual_peaks].max = xmax;
peak_cache[nvisual_peaks].min = xmin;
++nvisual_peaks;
next_visual_peak_frame = min ((double) start + cnt, (next_visual_peak_frame + samples_per_visual_peak));
stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
} }
peaks[nvisual_peaks].max = xmax; if (zero_fill) {
peaks[nvisual_peaks].min = xmin; cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
++nvisual_peaks; memset (&peak_cache[npeaks], 0, sizeof (PeakData) * zero_fill);
++next_visual_peak; }
//next_visual_peak_frame = min ((next_visual_peak * samples_per_visual_peak), (next_visual_peak_frame+samples_per_visual_peak) ); _first_run = false;
next_visual_peak_frame = min ((double) start+cnt, (next_visual_peak_frame+samples_per_visual_peak) ); _last_scale = samples_per_visual_peak;
stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak; _last_map_off = map_off;
_last_raw_map_length = raw_map_length;
} }
if (zero_fill) { memcpy ((void*)peaks, (void*)peak_cache.get(), npeaks * sizeof(PeakData));
cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
}
} else { } else {
DEBUG_TRACE (DEBUG::Peaks, "UPSAMPLE\n"); DEBUG_TRACE (DEBUG::Peaks, "UPSAMPLE\n");
/* the caller wants /* the caller wants