comparison data/fft/FFTDataServer.cpp @ 335:02d2ad95ea52 spectrogram-cache-rejig

* Get storage advice for each cache in an FFT data server. Allows us to be more confident about the actual memory situation and cut over from memory to disc part way through an FFT calculation if necessary. StorageAdviser is now a bit too optimistic though (it's too keen to allocate large numbers of small blocks in memory).
author Chris Cannam
date Tue, 13 Nov 2007 13:54:10 +0000
parents aa8dbac62024
children cc4eb32efc6c 6f6ab834449d
comparison
equal deleted inserted replaced
334:aa8dbac62024 335:02d2ad95ea52
28 #include "base/Thread.h" // for debug mutex locker 28 #include "base/Thread.h" // for debug mutex locker
29 29
30 #include <QMessageBox> 30 #include <QMessageBox>
31 #include <QApplication> 31 #include <QApplication>
32 32
33 //#define DEBUG_FFT_SERVER 1 33 #define DEBUG_FFT_SERVER 1
34 //#define DEBUG_FFT_SERVER_FILL 1 34 //#define DEBUG_FFT_SERVER_FILL 1
35 35
36 #ifdef DEBUG_FFT_SERVER_FILL 36 #ifdef DEBUG_FFT_SERVER_FILL
37 #ifndef DEBUG_FFT_SERVER 37 #ifndef DEBUG_FFT_SERVER
38 #define DEBUG_FFT_SERVER 1 38 #define DEBUG_FFT_SERVER 1
494 m_fftSize(fftSize), 494 m_fftSize(fftSize),
495 m_polar(polar), 495 m_polar(polar),
496 m_width(0), 496 m_width(0),
497 m_height(0), 497 m_height(0),
498 m_cacheWidth(0), 498 m_cacheWidth(0),
499 m_memoryCache(false), 499 m_cacheWidthPower(0),
500 m_compactCache(false), 500 m_cacheWidthMask(0),
501 m_lastUsedCache(-1), 501 m_lastUsedCache(-1),
502 m_criteria(criteria),
502 m_fftInput(0), 503 m_fftInput(0),
503 m_exiting(false), 504 m_exiting(false),
504 m_suspended(true), //!!! or false? 505 m_suspended(true), //!!! or false?
505 m_fillThread(0) 506 m_fillThread(0)
506 { 507 {
524 size_t maxCacheSize = 20 * 1024 * 1024; 525 size_t maxCacheSize = 20 * 1024 * 1024;
525 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample); 526 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
526 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width; 527 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
527 else m_cacheWidth = maxCacheSize / columnSize; 528 else m_cacheWidth = maxCacheSize / columnSize;
528 529
530 #ifdef DEBUG_FFT_SERVER
531 std::cerr << "FFTDataServer(" << this << "): cache width nominal "
532 << m_cacheWidth << ", actual ";
533 #endif
534
529 int bits = 0; 535 int bits = 0;
530 while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; } 536 while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; }
537 m_cacheWidthPower = bits + 1;
531 m_cacheWidth = 2; 538 m_cacheWidth = 2;
532 while (bits) { m_cacheWidth <<= 1; --bits; } 539 while (bits) { m_cacheWidth <<= 1; --bits; }
533 540 m_cacheWidthMask = m_cacheWidth - 1;
534 if (criteria == StorageAdviser::NoCriteria) { 541
542 #ifdef DEBUG_FFT_SERVER
543 std::cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
544 << m_cacheWidthMask << ")" << std::endl;
545 #endif
546
547 if (m_criteria == StorageAdviser::NoCriteria) {
535 548
536 // assume "spectrogram" criteria for polar ffts, and "feature 549 // assume "spectrogram" criteria for polar ffts, and "feature
537 // extraction" criteria for rectangular ones. 550 // extraction" criteria for rectangular ones.
538 551
539 if (m_polar) { 552 if (m_polar) {
540 criteria = StorageAdviser::Criteria 553 m_criteria = StorageAdviser::Criteria
541 (StorageAdviser::SpeedCritical | 554 (StorageAdviser::SpeedCritical |
542 StorageAdviser::LongRetentionLikely); 555 StorageAdviser::LongRetentionLikely);
543 } else { 556 } else {
544 criteria = StorageAdviser::Criteria 557 m_criteria = StorageAdviser::Criteria
545 (StorageAdviser::PrecisionCritical); 558 (StorageAdviser::PrecisionCritical);
546 } 559 }
547 } 560 }
548
549 int cells = m_width * m_height;
550 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
551 int maximumSize = (cells / 1024) * sizeof(float); // kb
552
553 // We don't have a compact rectangular representation, and compact
554 // of course is never precision-critical
555 bool canCompact = true;
556 if ((criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
557 canCompact = false;
558 minimumSize = maximumSize; // don't use compact
559 }
560
561 StorageAdviser::Recommendation recommendation;
562
563 try {
564
565 recommendation =
566 StorageAdviser::recommend(criteria, minimumSize, maximumSize);
567
568 } catch (InsufficientDiscSpace s) {
569
570 // Delete any unused servers we may have been leaving around
571 // in case we wanted them again
572
573 purgeLimbo(0);
574
575 // This time we don't catch InsufficientDiscSpace -- we
576 // haven't allocated anything yet and can safely let the
577 // exception out to indicate to the caller that we can't
578 // handle it.
579
580 recommendation =
581 StorageAdviser::recommend(criteria, minimumSize, maximumSize);
582 }
583
584 std::cerr << "Recommendation was: " << recommendation << std::endl;
585
586 m_memoryCache = false;
587
588 if ((recommendation & StorageAdviser::UseMemory) ||
589 (recommendation & StorageAdviser::PreferMemory)) {
590 m_memoryCache = true;
591 }
592
593 m_compactCache = canCompact &&
594 (recommendation & StorageAdviser::ConserveSpace);
595
596 std::cerr << "FFTDataServer: memory cache = " << m_memoryCache << ", compact cache = " << m_compactCache << std::endl;
597
598 #ifdef DEBUG_FFT_SERVER
599 std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl;
600 #endif
601
602 StorageAdviser::notifyPlannedAllocation
603 (m_memoryCache ? StorageAdviser::MemoryAllocation :
604 StorageAdviser::DiscAllocation,
605 m_compactCache ? minimumSize : maximumSize);
606 561
607 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) { 562 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
608 m_caches.push_back(0); 563 m_caches.push_back(0);
609 } 564 }
610 565
646 601
647 MutexLocker locker(&m_writeMutex, 602 MutexLocker locker(&m_writeMutex,
648 "FFTDataServer::m_writeMutex[~FFTDataServer]"); 603 "FFTDataServer::m_writeMutex[~FFTDataServer]");
649 604
650 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { 605 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
606
651 if (*i) { 607 if (*i) {
652 delete *i; 608 delete *i;
653 } else {
654 StorageAdviser::notifyDoneAllocation
655 (m_memoryCache ? StorageAdviser::MemoryAllocation :
656 StorageAdviser::DiscAllocation,
657 m_cacheWidth * m_height *
658 (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1);
659 } 609 }
660 } 610 }
661 611
662 deleteProcessingData(); 612 deleteProcessingData();
663 } 613 }
722 m_condition.wakeAll(); 672 m_condition.wakeAll();
723 } 673 }
724 } 674 }
725 } 675 }
726 676
677 void
678 FFTDataServer::getStorageAdvice(size_t w, size_t h,
679 bool &memoryCache, bool &compactCache)
680 {
681 int cells = w * h;
682 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
683 int maximumSize = (cells / 1024) * sizeof(float); // kb
684
685 // We don't have a compact rectangular representation, and compact
686 // of course is never precision-critical
687
688 bool canCompact = true;
689 if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
690 canCompact = false;
691 minimumSize = maximumSize; // don't use compact
692 }
693
694 StorageAdviser::Recommendation recommendation;
695
696 try {
697
698 recommendation =
699 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
700
701 } catch (InsufficientDiscSpace s) {
702
703 // Delete any unused servers we may have been leaving around
704 // in case we wanted them again
705
706 purgeLimbo(0);
707
708 // This time we don't catch InsufficientDiscSpace -- we
709 // haven't allocated anything yet and can safely let the
710 // exception out to indicate to the caller that we can't
711 // handle it.
712
713 recommendation =
714 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
715 }
716
717 std::cerr << "Recommendation was: " << recommendation << std::endl;
718
719 memoryCache = false;
720
721 if ((recommendation & StorageAdviser::UseMemory) ||
722 (recommendation & StorageAdviser::PreferMemory)) {
723 memoryCache = true;
724 }
725
726 compactCache = canCompact &&
727 (recommendation & StorageAdviser::ConserveSpace);
728
729 std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl;
730
731 #ifdef DEBUG_FFT_SERVER
732 std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::endl;
733 #endif
734 }
735
727 FFTCache * 736 FFTCache *
728 FFTDataServer::getCacheAux(size_t c) 737 FFTDataServer::getCacheAux(size_t c)
729 { 738 {
730 Profiler profiler("FFTDataServer::getCacheAux", false); 739 Profiler profiler("FFTDataServer::getCacheAux", false);
731 #ifdef DEBUG_FFT_SERVER 740 #ifdef DEBUG_FFT_SERVER
782 size_t width = m_cacheWidth; 791 size_t width = m_cacheWidth;
783 if (c * m_cacheWidth + width > m_width) { 792 if (c * m_cacheWidth + width > m_width) {
784 width = m_width - c * m_cacheWidth; 793 width = m_width - c * m_cacheWidth;
785 } 794 }
786 795
796 bool memoryCache = false;
797 bool compactCache = false;
798
799 getStorageAdvice(width, m_height, memoryCache, compactCache);
800
787 try { 801 try {
788 802
789 if (m_memoryCache) { 803 if (memoryCache) {
790 804
791 cache = new FFTMemoryCache 805 cache = new FFTMemoryCache
792 (m_compactCache ? FFTMemoryCache::Compact : 806 (compactCache ? FFTMemoryCache::Compact :
793 // FFTMemoryCache::Polar); 807 m_polar ? FFTMemoryCache::Polar :
794 m_polar ? FFTMemoryCache::Polar : 808 FFTMemoryCache::Rectangular);
795 FFTMemoryCache::Rectangular); 809
796 810 } else {
797 } else if (m_compactCache) {
798 811
799 cache = new FFTFileCache 812 cache = new FFTFileCache
800 (name, 813 (name,
801 MatrixFile::ReadWrite, 814 MatrixFile::ReadWrite,
802 FFTFileCache::Compact); 815 compactCache ? FFTFileCache::Compact :
803 816 m_polar ? FFTFileCache::Polar :
804 } else { 817 FFTFileCache::Rectangular);
805
806 cache = new FFTFileCache
807 (name,
808 MatrixFile::ReadWrite,
809 m_polar ? FFTFileCache::Polar :
810 FFTFileCache::Rectangular);
811 } 818 }
812 819
813 cache->resize(width, m_height); 820 cache->resize(width, m_height);
814 cache->reset(); 821 cache->reset();
815 822
816 } catch (std::bad_alloc) { 823 } catch (std::bad_alloc) {
817 824
818 delete cache; 825 delete cache;
819 cache = 0; 826 cache = 0;
820 827
821 if (m_memoryCache) { 828 if (memoryCache) {
822 829
823 std::cerr << "WARNING: Memory allocation failed when resizing" 830 std::cerr << "WARNING: Memory allocation failed when resizing"
824 << " FFT memory cache no. " << c << " to " << width 831 << " FFT memory cache no. " << c << " to " << width
825 << "x" << m_height << " (of total width " << m_width 832 << "x" << m_height << " (of total width " << m_width
826 << "): falling back to disc cache" << std::endl; 833 << "): falling back to disc cache" << std::endl;
827 834
828 try { 835 try {
829 836
830 cache = new FFTFileCache(name, MatrixFile::ReadWrite, 837 purgeLimbo(0);
838
839 cache = new FFTFileCache(name,
840 MatrixFile::ReadWrite,
831 FFTFileCache::Compact); 841 FFTFileCache::Compact);
832 842
833 cache->resize(width, m_height); 843 cache->resize(width, m_height);
834 cache->reset(); 844 cache->reset();
835 845
852 (0, QApplication::tr("FFT cache resize failed"), 862 (0, QApplication::tr("FFT cache resize failed"),
853 QApplication::tr 863 QApplication::tr
854 ("Failed to create or resize an FFT model slice.\n" 864 ("Failed to create or resize an FFT model slice.\n"
855 "There may be insufficient memory or disc space to continue.")); 865 "There may be insufficient memory or disc space to continue."));
856 } 866 }
857
858 StorageAdviser::notifyDoneAllocation
859 (m_memoryCache ? StorageAdviser::MemoryAllocation :
860 StorageAdviser::DiscAllocation,
861 width * m_height *
862 (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1);
863 867
864 m_caches[c] = cache; 868 m_caches[c] = cache;
865 m_lastUsedCache = c; 869 m_lastUsedCache = c;
866 return cache; 870 return cache;
867 } 871 }