[펌] Qt QML Performance Considerations And Suggestions
원문] https://doc.qt.io/qt-5/qtquick-performance.html
Performance Considerations And Suggestions
(성능 고려 사항 및 제안 사항)
Timing Considerations (타이밍 고려 사항)
응용 프로그램 개발자는 렌더링 엔진이 초당 60 프레임의 재생 빈도를 유지할 수 있도록 노력해야 합니다. 60 FPS는 드로잉 프리미티브를 그래픽 하드웨어에 업로드하는 데 필요한 처리를 포함하여 처리가 수행 될 수 있는 각 프레임 사이에 약 16 밀리 초가 있음을 의미합니다.
실제로 이는 응용 프로그램 개발자가 다음을 수행해야 함을 의미합니다.
- - 가능한 경우 비동기식 이벤트 중심 프로그래밍을 하십시오
- - 중요한 작업을 수행하기 위해 작업자 스레드를 사용 하십시오.
- - 수동으로 이벤트 루프를 돌리지 마십시오.
- - 차단 기능 내에서 프레임 당 2 밀리 초 이상을 소비하지 마십시오.
이렇게하지 않으면 건너 뛴 프레임이 생겨 사용자 경험에 큰 영향을 줍니다.
참고 : 유혹에 빠지지 만 결코 사용 해서는 안되는 패턴 은 QML에서 호출 된 C ++ 코드 블록 내에서 블로킹을 방지하기 위해 QEventLoop을직접 생성 하거나 QCoreApplication :: processEvents ()를 호출하는 것입니다. 시그널 핸들러 나 바인딩에 이벤트 루프를 입력하면 QML 엔진이 다른 바인딩, 애니메이션, 전환 등을 계속 실행하기 때문에 위험합니다. 이러한 바인딩은 부작용을 일으킬 수 있습니다. 예를 들어, 이벤트 루프.
Profiling (프로파일링)
가장 중요한 팁은 Qt Creator에 포함 된 QML 프로파일 러를 사용하는 것입니다. 응용 프로그램에서 시간이 어디인지 알면 잠재적으로 존재할 수있는 문제 영역보다는 실제로 존재하는 문제 영역에 집중할 수 있습니다. QML 프로파일 링 도구를 사용하는 방법에 대한 자세한 내용은 Qt Creator 설명서를 참조하십시오.
가장 자주 실행되는 바인딩 또는 가장 많은 시간을 소비하는 응용 프로그램 기능을 결정하면 문제 영역을 최적화해야 하는지 또는 응용 프로그램의 구현 세부 사항을 재 설계하여 성능이 향상되는지 여부를 결정할 수 있습니다 . 프로파일링 하지 않고 코드를 최적화 하려고 하면 성능이 크게 향상되는 것이 아니라 사소한 문제가 발생할 수 있습니다.
JavaScript Code (자바 스크립트 코드)
대부분의 QML 응용 프로그램은 동적 함수, 신호 처리기 및 속성 바인딩 표현식의 형태로 많은 양의 JavaScript 코드를 포함합니다. 이것은 일반적으로 문제가되지 않습니다. 바인딩 컴파일러에서 수행 한 것과 같은 QML 엔진의 일부 최적화 덕분에 C ++ 함수를 호출하는 것보다 (일부 사용 사례에서) 더 빠를 수 있습니다. 그러나 불필요한 처리가 실수로 발생하지 않도록 주의해야 합니다.
Bindings (바인딩)
QML에는 두 가지 유형의 바인딩이 있습니다. 최적화 된 바인딩과 최적화되지 않은 바인딩입니다. QML 엔진은 완전한 JavaScript 실행 환경으로 전환 할 필요 없이 간단한 바인딩 표현식을 평가할 수 있는 최적화 된 바인딩 식 계산기를 사용하기 때문에 바인딩 식을 최대한 간단하게 유지하는 것이 좋습니다. 이러한 최적화 된 바인딩은 보다 복잡한 (최적화되지 않은) 바인딩보다 훨씬 효율적으로 평가됩니다. 바인딩 최적화의 기본 요구 사항은 컴파일시 액세스 되는 모든 심볼의 유형 정보를 알아야 한다는 것입니다.
최적화 가능성을 극대화하기 위해 표현식을 바인딩 할 때 피해야 할 사항 :
- - 자바 스크립트 중간 변수 선언하기
- - "var"속성에 접근하기
- - JavaScript 함수 호출하기
- - 바인딩 표현식 내에서 클로저 또는 함수 정의하기
- - 즉각적인 평가 범위 밖에서 속성에 액세스
- - 부작용으로 다른 속성 쓰기
바인딩은 작업하는 객체 및 속성의 유형을 알면 가장 빠릅니다. 이는 바인딩 표현식에서 비 최종 속성 조회가 경우에 따라 느려질 수 있음을 의미합니다. 예를 들어, 파생 된 유형에 의해 조회되는 속성 유형이 변경되었을 수 있습니다.
즉각적인 평가 범위는 다음 내용이 포함되어 있다고 요약하여 요약 할 수 있습니다.
- - expression 범위 객체의 속성 (바인딩 표현식의 경우 이것은 속성 바인딩이 속한 객체 임)
- - 컴퍼넌트 내의 임의의 오브젝트의 ID
- - 컴퍼넌트의 루트 항목의 프로퍼티
다른 구성 요소의 객체 및 해당 객체의 속성뿐만 아니라 JavaScript 가져 오기에서 정의되거나 포함 된 심볼은 즉각적인 평가 범위에 있지 않으므로 이러한 객체에 액세스하는 바인딩은 최적화되지 않습니다.
바인딩을 QML 엔진의 최적화 된 바인딩 표현식 평가 기가 최적화 할 수없고 전체 JavaScript 환경에서 평가해야하는 경우 위에 나열된 팁 중 일부는 더 이상 적용되지 않습니다. 예를 들어 매우 복잡한 바인딩에서 중간 JavaScript 변수에 속성 확인 결과를 캐시하는 것이 도움이 될 수 있습니다. 다음 섹션에는 이러한 종류의 최적화에 대한 자세한 정보가 있습니다.
Type-Conversion (유형 변환)
자바 스크립트 사용의 주요 비용 중 하나는 대부분의 경우 QML 유형의 속성에 액세스 할 때 기본 C ++ 데이터 (또는 그에 대한 참조)가 포함 된 외부 리소스가 있는 JavaScript 객체가 작성된다는 것입니다. 대부분의 경우 이 값은 상당히 저렴하지만 다른 값은 꽤 비쌀 수 있습니다.
비용이 많이 드는 한 가지 예는 C ++ QVariantMap Q_PROPERTY 를 QML "variant"속성에 할당하는 것 입니다. 특정 유형의 시퀀스 ( int, qreal, bool, QString 및 Qurl의 QList) 가 있지만 목록이 비쌀 수도 있습니다.)는 저렴해야한다. 다른 목록 유형은 값 비싼 변환 비용 (새로운 JavaScript Array 생성 및 C ++ 유형 인스턴스에서 JavaScript 값으로의 유형별 변환을 통해 하나씩 새 유형 추가)을 필요로합니다.
일부 기본 속성 유형 (예 : '문자열'및 'url'속성) 간의 변환은 비용이 많이 듭니다. 가장 일치하는 속성 유형을 사용하면 불필요한 변환을 피할 수 있습니다.
QVariantMap 을 QML에 노출 해야 하는 경우 "variant"속성 대신 "var"속성을 사용하십시오. 일반적으로 "property var"는 QtQuick 2.0 이상의 모든 유즈 케이스에 대해 "속성 변형"보다 우수한 것으로 간주되어야 합니다 ( "속성 변형"은 쓸모없는 것으로 표시됨). 참된 JavaScript 참조는 저장된 (특정 표현식에 필요한 변환 수를 줄일 수 있음).
Resolving Properties (속성 해결)
속성 해결에는 시간이 필요합니다. 어떤 경우에는 조회 결과를 캐싱하고 재사용 할 수 있지만 가능한 경우 불필요한 작업을 모두 피하는 것이 가장 좋습니다.
다음 예제에서는 자주 실행되는 코드 블록을 사용합니다 (이 경우 명시 적 루프의 내용이지만 일반적으로 평가되는 바인딩 표현식 일 수 있습니다). "rect"id와 "color"속성을 여러 번 사용하는 개체 :
// bad.qml import QtQuick 2.3 Item { width: 400; height: 200 Rectangle { id: rect; anchors.fill: parent; color: "blue" } function printValue(which, value) { console.log(which + " = " + value); } Component.onCompleted: { var t0 = new Date(); for (var i = 0; i < 1000; ++i) { printValue("red", rect.color.r); printValue("green", rect.color.g); printValue("blue", rect.color.b); printValue("alpha", rect.color.a); } var t1 = new Date(); console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); } }
블록에서 한 번만 공통 기초를 해결할 수 있습니다.
// good.qml import QtQuick 2.3 Item { width: 400; height: 200
Rectangle { id: rect; anchors.fill: parent; color: "blue"
} function printValue(which, value) { console.log(which + " = " + value); } Component.onCompleted: { var t0 = new Date(); for (var i = 0; i < 1000; ++i) { var rectColor = rect.color; // resolve the common base. printValue("red", rectColor.r); printValue("green", rectColor.g); printValue("blue", rectColor.b); printValue("alpha", rectColor.a); } var t1 = new Date(); console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); } }
이 간단한 변경만으로도 상당한 성능 향상을 가져옵니다. 위의 코드는 다음과 같이 속성 확인을 루프 밖으로 끌어 올리면 (루프 처리 중에 속성이 변경되지 않으므로) 더욱 향상 될 수 있습니다.
// better.qml import QtQuick 2.3 Item { width: 400; height: 200
Rectangle { id: rect; anchors.fill: parent; color: "blue" } function printValue(which, value) { console.log(which + " = " + value); } Component.onCompleted: { var t0 = new Date(); var rectColor = rect.color; // resolve the common base outside the tight loop. for (var i = 0; i < 1000; ++i) { printValue("red", rectColor.r); printValue("green", rectColor.g); printValue("blue", rectColor.b); printValue("alpha", rectColor.a); } var t1 = new Date(); console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); } }
Property Bindings (속성 바인딩)
속성 바인딩 표현식은 참조하는 속성 중 하나라도 변경되면 다시 평가됩니다. 따라서 바인딩 표현식은 가능한 한 간단하게 유지되어야합니다.
일부 처리를 수행하는 루프가 있지만 처리의 최종 결과 만 중요하면 나중에 속성 자체를 점진적으로 업데이트하지 않고 업데이트 해야 하는 속성에 나중에 할당하는 임시 누적기를 업데이트하는 것이 좋습니다 누적 중간 단계에서 바인딩 표현식의 재평가를 유발하지 않기 위해.
다음의 인위적인 예는이 점을 보여줍니다.
// bad.qml import QtQuick 2.3 Item { id: root width: 400; height: 200
property int accumulatedValue: 0 Text { anchors.fill: parent text: root.accumulatedValue.toString() onTextChanged: console.log("text binding re-evaluated") } Component.onCompleted: { var someData = [ 1, 2, 3, 4, 5, 20 ]; for (var i = 0; i < someData.length; ++i) { accumulatedValue = accumulatedValue + someData[i]; } } }
onCompleted 처리기의 루프는 "text"속성 바인딩이 6 번 재평가 되도록 합니다 (텍스트 값에 의존하는 다른 모든 속성 바인딩 및 onTextChanged 신호 처리기가 각각 다시 평가되도록 함) 시간을 표시하고 매번 표시 할 텍스트를 레이아웃 합니다). 이 경우에는 실제로 누적의 최종 가치에만 관심이 있으므로 분명히 불필요합니다.
다음과 같이 다시 작성할 수 있습니다.
// good.qml import QtQuick 2.3 Item { id: root width: 400; height: 200
property int accumulatedValue: 0 Text { anchors.fill: parent text: root.accumulatedValue.toString() onTextChanged: console.log("text binding re-evaluated") } Component.onCompleted: { var someData = [ 1, 2, 3, 4, 5, 20 ]; var temp = accumulatedValue; for (var i = 0; i < someData.length; ++i) { temp = temp + someData[i]; } accumulatedValue = temp; } }
Sequence tips (시퀀스 팁)
앞서 언급 했듯이 일부 시퀀스 유형은 빠르지 만 (예 : QList <int>, QList <qreal>, QList <bool>, QList < QString >, QStringList 및 QList < QUrl >) 다른 것들은 훨씬 느립니다. 느린 형식 대신 가능하면 이러한 유형을 사용하는 것 외에 최상의 성능을 얻기 위해 알아야 할 다른 성능 관련 의미 체계가 있습니다.
먼저, 시퀀스의 타입에 두 개의 다른 구현 예들이있다 : 서열은 여기서 하나 Q_PROPERTY (A)의 QObject를 시퀀스 (A)로부터 리턴되는 경우에 대한 (이 기준 서열 부를 것이다), 그리고 다른 Q_INVOKABLE (A)의 함수 QObject를 (우리 이것을 복사 순서라고 부름).
참조 시퀀스는 QMetaObject :: property () 를 통해 읽고 쓰여지고 따라서 QVariant 로 읽고 쓰여 집니다 . 즉, 시퀀스의 모든 요소 값을 JavaScript에서 변경하면 세 단계가 발생합니다. 전체 시퀀스는 QObject 에서 읽히고 ( QVariant로 올바른 유형의 시퀀스로 캐스팅 됩니다.); 지정된 인덱스의 요소가 그 순서로 변경됩니다. 완전한 시퀀스는 ( QVariant 로서 ) QObject에 다시 기록 될 것이다 .
실제 시퀀스가 JavaScript 객체의 리소스 데이터에 저장되므로 복사 시퀀스가 훨씬 간단 해 지므로 읽기 / 수정 / 쓰기 사이클이 발생하지 않습니다 (대신 리소스 데이터가 직접 수정 됨).
따라서 참조 시퀀스의 요소에 대한 쓰기는 복사 시퀀스의 요소에 대한 쓰기보다 훨씬 느립니다. 사실 N 요소 참조 시퀀스의 단일 요소에 쓰는 것은 N 요소 복사 시퀀스를 해당 참조 시퀀스에 할당하는 것과 비용이 같습니다. 따라서 임시 복사 시퀀스를 수정 한 다음에 결과를 할당하는 것이 좋습니다. 계산 중 참조 시퀀스.
다음 C ++ 유형의 존재 ( "Qt.example 1.0"네임 스페이스에 대한 사전 등록)를 가정합니다.
class SequenceTypeExample : public QQuickItem { Q_OBJECT Q_PROPERTY (QList<qreal> qrealListProperty READ qrealListProperty WRITE setQrealListProperty NOTIFY qrealListPropertyChanged) public: SequenceTypeExample() : QQuickItem() { m_list << 1.1 << 2.2 << 3.3; } ~SequenceTypeExample() {} QList<qreal> qrealListProperty() const { return m_list; } void setQrealListProperty(const QList<qreal> &list) { m_list = list; emit qrealListPropertyChanged(); } signals: void qrealListPropertyChanged(); private: QList<qreal> m_list; };
다음 예제는 엄격한 루프에서 참조 시퀀스의 요소에 쓰므로 성능이 좋지 않습니다.
// bad.qml import QtQuick 2.3 import Qt.example 1.0 SequenceTypeExample { id: root width: 200 height: 200 Component.onCompleted: { var t0 = new Date(); qrealListProperty.length = 100; for (var i = 0; i < 500; ++i) { for (var j = 0; j < 100; ++j) { qrealListProperty[j] = j; } } var t1 = new Date(); console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); } }
있는 QObject를 읽고 의한 내부 루프에서 물품 속성 "qrealListProperty[j] = j"
발현이 코드는 매우 만족스럽지 못한한다. 대신 기능적으로는 동일하지만 훨씬 빠른 것은 다음과 같습니다.
// good.qml import QtQuick 2.3 import Qt.example 1.0 SequenceTypeExample { id: root width: 200 height: 200 Component.onCompleted: { var t0 = new Date(); var someData = [1.1, 2.2, 3.3] someData.length = 100; for (var i = 0; i < 500; ++i) { for (var j = 0; j < 100; ++j) { someData[j] = j; } qrealListProperty = someData; } var t1 = new Date(); console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); } }
둘째, 속성의 변경 요소가 변경되면 속성의 변경 신호가 방출됩니다. 시퀀스 속성에서 특정 요소에 대한 바인딩이 많은 경우 해당 요소에 바인딩 된 동적 속성을 만들고 해당 동적 속성을 시퀀스 요소 대신 바인딩 표현식의 심볼로 사용하는 것이 좋습니다. 값이 변경되면 바인딩의 재평가 만 발생합니다.
이것은 대다수의 클라이언트가 절대로 공격해서는 안되지만 다음과 같은 일을 할 때를 대비하여 가치가 있는 비정상적인 유스 케이스입니다.
// bad.qml import QtQuick 2.3 import Qt.example 1.0 SequenceTypeExample { id: root property int firstBinding: qrealListProperty[1] + 10; property int secondBinding: qrealListProperty[1] + 20; property int thirdBinding: qrealListProperty[1] + 30; Component.onCompleted: { var t0 = new Date(); for (var i = 0; i < 1000; ++i) { qrealListProperty[2] = i; } var t1 = new Date(); console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); } }
루프에서 인덱스 2의 요소 만 수정 되더라도 변경 신호의 세분성은 전체 속성이 변경되었으므로 세 가지 바인딩이 모두 다시 평가됩니다. 따라서 중간 바인딩을 추가하는 것이 도움이 될 수 있습니다.
// good.qml import QtQuick 2.3 import Qt.example 1.0 SequenceTypeExample { id: root property int intermediateBinding: qrealListProperty[1] property int firstBinding: intermediateBinding + 10; property int secondBinding: intermediateBinding + 20; property int thirdBinding: intermediateBinding + 30; Component.onCompleted: { var t0 = new Date(); for (var i = 0; i < 1000; ++i) { qrealListProperty[2] = i; } var t1 = new Date(); console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); } }
위의 예에서는 중간 바인딩 만 매번 다시 평가되므로 성능이 크게 향상됩니다.
Value-Type tips (Value-Type 팁)
값 유형 속성 (font, color, vector3d 등)은 시퀀스 유형 등록 정보와 비슷한 QObject 등록 정보 및 변경 알림 의미 를가집니다 . 이와 같이, 시퀀스에 대해 위에 주어진 팁은 value-type 속성에도 적용 할 수 있습니다. 일반적으로 값 유형의 하위 속성 수가 시퀀스의 요소 수보다 훨씬 적기 때문에 일반적으로 값 유형에 대한 문제는 적지 만 재평가되는 바인딩 수가 증가하면 불필요하게 성능에 부정적인 영향을 미칩니다.
Other JavaScript Objects (기타 JavaScript 객체)
서로 다른 JavaScript 엔진은 다른 최적화를 제공합니다. Qt Quick 2가 사용 하는 JavaScript 엔진 은 객체 인스턴스화 및 속성 조회에 최적화되어 있지만 제공되는 최적화는 특정 기준에 의존합니다. 애플리케이션이 기준을 충족하지 못하면 JavaScript 엔진은 훨씬 느린 성능으로 "느린 경로"모드로 돌아갑니다. 따라서 항상 다음 기준을 충족하는지 확인하십시오.
- 가능한 한 eval ()을 사용하지 마십시오.
- 개체의 속성을 삭제하지 마십시오.
Common Interface Elements (공통 인터페이스 요소)
Text Elements (텍스트 요소)
텍스트 레이아웃을 계산하는 것은 느린 작업 일 수 있습니다. 가능하면 레이아웃 엔진 PlainText
대신 필요한 형식을 사용하는 것이 좋습니다 StyledText
. 레이아웃 엔진에 필요한 작업량이 줄어들 기 때문입니다. PlainText
이미지를 삽입해야 하거나 태그를 사용하여 전체 텍스트가 아닌 특정 서식 (굵게, 기울임 꼴 등)을 갖는 문자 범위를 지정해야 하는 경우에는 사용할 수 없습니다 StyledText
.
이 모드는 구문 분석 비용이 발생할 AutoText
수 있기 때문에 텍스트가 (아마도 그렇지 않은 경우 에만) 사용해야 합니다 StyledText
. RichText
로 모드는 사용하지 않아야 StyledText
비용의 일부분에 해당 기능의 대부분을 제공합니다.
Images (이미지)
이미지는 모든 사용자 인터페이스의 중요한 부분입니다. 불행히도, 그것들은로드하는데 걸리는 시간, 그들이 소비하는 메모리의 양, 그리고 그들이 사용되는 방식 때문에 문제의 큰 원인이 됩니다.
Asynchronous Loading (비동기 로딩)
이미지는 종종 꽤 크기 때문에 이미지를 로드해도 UI 스레드가 차단되지 않도록 하는 것이 좋습니다. QML Image 요소의 "비동기"속성 true
을 설정하면 로컬 파일 시스템에서 이미지를 비동기 적으로 로드 할 수 있습니다 (원격 이미지는 항상 비동기 적으로 로드 됩니다). 그러면 사용자 인터페이스의 미학에 부정적인 영향을 미치지 않습니다.
"asynchronous"속성이 설정된 이미지 요소는 true
우선 순위가 낮은 작업자 스레드에서 이미지를 로드합니다.
Explicit Source Size (명시 적 소스 크기)
응용 프로그램에서 큰 이미지를 로드하지만 크기가 작은 요소에 표시하는 경우 "sourceSize"속성을 렌더링 할 요소의 크기로 설정하면 크기가 작은 이미지 버전이 이미지에 표시되지 않고 메모리에 유지됩니다. 큰 것.
sourceSize를 변경하면 이미지가 다시로드됩니다.
Avoid Run-time Composition (실행 시간 구성 피하기)
또한 미리 구성된 이미지 리소스를 응용 프로그램과 함께 제공하여 런타임에 작곡 작업을 피할 수 있음을 기억하십시오 (예 : 그림자 효과가 있는 요소 제공).
Avoid Smoothing Images (이미지 스무딩 지양)
image.smooth
필요한 경우에만 활성화하십시오 . 일부 하드웨어에서는 느리지 만 이미지가 자연 크기로 표시되면 시각 효과가 없습니다.
Painting (그리기)
같은 영역을 여러 번 칠하지 마십시오. 배경을 여러 번 칠하지 않으려면 Rectangle 대신 루트 요소로 Item을 사용하십시오.
Position Elements With Anchors (앵커를 이용한 요소 배치)
서로 상대적으로 항목을 배치하는 바인딩 대신 앵커를 사용하는 것이 더 효율적입니다. rect1을 기준으로 rect2를 배치하는 바인딩의 사용을 고려하십시오.
Rectangle { id: rect1; x: 20; width: 200; height: 200 } Rectangle { id: rect2; x: rect1.x; y: rect1.y + rect1.height width: rect1.width - 20 height: 200 }
이것은 앵커를 사용하여보다 효율적으로 달성됩니다.
Rectangle { id: rect1 x: 20 width: 200; height: 200 } Rectangle { id: rect2 height: 200 anchors.left: rect1.left anchors.top: rect1.bottom anchors.right: rect1.right anchors.rightMargin: 20 }
앵커를 사용하는 대신 시각적 객체의 x, y, 너비 및 높이 속성에 바인딩 표현식을 할당하여 바인딩을 배치하는 것은 유연성이 극대화 되지만 상대적으로 느립니다.
레이아웃이 동적이지 않은 경우 레이아웃을 지정하는 가장 효과적인 방법은 x, y, width 및 height 속성의 정적 초기화를 사용하는 것입니다. 항목 좌표는 항상 부모와 관련이 있으므로, 부모의 0,0 좌표에서 고정 된 오프셋을 원한다면 앵커를 사용하지 않아야 합니다. 다음 예제에서 자식 Rectangle 객체는 같은 위치에 있지만 표시되는 앵커 코드는 정적 초기화를 통해 고정 위치 지정을 사용하는 코드만큼 효율적이지 않습니다.
Rectangle { width: 60 height: 60 Rectangle { id: fixedPositioning x: 20 y: 20 width: 20 height: 20 } Rectangle { id: anchorPositioning anchors.fill: parent anchors.margins: 20 } }
Models and Views (모델 및 뷰)
대부분의 응용 프로그램에는 보기에 데이터를 제공하는 하나 이상의 모델이 있습니다. 최대 성능을 얻기 위해서는 응용 프로그램 개발자가 알아야 할 몇 가지 의미가 있습니다.
Custom C++ Models (사용자 정의 C ++ 모델)
QML의 뷰와 함께 사용하기 위해 C ++에서 사용자 지정 모델을 직접 작성하는 것이 바람직합니다. 그러한 모델의 최적 구현은 이행해야 하는 유스 케이스에 크게 의존하지만 일부 일반 지침은 다음과 같습니다.
- 가능한 한 비동기가되도록하십시오.
- 모든 작업을 (낮은 우선 순위) 작업자 스레드에서 수행합니다.
- I / O 및 IPC가 최소화 될 수 있도록 백엔드 작업을 일괄 처리하십시오.
- 슬라이싱 슬라이스 창을 사용하여 결과를 캐싱합니다. 매개 변수는 프로파일 링의 도움을 받아 결정됩니다.
우선 순위가 낮은 작업자 스레드를 사용하는 것이 GUI 스레드가 굶주릴 위험을 최소화하는 것이 좋습니다 (성능이 저하 될 수 있음). 또한 동기화 및 잠금 메커니즘은 성능 저하의 중요한 원인이 될 수 있으므로 불필요한 잠금을 피하려면주의해야합니다.
ListModel QML Type (ListModel QML 유형)
QML은 ListView에 데이터를 공급하는 데 사용할 수 있는 ListModel 유형을 제공합니다 . 대부분의 유스 케이스에서는 충분하고 올바르게 사용되는 한 상대적으로 성능이 좋습니다.
Populate Within A Worker Thread (작업자 스레드 채우기)
ListModel 요소는 JavaScript에서 (낮은 우선 순위) 작업자 스레드로 채울 수 있습니다. 개발자는 WorkerScript 내에서 ListModel의 명시 적으로 "sync ()"를 호출 하여 변경 사항을 주 스레드와 동기화해야 합니다. 자세한 내용은 WorkerScript 설명서를 참조하십시오.
WorkerScript 요소 를 사용하면 별도의 JavaScript 엔진이 만들어지게 됩니다 (JavaScript 엔진은 스레드 당). 이로 인해 메모리 사용이 증가합니다. 그러나 여러 WorkerScript 요소는 모두 동일한 작업자 스레드를 사용하므로 두 번째 또는 세 번째 WorkerScript 요소 사용시 메모리 영향은 응용 프로그램에서 이미 사용 된 경우 무시할 수 있습니다.
Don't Use Dynamic Roles (동적 역할을 사용하지 마십시오.)
의 ListModel의 의 요소 QtQuick 이 훨씬 더 확대됨에 보다 QtQuick 유형이 변경되지 않는 경우, 캐싱 성능이 대폭 향상 - 성능 향상은 주로 특정 모델의 각 요소에서의 역할의 유형에 대해 가정에서 올 1. 유형이 요소에서 요소로 동적으로 변경 될 수 있는 경우 이 최적화는 불가능 해지고 모델의 성능은 한 단계 더 악화됩니다.
따라서 동적 입력은 기본적으로 사용되지 않습니다. 개발자는 동적 타이핑이 가능하도록 모델의 부울 "dynamicRoles"속성을 구체적으로 설정해야 합니다 (그리고 성능 저하가 발생합니다). 동적 유형 지정을 사용하지 않도록 응용 프로그램을 재 설계 할 수 있는 경우 동적 유형 지정을 사용하지 않는 것이 좋습니다.
Views (뷰)
뷰 델리게이트는 가능한 한 단순하게 유지되어야 합니다. 위임자에게 필요한 정보를 표시 할만큼 충분한 QML을 확보하십시오. 즉각적으로 필요하지 않은 추가 기능 (예 : 클릭 할 때 더 많은 정보가 표시되는 경우)은 필요할 때까지 생성되지 않아야 합니다 (지연 초기화의 다음 섹션 참조).
다음 목록은 대리인을 디자인 할 때 유의해야 할 사항을 요약 한 것입니다.
- 델리게이트에 있는 요소가 적을수록 생성 속도가 빨라 지므로 보기를 더 빠르게 스크롤 할 수 있습니다.
- 델리게이트의 바인딩 수를 최소한으로 유지하십시오. 특히 대리자 내에서 상대적 위치 지정을 위해 바인딩이 아닌 앵커를 사용하십시오.
- 대리자 내에서 ShaderEffect 요소를 사용하지 마십시오 .
- 대리자에서 클리핑을 사용하지 마십시오.
보이는 영역 밖에서 대리자를 비동기 적으로 생성하고 버퍼링 할 수 있도록 보기의 cacheBuffer 속성을 설정할 수 있습니다. cacheBuffer를 사용하는 것은 중요하지 않고 단일 프레임 내에서 생성 될 가능성이 적은 뷰 대리자에게 권장됩니다.
cacheBuffer는 추가 대리자를 메모리에 보관합니다. 따라서 cacheBuffer 활용에서 파생 된 값은 추가 메모리 사용량과 균형을 이루어야 합니다. 개발자는 cacheBuffer 활용으로 인한 메모리 증가로 인해 드물게 스크롤 할 때 프레임 속도가 저하 될 수 있으므로 벤치마킹을 사용하여 유스 케이스에 가장 적합한 값을 찾아야 합니다..
Visual Effects (시각 효과)
Qt Quick 2 에는 개발자와 디자이너가 매우 매력적인 사용자 인터페이스를 만들 수 있는 몇 가지 기능이 포함되어 있습니다. 유동성 및 동적 전환뿐만 아니라 시각 효과는 응용 프로그램에서 큰 효과를 발휘할 수 있지만 QML에서 일부 기능을 사용할 때는 성능에 영향을 줄 수 있으므로 주의해야 합니다.
Animations (애니메이션)
일반적으로 속성에 애니메이션을 적용하면 해당 속성을 참조하는 바인딩이 다시 평가됩니다. 일반적으로 이것은 원하는 것이지만 다른 경우에는 애니메이션을 수행하기 전에 바인딩을 비활성화 한 다음 애니메이션이 완료되면 바인딩을 다시 할당하는 것이 좋습니다.
애니메이션 중에는 JavaScript를 실행하지 마십시오. 예를 들어, x 속성 애니메이션의 각 프레임에 대해 복잡한 JavaScript 표현식을 사용하지 않아야합니다.
개발자는 주 애니메이션에서 실행되므로 스크립트 애니메이션을 사용할 때 특히 주의해야 합니다. 따라서 너무 오래 걸리면 프레임을 건너 뛸 수 있습니다.
Particles (입자)
Qt는 빠른 입자 모듈은 아름다운 입자 효과는 사용자 인터페이스에 완벽하게 통합 할 수 있습니다. 그러나 모든 플랫폼에는 다른 그래픽 하드웨어 기능이 있으며 Particles 모듈은 하드웨어가 정상적으로 지원할 수 있는 매개 변수를 제한 할 수 없습니다. 더 많은 입자를 렌더링 하려고 시도할수록 (그리고 그 크기가 클수록) 60 FPS로 렌더링 하려면 그래픽 하드웨어가 더 빨라질 필요가 있습니다. 더 많은 파티클에 영향을 주려면 더 빠른 CPU가 필요합니다. 따라서 대상 플랫폼에서 모든 입자 효과를 신중하게 테스트하여 60FPS로 렌더링 할 수 있는 입자의 수와 크기를 조정하는 것이 중요합니다.
불필요한 시뮬레이션을 피하기 위해 파티클 시스템을 사용하지 않을 때 (예 : 보이지 않는 요소에서) 비활성화 할 수 있습니다.
더 자세한 정보는 Particle System Performance Guide 를 참조하십시오.
Controlling Element Lifetime (요소 수명 제어)
단일 QML 파일에 포함 된 간단한 모듈 형 구성 요소로 응용 프로그램을 분할하면 응용 프로그램 시작 시간을 단축하고 메모리 사용을 보다 잘 제어 할 수 있으며 응용 프로그램에서 활성이지만 보이지 않는 요소의 수를 줄일 수 있습니다.
Lazy Initialization (느린 초기화)
QML 엔진은 구성 요소의 로드 및 초기화가 프레임을 건너 뛰지 않도록 보장하기 위해 몇 가지 까다로운 작업을 수행합니다. 그러나 시작 시간을 줄이는 더 좋은 방법은 할 필요가 없는 일을 피하고 필요한 때까지 작업을 지연시키는 것보다 낫습니다. 이 작업은 로더 를 사용하거나 구성 요소를 동적으로 생성 하여 수행 할 수 있습니다 .
Using Loader (로더 사용)
로더는 구성 요소의 동적로드 및 언로드를 허용하는 요소입니다.
- Loader의 "활성"속성을 사용하면 초기화가 필요할 때까지 지연 될 수 있습니다.
- 오버로드 된 버전의 "setSource ()"함수를 사용하여 초기 속성 값을 제공 할 수 있습니다.
- 로더 비동기 특성을 true로 설정하면 구성 요소가 인스턴스 화되는 동안 유동성이 향상 될 수도 있습니다.
Using Dynamic Creation (동적 생성 사용)
개발자는 Qt.createComponent () 함수를 사용하여 JavaScript 내에서 런타임에 동적으로 구성 요소를 만든 다음 createObject ()를 호출하여 인스턴스를 생성 할 수 있습니다. 호출에 지정된 소유권 의미에 따라 개발자가 만든 개체를 수동으로 삭제해야 할 수 있습니다. 자세한 내용 은 JavaScript에서 Dynamic QML Object Creation 을 참조하십시오.
Destroy Unused Elements (사용되지 않는 요소 삭제)
보이지 않는 요소의 자식 (예 : 첫 번째 탭이 표시되는 동안 탭 위젯의 두 번째 탭) 인 경우 보이지 않는 요소는 대부분의 경우 지연적으로 초기화해야 하며 더 이상 사용하지 않으면 삭제해야 합니다 (예 : 렌더링, 애니메이션, 속성 바인딩 평가 등)을 활성 상태로 두는 지속적인 비용을 피할 수 있습니다.
Loader 요소로로드 된 항목은 Loader의 "source"또는 "sourceComponent"속성을 재설정하여 해제 될 수 있지만 다른 항목은 destroy ()를 호출하여 명시 적으로 해제 될 수 있습니다. 경우에 따라 항목을 활성 상태로 두어야 할 수도 있습니다.이 경우 최소한 항목을 보이지 않게해야 합니다.
활성이지만 보이지 않는 요소에 대한 자세한 내용은 렌더링 섹션을 참조하십시오.
Rendering (렌더링)
QtQuick 2 에서 렌더링하는 데 사용되는 장면 그래프 는 매우 동적 인 애니메이션 사용자 인터페이스가 60FPS로 유동적으로 렌더링되도록합니다. 그러나 렌더링 성능을 크게 떨어 뜨릴 수 있는 몇 가지 사항이 있습니다. 개발자는 가능하면 이러한 함정을 조심해야 합니다.
Clipping (클리핑)
클리핑은 기본적으로 비활성화되어 있으므로 필요할 때만 활성화해야합니다.
클리핑은 최적화가 아닌 시각 효과입니다. 그것은 렌더러의 복잡도를 줄이기보다는 증가시킵니다. 클리핑이 활성화 된 경우, 아이템은 자체 페인팅과 그 아이의 페인팅을 경계 사각형에 클립합니다. 이렇게하면 렌더러가 요소의 그리기 순서를 자유롭게 재정렬 할 수 없게되어 최적 이하의 장면 그래프를 순회하는 결과를 가져옵니다.
델리게이트 내부의 클리핑은 특히 좋지 않으므로 피해야합니다.
Over-drawing and Invisible Elements (과도한 그림과 보이지 않는 요소들)
다른 요소 (불투명 한 요소)로 완전히 덮여있는 요소가 있는 경우 "visible"속성을 설정하는 것이 가장 좋으며 false
그렇지 않으면 불필요하게 그려집니다.
마찬가지로 보이지 않는 요소 (예 : 첫 번째 탭이 표시되는 동안 탭 위젯의 두 번째 탭)는 시작 시간에 초기화해야 합니다 (예 : 두 번째 탭을 인스턴스화 하는 데 드는 비용이 너무 많이 소요되는 경우). 탭을 활성화 한 경우에만 할 수 있음), 그리는 데 드는 비용을 피하기 위해 "visible"속성을 false로 설정해야 합니다 (이전에 설명했듯이 이후 애니메이션이나 바인딩 평가의 비용이 계속 발생 함). 그들은 여전히 활동적이다).
Translucent vs Opaque (불투명 대 반투명)
일반적으로 불투명 한 콘텐츠는 반투명보다 그리기가 훨씬 빠릅니다. 그 이유는 반투명 한 콘텐트가 블렌딩을 필요로하고 렌더러가 불투명 한 콘텐트를 더 잘 최적화 할 수 있기 때문입니다.
반투명 한 픽셀이 있는 이미지는 대부분 불투명하지만 완전히 반투명 한 것으로 처리됩니다. 가장자리가 투명한 BorderImage 에서도 마찬가지입니다 .
Shaders (셰이더)
ShaderEffect의 유형은 가능한 약간의 오버 헤드와 Qt는 빠른 응용 프로그램에서 GLSL 코드 인라인을 배치 할 수 있습니다. 그러나 프래그먼트 프로그램은 렌더링 된 모양의 모든 픽셀에 대해 실행해야한다는 것을 인식하는 것이 중요합니다. 저가형 하드웨어에 배포 할 때 셰이더가 많은 픽셀을 차지하는 경우 성능 저하를 피하기 위해 조각 쉐이더를 몇 가지 지침으로 유지해야 합니다.
GLSL로 작성된 셰이더는 복잡한 변환과 시각 효과를 작성할 수 있지만 주의해서 사용해야합니다.
ShaderEffectSource를 사용 하면 장면을 그리기 전에 FBO에 미리 렌더링 됩니다. 이 여분의 오버 헤드는 상당히 비쌀 수 있습니다.
Memory Allocation And Collection (메모리 할당 및 수집)
응용 프로그램에서 할당 할 메모리 양과 해당 메모리를 할당하는 방법은 매우 중요한 고려 사항입니다. 메모리가 제한된 장치의 메모리 부족 조건에 대한 명백한 우려 외에도 힙에 메모리를 할당하는 것은 계산 상 매우 비쌉니다. 특정 할당 전략을 사용하면 페이지에서 데이터 조각화가 증가 할 수 있습니다. JavaScript는 자동으로 가비지 수집되는 관리되는 메모리 힙을 사용합니다. 이는 몇 가지 장점이 있지만 몇 가지 중요한 의미가 있습니다.
QML로 작성된 응용 프로그램은 C ++ 힙과 자동 관리 JavaScript 힙의 메모리를 사용합니다. 애플리케이션 개발자는 성능을 극대화하기 위해 각각의 미묘함을 인식해야 합니다.
Tips For QML Application Developers (QML 응용 프로그램 개발자를 위한 팁)
이 섹션에 포함 된 팁과 제안은 지침 일 뿐이며 모든 상황에 적용되지 않을 수도 있습니다. 최선의 결정을 내릴 수 있도록 경험적 기준을 사용하여 애플리케이션을 신중하게 벤치마킹하고 분석하십시오.
nstantiate and initialize components lazily (구성 요소를 느리게 초기화하고 초기화하십시오.)
응용 프로그램이 여러보기 (예 : 여러 탭)로 구성되어 있지만 한 번에 하나씩 만 필요한 경우에는 지연 인스턴스화를 사용하여 주어진 시간에 할당해야 할 메모리 양을 최소화 할 수 있습니다. 자세한 정보 는 Lazy Initialization 의 이전 섹션 을 참조하십시오.
Destroy unused objects (사용하지 않는 객체를 파괴하십시오)
구성 요소를 느슨하게 로드하거나 JavaScript 표현식 중에 동적으로 객체를 만드는 경우 destroy()
자동 가비지 수집이 수행되기를 기다리는 대신 수동으로 수행하는 것이 더 좋습니다 . 자세한 내용은 요소 수명 제어 에 대한 이전 절 을 참조하십시오.
Don't manually invoke the garbage collector (수동으로 가비지 수집기를 호출하지 마십시오.)
대부분의 경우 가비지 콜렉터를 수동으로 호출하는 것은 현명한 방법이 아닙니다. 상당한 시간 동안 GUI 스레드를 차단하기 때문입니다. 이로 인해 건너 뛴 프레임과 불규칙한 애니메이션이 생길 수 있습니다.
가비지 콜렉터를 수동으로 호출하는 것이 허용되는 경우도 있지만 (다음 섹션에서 자세히 설명 함) 대부분의 경우 가비지 수집기 호출은 불필요하고 비생산적입니다.
Avoid complex bindings (복잡한 바인딩을 피하십시오)
복잡한 바인딩의 성능 저하는 제외하고 (예 : 평가를 수행하기 위해 JavaScript 실행 컨텍스트를 입력해야하기 때문에), C ++ 힙과 JavaScript 힙 모두에서 QML의 최적화로 평가할 수있는 바인딩보다 많은 메모리를 차지합니다 바인딩 표현식 평가 기.
Avoid defining multiple identical implicit types (여러 개의 동일한 암시 적 유형 정의 안 함)
QML 요소에 QML에 정의 된 사용자 지정 속성이 있으면 자체 암시 적 형식이 됩니다. 자세한 내용은 다음 섹션에서 자세히 설명합니다. 여러 개의 동일한 암시 적 유형이 구성 요소에서 인라인으로 정의되면 일부 메모리가 낭비됩니다. 이러한 상황에서는 일반적으로 재사용 할 수 있는 새 구성 요소를 명시 적으로 정의하는 것이 좋습니다.
사용자 지정 속성을 정의하면 유익한 성능 최적화 (예 : 필요하거나 다시 계산되는 바인딩 수를 줄이거나) 또는 구성 요소의 유지 관리 및 모듈성을 향상시킬 수 있습니다. 이 경우 맞춤 속성을 사용하는 것이 좋습니다. 그러나 새로운 유형은 두 번 이상 사용되는 경우 메모리를 절약하기 위해 자체 구성 요소 (.qml 파일)로 분할되어야 합니다.
Reuse existing components (기존 구성 요소 재사용)
새 구성 요소를 정의하려는 경우 해당 플랫폼의 구성 요소 집합에 해당 구성 요소가 아직 존재하지 않는지 이중으로 확인하는 것이 좋습니다. 그렇지 않으면 QML 엔진이 본질적으로 이미 존재하고 잠재적으로 이미 로드된 다른 구성 요소의 복제본인 유형에 대한 유형 데이터를 생성하고 저장하게 됩니다.
Use singleton types instead of pragma library scripts (pragma 라이브러리 스크립트 대신 싱글 톤 유형 사용)
pragma 라이브러리 스크립트를 사용하여 응용 프로그램 전체의 인스턴스 데이터를 저장하는 경우 대신 QObject 단독 형을 사용하는 것이 좋습니다. 이렇게하면 성능이 향상되고 사용되는 JavaScript 힙 메모리가 줄어 듭니다.
Memory Allocation in a QML Application (QML 응용 프로그램의 메모리 할당)
QML 애플리케이션의 메모리 사용은 C ++ 힙 사용과 JavaScript 힙 사용의 두 부분으로 나눌 수 있습니다. QML 엔진이나 JavaScript 엔진에 의해 할당 될 때마다 할당되는 메모리 중 일부는 피할 수 없는 반면 나머지는 애플리케이션 개발자가 결정한 사항에 따라 결정됩니다.
C ++ 힙에는 다음이 포함됩니다.
- QML 엔진의 고정적이고 피할 수없는 오버 헤드 (구현 데이터 구조, 컨텍스트 정보 등);
- 애플리케이션에 의해 로드되는 모듈 및 구성 요소에 따라 QML 엔진에 의해 생성되는 유형별 속성 메타 데이터를 포함하여 구성 요소 별 컴파일 데이터 및 유형 정보.
- 객체 별 C ++ 데이터 (속성 값 포함)와 요소 별 메타 객체 계층 구조 (응용 프로그램이 인스턴스화하는 구성 요소에 따라 다름)
- QML 가져 오기 (라이브러리)에 의해 특별히 할당 된 모든 데이터.
JavaScript 힙에는 다음이 포함됩니다.
- JavaScript 엔진 자체의 고정적이고 피할 수없는 오버 헤드 (내장 JavaScript 유형 포함).
- JavaScript 통합의 고정 된 피할 수없는 오버 헤드 (로드 된 유형, 함수 템플리트 등을 위한 생성자 함수);
- 유형별 레이아웃 정보 및 런타임시 JavaScript 엔진에 의해 생성 된 기타 내부 유형 데이터 (각 유형에 대해서는 아래 참고 참조).
- 객체 당 JavaScript 데이터 ( "var"속성, JavaScript 함수 및 신호 처리기 및 최적화되지 않은 바인딩 표현식).
- 표현식 평가 중에 할당 된 변수.
또한 주 스레드에서 사용할 수 있도록 할당 된 JavaScript 힙 하나와 선택적으로 WorkerScript 스레드 에서 사용하도록 할당 된 다른 JavaScript 힙이 하나씩 있습니다. 응용 프로그램이 WorkerScript 요소를 사용하지 않으면 해당 오버 헤드가 발생하지 않습니다. JavaScript 힙 크기는 수 메가 바이트가 될 수 있으므로 메모리가 부족한 장치 용으로 작성된 응용 프로그램은 목록 모델을 비동기 적으로 채우는 데 유용함에도 불구하고 WorkerScript 요소 를 사용하지 않는 것이 가장 좋습니다 .
QML 엔진과 JavaScript 엔진은 모두 관찰 된 유형에 대한 유형 데이터의 자체 캐시를 자동으로 생성합니다. 응용 프로그램에 의해 로드되는 모든 구성 요소는 고유 한 (명시 적) 유형이며 QML에서 자체 사용자 정의 등록 정보를 정의하는 모든 요소 (구성 요소 인스턴스)는 암시 적 유형입니다. 사용자 지정 속성을 정의하지 않은 모든 요소 (구성 요소 인스턴스)는 JavaScript 및 QML 엔진에서 자체 암시 적 형식이 아닌 구성 요소에 의해 명시 적으로 정의 된 형식으로 간주됩니다.
다음 예제를 고려하십시오.
import QtQuick 2.3 Item { id: root Rectangle { id: r0 color: "red" } Rectangle { id: r1 color: "blue" width: 50 } Rectangle { id: r2 property int customProperty: 5 } Rectangle { id: r3 property string customProperty: "hello" } Rectangle { id: r4 property string customProperty: "hello" } }
이전 예제에서 사각형 r0과 r1에는 사용자 지정 속성이 없으므로 JavaScript 및 QML 엔진은 둘 다 동일한 유형으로 간주합니다. 즉, r0 및 r1은 모두 명시 적으로 정의 된 Rectangle 유형으로 간주됩니다. 사각형 r2, r3 및 r4는 각각 사용자 지정 속성을 가지며 각기 다른 (암시 적) 유형으로 간주됩니다. r3과 r4는 동일한 속성 정보를 가지고 있더라도 사용자 정의 속성이 인스턴스 인 구성 요소에 선언되지 않았기 때문에 각각 다른 유형으로 간주됩니다.
r3 및 r4가 RectangleWithString 구성 요소의 인스턴스이고 customProperty라는 문자열 속성 선언이 해당 구성 요소 정의에 포함 된 경우 r3 및 r4는 동일한 유형으로 간주됩니다 (즉, RectangleWithString 유형이 아니라 자체 암시 적 유형을 정의).
In-Depth Memory Allocation Considerations 심층 메모리 할당 고려 사항
메모리 할당이나 성능 절충에 관한 결정을 내릴 때마다 CPU 캐시 성능, 운영 체제 페이징 및 JavaScript 엔진 가비지 수집의 영향을 염두에 두는 것이 중요합니다. 잠재적 솔루션은 최고의 솔루션이 선택되도록 신중하게 벤치마킹해야합니다.
응용 프로그램 개발자가 개발중인 플랫폼의 구현 세부 사항에 대한 실질적인 지식과 컴퓨터 과학의 기본 원리에 대한 확실한 이해를 대체 할 수있는 일반적인 지침은 없습니다. 또한, 절충 결정을 내릴 때 훌륭한 벤치 마크 및 분석 도구 세트를 대체 할 수있는 이론적 인 계산 방법은 없습니다.
Fragmentation 단편화
단편화는 C ++ 개발 문제입니다. 응용 프로그램 개발자가 C ++ 유형이나 플러그인을 정의하지 않으면 이 절을 무시해도 됩니다.
시간이 지남에 따라 응용 프로그램은 많은 양의 메모리를 할당하고 해당 메모리에 데이터를 작성한 다음 일부 데이터의 사용이 끝나면 일부만 비울 것입니다. 이로 인해 "비어있는"메모리가 연속되지 않는 청크에 위치 할 수 있으며, 다른 응용 프로그램에서 운영 체제로 되돌릴 수 없습니다. 또한 "실제"데이터가 많은 다른 물리적 메모리 페이지에 분산 될 수 있으므로 응용 프로그램의 캐싱 및 액세스 특성에 영향을 미칩니다. 이것은 차례로 운영 체제가 스왑 되도록 강제 할 수 있습니다. 이는 파일 시스템 I / O를 유발할 수 있습니다. 이는 상대적으로 말하면 매우 느린 작업입니다.
조각 모음은 풀 할당 자 (및 기타 인접 메모리 할당 자)를 사용하거나, 객체 수명을 신중하게 관리하거나, 캐시를 주기적으로 정리 및 재 구축하거나, 메모리 관리 런타임을 활용하여 한 번에 할당되는 메모리 양을 줄임으로써 피할 수 있습니다 가비지 콜렉션 (예 : JavaScript).
Garbage Collection 쓰레기 수거
JavaScript는 가비지 수집을 제공합니다. C ++ 힙과 달리 JavaScript 힙에서 할당 된 메모리는 JavaScript 엔진에서 소유합니다. 엔진은 JavaScript 힙에서 모든 참조되지 않은 데이터를 주기적으로 수집합니다.
Implications of Garbage Collection 쓰레기 수거의 함의
가비지 수집에는 장단점이 있습니다. 이는 객체 수명을 수동으로 관리하는 것이 덜 중요하다는 것을 의미합니다. 그러나 잠재적으로 오래 지속될 수 있는 작업은 응용 프로그램 개발자가 제어 할 수 없는 시간에 JavaScript 엔진에 의해 시작될 수 있음을 의미합니다. JavaScript 힙 사용이 응용 프로그램 개발자에 의해 신중하게 고려되지 않으면 가비지 수집 빈도와 기간이 응용 프로그램 환경에 부정적인 영향을 줄 수 있습니다.
Manually Invoking the Garbage Collector 수동으로 가비지 수집기 호출
QML로 작성된 응용 프로그램은 가비지 수집을 일부 단계에서 수행해야 합니다. 가비지 수집은 사용 가능한 여유 메모리 양이 적을 때 JavaScript 엔진에 의해 자동으로 트리거되지만 가비지 수집기를 수동으로 호출 할 시기는 응용 프로그램 개발자가 결정할 때 가끔 더 좋습니다 (일반적으로 그렇지는 않지만).
응용 프로그램 개발자는 응용 프로그램이 상당 기간 동안 유휴 상태 일 때를 가장 잘 이해할 수 있습니다. QML 응용 프로그램이 많은 JavaScript 힙 메모리를 사용하여 특히 성능에 민감한 작업 (예 : 목록 스크롤, 애니메이션 등) 동안 정기적으로 중단되는 가비지 수집 주기를 유발할 경우 응용 프로그램 개발자가 수동으로 활동이 없는 기간의 가비지 컬렉터 유휴 기간은 활동이 발생하는 동안 가비지 컬렉터를 호출하여 발생하는 사용자 경험 (건너 뛴 프레임, 불규칙한 애니메이션 등)의 저하를 사용자가 감지하지 못하기 때문에 가비지 수집을 수행하는 데 이상적입니다.
가비지 컬렉터는 gc()
JavaScript 내에서 호출하여 수동으로 호출 할 수 있습니다 . 이렇게하면 포괄적인 수집주기가 수행되기 때문에 완료하는 데 수백에서 수천 밀리 초 이상 걸릴 수 있으므로 가능하면 피해야 합니다.
Memory vs Performance Trade-offs 메모리 대 성능 절충
경우에 따라 메모리 사용량을 늘리면 처리 시간이 줄어들 수 있습니다. 예를 들어, 긴밀한 루프에서 사용 된 심볼 검색 결과를 JavaScript 표현식의 임시 변수에 캐싱하면 해당 표현식을 평가할 때 성능이 크게 향상되지만 임시 변수를 할당해야 합니다. 경우에 따라 이러한 상반 관계는 합리적입니다 (위의 경우와 같이 거의 항상 감지 할 수 있음). 그러나 다른 경우에는 시스템의 메모리 부담이 증가하지 않도록 약간 더 오래 걸릴 수 있습니다. .
어떤 경우에는 증가 된 메모리 압력의 영향이 극단적 일 수 있습니다. 일부 상황에서는 메모리 사용량을 가정 한 성능 향상으로 트레이드하면 page-thrash 또는 cache-thrash가 증가하여 성능이 크게 저하 될 수 있습니다. 주어진 상황에서 어떤 솔루션이 가장 적합한 지 판단하기 위해 절충안의 영향을 신중하게 벤치마킹해야 합니다.
캐시 성능과 메모리 타임 트레이드 오프에 대한 자세한 정보는 Ulrich Drepper의 우수 기사 "모든 프로그래머가 메모리에 대해 알아야 할 사항"(http://ftp.linux.org.ua/pub/docs/에서 사용 가능)을 참조하십시오. developer / general / cpumemory.pdf, 2012 년 4 월 18 일), C ++ 관련 최적화에 대한 정보는 Agner Fog의 C ++ 애플리케이션 최적화에 관한 훌륭한 매뉴얼을 참조하십시오 (http://www.agner.org/optimize/). 2012 년 4 월 18 일).