diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index a1939413874f994b01d0181ec7eb447e7034e9df..7697fb82f5bb8028eb22790049ea3ed80058b5b8 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -481,6 +481,9 @@ void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
 #if JVET_M0068_M0171_MMVD_CLEANUP
   PU::restrictBiPredMergeCandsOne(pu);
 #endif
+#if JVET_M0823_MMVD_ENCOPT
+  pu.mmvdEncOptMode = 0;
+#endif
 }
 void MergeCtx::setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx)
 {
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index f9a9da2d516638c304e6b331e089392af0729388..ede8d83edc59b0b1acf952398d651a13cdd9adb1 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -326,7 +326,9 @@ void InterPrediction::xSubPuMC( PredictionUnit& pu, PelUnitBuf& predBuf, const R
       subPu.UnitArea::operator=(UnitArea(pu.chromaFormat, Area(x, y, dx, dy)));
       subPu = curMi;
       PelUnitBuf subPredBuf = predBuf.subBuf(UnitAreaRelative(pu, subPu));
-
+#if JVET_M0823_MMVD_ENCOPT
+      subPu.mmvdEncOptMode = 0;
+#endif
       motionCompensation(subPu, subPredBuf, eRefPicList);
       secDim = later - secStep;
     }
@@ -396,8 +398,12 @@ void InterPrediction::xPredInterUni(const PredictionUnit& pu, const RefPicList&
          pu.cu->lumaSize(),
          sps);
 
-
+#if JVET_M0823_MMVD_ENCOPT
+  int numOfPass = (pu.mmvdMergeFlag && pu.mmvdEncOptMode) ? 1 : (std::min((int)pcYuvPred.bufs.size(), m_maxCompIDToPred + 1));
+  for (uint32_t comp = COMPONENT_Y; comp < numOfPass; comp++)
+#else
   for( uint32_t comp = COMPONENT_Y; comp < pcYuvPred.bufs.size() && comp <= m_maxCompIDToPred; comp++ )
+#endif
   {
     const ComponentID compID = ComponentID( comp );
     if (compID == COMPONENT_Y && !luma)
@@ -458,7 +464,11 @@ void InterPrediction::xPredInterBi(PredictionUnit& pu, PelUnitBuf &pcYuvPred)
       bioApplied = false;
     }
   }
-
+#if JVET_M0823_MMVD_ENCOPT
+  if (pu.mmvdEncOptMode == 2 && pu.mmvdMergeFlag) {
+    bioApplied = false;
+  }
+#endif
   for (uint32_t refList = 0; refList < NUM_REF_PIC_LIST_01; refList++)
   {
     if( pu.refIdx[refList] < 0)
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index cb70e1dcec69ea1ecc85fef4c8dd0ba875dba20c..cecbc4a8e8656ecca050212767b1493afb8852c6 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -93,6 +93,8 @@
 #define JVET_M0464_UNI_MTS                                1
 #define JVET_M0068_M0171_MMVD_CLEANUP                     1 // MMVD cleanup with 1) flip removal, 2) L1 zero vector fix, 3) bi-pred restriction after merge/MMVD
 #define JVET_M0255_FRACMMVD_SWITCH                        1 // disable fractional MVD in MMVD adaptively
+#define JVET_M0312_FRACMMVD_SWITCH_FOR_UHD                1 // disable fractional MVD in MMVD for UHD sequences
+#define JVET_M0823_MMVD_ENCOPT                            1 // encoder optimization for MMVD
 
 #if JVET_M0464_UNI_MTS
 typedef std::pair<int, bool> TrMode;
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index ecaf89ca16990d33b31fcee0e1c6c86d1e0c0d15..bb4d9470ca183c7fc17ce36418b3d1db1b967979 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -371,6 +371,9 @@ void PredictionUnit::initData()
   shareParentSize.width = -1;
   shareParentSize.height = -1;
 #endif
+#if JVET_M0823_MMVD_ENCOPT
+  mmvdEncOptMode = 0;
+#endif
 }
 
 PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData)
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 4eb057dde4a9cfe99bc1f4970d4f1e29e9f9613d..a1e1a75b6e7220573877e6f796ba45b3612b5c49 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -377,6 +377,9 @@ struct InterPredictionData
 #endif
   Mv        bv;                             // block vector for IBC
   Mv        bvd;                            // block vector difference for IBC
+#if JVET_M0823_MMVD_ENCOPT
+  uint8_t   mmvdEncOptMode;                  // 0: no action 1: skip chroma MC for MMVD candidate pre-selection 2: skip chroma MC and BIO for MMVD candidate pre-selection
+#endif
 };
 
 struct PredictionUnit : public UnitArea, public IntraPredictionData, public InterPredictionData
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 3667f70d95cd8b695aedfdb2dfe64eaf43fb417a..a4fd0463202cd0050276c7dd2597ad3ae430a766 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1983,7 +1983,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.mmvdSkip = true;
       int tempNum = 0;
       tempNum = MMVD_ADD_NUM;
+#if !JVET_M0823_MMVD_ENCOPT
       bool allowDirection[4] = { true, true, true, true };
+#endif
       for (uint32_t mergeCand = mergeCtx.numValidMergeCand; mergeCand < mergeCtx.numValidMergeCand + tempNum; mergeCand++)
       {
         const int mmvdMergeCand = mergeCand - mergeCtx.numValidMergeCand;
@@ -1993,9 +1995,12 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         int bitsCand = 0;
         int baseIdx;
         int refineStep;
+#if !JVET_M0823_MMVD_ENCOPT
         int direction;
+#endif
         baseIdx = mmvdMergeCand / MMVD_MAX_REFINE_NUM;
         refineStep = (mmvdMergeCand - (baseIdx * MMVD_MAX_REFINE_NUM)) / 4;
+#if !JVET_M0823_MMVD_ENCOPT
         direction = (mmvdMergeCand - baseIdx * MMVD_MAX_REFINE_NUM - refineStep * 4) % 4;
         if (refineStep == 0)
         {
@@ -2005,6 +2010,7 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         {
           continue;
         }
+#endif
         bitsBaseIdx = baseIdx + 1;
         if (baseIdx == MMVD_BASE_MV_NUM - 1)
         {
@@ -2024,13 +2030,20 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
 
         PU::spanMotionInfo(pu, mergeCtx);
         distParam.cur = singleMergeTempBuffer->Y();
+#if JVET_M0823_MMVD_ENCOPT
+        pu.mmvdEncOptMode = (refineStep > 2 ? 2 : 1);
+#endif
         m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer);
-
+#if JVET_M0823_MMVD_ENCOPT
+        pu.mmvdEncOptMode = 0;
+#endif
         Distortion uiSad = distParam.distFunc(distParam);
 
 
         double cost = (double)uiSad + (double)bitsCand * sqrtLambdaForFirstPass;
+#if !JVET_M0823_MMVD_ENCOPT
         allowDirection[direction] = cost >  1.3 * candCostList[0] ? 0 : 1;
+#endif
         insertPos = -1;
         updateDoubleCandList(mergeCand, cost, RdModeList, candCostList, RdModeList2, (uint32_t)NUM_LUMA_MODE, uiNumMrgSATDCand, &insertPos);
         if (insertPos != -1)
@@ -2183,6 +2196,13 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         }
         else
         {
+#if JVET_M0823_MMVD_ENCOPT
+          if (uiMergeCand >= mergeCtx.numValidMergeCand && uiMergeCand < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM) {
+            pu.mmvdEncOptMode = 0;
+            m_pcInterSearch->motionCompensation(pu);
+          }
+          else
+#endif
           if (uiNoResidualPass != 0 && uiMergeCand < mergeCtx.numValidMergeCand && RdModeList[uiMrgHADIdx] >= (MRG_MAX_NUM_CANDS + MMVD_ADD_NUM))
           {
             tempCS->getPredBuf().copyFrom(acMergeBuffer[uiMergeCand]);
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index 44bca844fc0e1d485274e811ad2c37a8cbee5007..dae751b02850c032e49a4dd6338a26b43fbe02c0 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -1427,6 +1427,10 @@ void EncSlice::checkDisFracMmvd( Picture* pcPic, uint32_t startCtuTsAddr, uint32
   {
     pcSlice->setDisFracMMVD( true );
   }
+#if JVET_M0312_FRACMMVD_SWITCH_FOR_UHD
+  bool useIntegerMVD = (pcPic->lwidth()*pcPic->lheight() > 1920 * 1080);
+  pcSlice->setDisFracMMVD(useIntegerMVD);
+#endif
 }
 #endif