Mercurial > hg > svcore
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 } |