ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
 }
 
-TEST_F(LibRadosIoECPP, SimpleWritePP) {
+TEST_P(LibRadosIoECPP, SimpleWritePP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
   ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
 }
 
-TEST_F(LibRadosIoECPP, ReadOpPP) {
+TEST_P(LibRadosIoECPP, ReadOpPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
   }
 }
 
-TEST_F(LibRadosIoECPP, SparseReadOpPP) {
+TEST_P(LibRadosIoECPP, SparseReadOpPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
   }
 }
 
-TEST_F(LibRadosIoECPP, RoundTripPP) {
+TEST_P(LibRadosIoECPP, RoundTripPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   Rados cluster;
   ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf)));
 }
 
-TEST_F(LibRadosIoECPP, RoundTripPP2)
+TEST_P(LibRadosIoECPP, RoundTripPP2)
 {
   SKIP_IF_CRIMSON();
   bufferlist bl;
   ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
 }
 
-TEST_F(LibRadosIoECPP, OverlappingWriteRoundTripPP) {
+TEST_P(LibRadosIoECPP, OverlappingWriteRoundTripPP) {
   SKIP_IF_CRIMSON();
   int bsize = alignment;
   int dbsize = bsize * 2;
   ASSERT_EQ(0, memcmp(bl3.c_str(), buf, dbsize));
 }
 
-TEST_F(LibRadosIoECPP, WriteFullRoundTripPP) {
+TEST_P(LibRadosIoECPP, WriteFullRoundTripPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   char buf2[64];
   ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2)));
 }
 
-TEST_F(LibRadosIoECPP, WriteFullRoundTripPP2)
+TEST_P(LibRadosIoECPP, WriteFullRoundTripPP2)
 {
   SKIP_IF_CRIMSON();
   bufferlist bl;
   ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
 }
 
-TEST_F(LibRadosIoECPP, AppendRoundTripPP) {
+TEST_P(LibRadosIoECPP, AppendRoundTripPP) {
   SKIP_IF_CRIMSON();
   char *buf = (char *)new char[alignment];
   char *buf2 = (char *)new char[alignment];
   ASSERT_EQ(0, memcmp(bl3_str + alignment, buf2, alignment));
 }
 
-TEST_F(LibRadosIoECPP, TruncTestPP) {
+TEST_P(LibRadosIoECPP, TruncTestPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xaa, sizeof(buf));
   ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)));
 }
 
-TEST_F(LibRadosIoECPP, RemoveTestPP) {
+TEST_P(LibRadosIoECPP, RemoveTestPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xaa, sizeof(buf));
   ASSERT_EQ(-ENOENT, ioctx.read("foo", bl2, sizeof(buf), 0));
 }
 
-TEST_F(LibRadosIoECPP, XattrsRoundTripPP) {
+TEST_P(LibRadosIoECPP, XattrsRoundTripPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   char attr1[] = "attr1";
   ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
 }
 
-TEST_F(LibRadosIoECPP, RmXattrPP) {
+TEST_P(LibRadosIoECPP, RmXattrPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   char attr1[] = "attr1";
   ASSERT_EQ(-ENOENT, ioctx.rmxattr("foo_rmxattr", attr2));
 }
 
-TEST_F(LibRadosIoECPP, CrcZeroWrite) {
+TEST_P(LibRadosIoECPP, CrcZeroWrite) {
   SKIP_IF_CRIMSON();
   set_allow_ec_overwrites();
   char buf[128];
   ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
 }
 
-TEST_F(LibRadosIoECPP, XattrListPP) {
+TEST_P(LibRadosIoECPP, XattrListPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   char attr1[] = "attr1";
   ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
 }
 
-TEST_F(LibRadosIoECPP, CmpExtPP) {
+TEST_P(LibRadosIoECPP, CmpExtPP) {
   SKIP_IF_CRIMSON();
   bufferlist bl;
   bl.append("ceph");
   ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
 }
 
-TEST_F(LibRadosIoECPP, CmpExtDNEPP) {
+TEST_P(LibRadosIoECPP, CmpExtDNEPP) {
   SKIP_IF_CRIMSON();
   bufferlist bl;
   bl.append(std::string(4, '\0'));
   ASSERT_EQ(0, memcmp(bl.c_str(), "CEPH", 4));
 }
 
-TEST_F(LibRadosIoECPP, CmpExtMismatchPP) {
+TEST_P(LibRadosIoECPP, CmpExtMismatchPP) {
   SKIP_IF_CRIMSON();
   bufferlist bl;
   bl.append("ceph");
   ASSERT_EQ(0, ioctx.operate("foo", &read, &bl));
   ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
 }
+
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosIoECPP);
\ No newline at end of file
 
 }
 
 // EC testing
-TEST_F(LibRadosLockECPP, LockExclusivePP) {
+TEST_P(LibRadosLockECPP, LockExclusivePP) {
   SKIP_IF_CRIMSON();
   ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL,  0));
   ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP1", "Cookie", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockECPP, LockSharedPP) {
+TEST_P(LibRadosLockECPP, LockSharedPP) {
   SKIP_IF_CRIMSON();
   ASSERT_EQ(0, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
   ASSERT_EQ(-EEXIST, ioctx.lock_shared("foo", "TestLockECPP2", "Cookie", "Tag", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockECPP, LockExclusiveDurPP) {
+TEST_P(LibRadosLockECPP, LockExclusiveDurPP) {
   SKIP_IF_CRIMSON();
   struct timeval tv;
   tv.tv_sec = 1;
   ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_exclusive, nullptr));
 }
 
-TEST_F(LibRadosLockECPP, LockSharedDurPP) {
+TEST_P(LibRadosLockECPP, LockSharedDurPP) {
   SKIP_IF_CRIMSON();
   struct timeval tv;
   tv.tv_sec = 1;
   ASSERT_EQ(expected, wait_until(1.0s, 0.1s, expected, lock_shared, nullptr));
 }
 
-TEST_F(LibRadosLockECPP, LockMayRenewPP) {
+TEST_P(LibRadosLockECPP, LockMayRenewPP) {
   SKIP_IF_CRIMSON();
   ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
   ASSERT_EQ(-EEXIST, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, 0));
   ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP5", "Cookie", "", NULL, LOCK_FLAG_MAY_RENEW));
 }
 
-TEST_F(LibRadosLockECPP, UnlockPP) {
+TEST_P(LibRadosLockECPP, UnlockPP) {
   SKIP_IF_CRIMSON();
   ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
   ASSERT_EQ(0, ioctx.unlock("foo", "TestLockECPP6", "Cookie"));
   ASSERT_EQ(0, ioctx.lock_exclusive("foo", "TestLockECPP6", "Cookie", "", NULL, 0));
 }
 
-TEST_F(LibRadosLockECPP, ListLockersPP) {
+TEST_P(LibRadosLockECPP, ListLockersPP) {
   SKIP_IF_CRIMSON();
   std::stringstream sstm;
   sstm << "client." << cluster.get_instance_id();
   }
 }
 
-TEST_F(LibRadosLockECPP, BreakLockPP) {
+TEST_P(LibRadosLockECPP, BreakLockPP) {
   SKIP_IF_CRIMSON();
   int exclusive;
   std::string tag;
   ASSERT_EQ("Cookie", it->cookie);
   ASSERT_EQ(0, ioctx.break_lock("foo", "TestLockECPP8", it->client, "Cookie"));
 }
+
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosLockECPP);
 
 protected:
   static void SetUpTestCase() {
     SKIP_IF_CRIMSON();
-    pool_name = get_temp_pool_name();
-    ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+    pool_name_default = get_temp_pool_name();
+    pool_name_fast = get_temp_pool_name();
+    pool_name_fast_split = get_temp_pool_name();
     src_pool_name = get_temp_pool_name();
+    ASSERT_EQ("", connect_cluster_pp(s_cluster));
     ASSERT_EQ(0, s_cluster.pool_create(src_pool_name.c_str()));
 
-    librados::IoCtx ioctx;
-    ASSERT_EQ(0, s_cluster.ioctx_create(pool_name.c_str(), ioctx));
-    ioctx.application_enable("rados", true);
-
-    librados::IoCtx src_ioctx;
-    ASSERT_EQ(0, s_cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
-    src_ioctx.application_enable("rados", true);
+    for (const std::string& pool_name : {pool_name_default, pool_name_fast, pool_name_fast_split, src_pool_name}) {
+      ASSERT_EQ("", create_ec_pool_pp(pool_name_default, s_cluster, false));
+      librados::IoCtx ioctx;
+      ASSERT_EQ(0, s_cluster.ioctx_create(pool_name.c_str(), ioctx));
+      ioctx.application_enable("rados", true);
+    }
+    ASSERT_EQ("", set_split_ops_pp(pool_name_fast_split, s_cluster, true));
   }
   static void TearDownTestCase() {
     SKIP_IF_CRIMSON();
-    ASSERT_EQ(0, s_cluster.pool_delete(src_pool_name.c_str()));
-    ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+    for (const std::string& pool_name : {pool_name_default, pool_name_fast, pool_name_fast_split, src_pool_name}) {
+      ASSERT_EQ(0, destroy_ec_pool_pp(pool_name, s_cluster));
+    }
+    s_cluster.shutdown();
   }
   static std::string src_pool_name;
 
   ASSERT_EQ(expected_meta, meta);
 }
 
-TEST_F(LibRadosMiscECPP, CompareExtentRange) {
+TEST_P(LibRadosMiscECPP, CompareExtentRange) {
   SKIP_IF_CRIMSON();
   bufferlist bl1;
   bl1.append("ceph");
   ASSERT_EQ(0, cluster.conf_get(option, actual));
   ASSERT_EQ(expected, actual);
 }
+
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosMiscECPP);
 
 }
 
 // EC testing
-TEST_F(LibRadosSnapshotsECPP, SnapListPP) {
+TEST_P(LibRadosSnapshotsECPP, SnapListPP) {
   SKIP_IF_CRIMSON();
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
   EXPECT_EQ(0, ioctx.snap_remove("snap1"));
 }
 
-TEST_F(LibRadosSnapshotsECPP, SnapRemovePP) {
+TEST_P(LibRadosSnapshotsECPP, SnapRemovePP) {
   SKIP_IF_CRIMSON();
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
   ASSERT_EQ(-ENOENT, ioctx.snap_lookup("snap1", &rid));
 }
 
-TEST_F(LibRadosSnapshotsECPP, RollbackPP) {
+TEST_P(LibRadosSnapshotsECPP, RollbackPP) {
   SKIP_IF_CRIMSON();
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
   EXPECT_EQ(0, ioctx.snap_remove("snap1"));
 }
 
-TEST_F(LibRadosSnapshotsECPP, SnapGetNamePP) {
+TEST_P(LibRadosSnapshotsECPP, SnapGetNamePP) {
   SKIP_IF_CRIMSON();
   char buf[bufsize];
   memset(buf, 0xcc, sizeof(buf));
   EXPECT_EQ(0, ioctx.snap_remove("snapfoo"));
 }
 
-TEST_F(LibRadosSnapshotsSelfManagedECPP, SnapPP) {
+TEST_P(LibRadosSnapshotsSelfManagedECPP, SnapPP) {
   SKIP_IF_CRIMSON();
   std::vector<uint64_t> my_snaps;
   my_snaps.push_back(-2);
   delete[] buf2;
 }
 
-TEST_F(LibRadosSnapshotsSelfManagedECPP, RollbackPP) {
+TEST_P(LibRadosSnapshotsSelfManagedECPP, RollbackPP) {
   SKIP_IF_CRIMSON();
   std::vector<uint64_t> my_snaps;
   IoCtx readioctx;
   delete[] buf2;
 }
 
-TEST_F(LibRadosSnapshotsSelfManagedECPP, Bug11677) {
+TEST_P(LibRadosSnapshotsSelfManagedECPP, Bug11677) {
   SKIP_IF_CRIMSON();
   std::vector<uint64_t> my_snaps;
   my_snaps.push_back(-2);
   ioctx.snap_set_read(LIBRADOS_SNAP_HEAD);
   delete[] buf;
 }
+
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosSnapshotsECPP);
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosSnapshotsSelfManagedECPP);
 
   ASSERT_EQ(0, cluster.get_pool_stats(v, stats));
 }
 
-TEST_F(LibRadosStatECPP, StatPP) {
+TEST_P(LibRadosStatECPP, StatPP) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
   ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
 }
 
-TEST_F(LibRadosStatECPP, ClusterStatPP) {
+TEST_P(LibRadosStatECPP, ClusterStatPP) {
   SKIP_IF_CRIMSON();
   cluster_stat_t cstat;
   ASSERT_EQ(0, cluster.cluster_stat(cstat));
 }
 
-TEST_F(LibRadosStatECPP, PoolStatPP) {
+TEST_P(LibRadosStatECPP, PoolStatPP) {
   SKIP_IF_CRIMSON();
   std::string n = ioctx.get_pool_name();
   ASSERT_EQ(n, pool_name);
   ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
 }
 
-TEST_F(LibRadosStatECPP, StatPPNS) {
+TEST_P(LibRadosStatECPP, StatPPNS) {
   SKIP_IF_CRIMSON();
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
   ASSERT_EQ(-ENOENT, ioctx.stat("nonexistent", &size, &mtime));
   ASSERT_EQ(-ENOENT, ioctx.stat("foo2", &size, &mtime));
 }
+
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosStatECPP);
 
 #include <time.h>
 #include <unistd.h>
 #include <iostream>
+
+#include "test.h"
 #include "gtest/gtest.h"
 
 using namespace librados;
   return destroy_rule_pp(cluster, rule, oss);
 }
 
-std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
-{
+std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster) {
   std::string err = connect_cluster_pp(cluster);
   if (err.length())
     return err;
 
+  err = create_ec_pool_pp(pool_name, cluster, false);
+  if (err.length()) {
+    cluster.shutdown();
+    return err;
+  }
+
+  return err;
+}
+
+std::string create_ec_pool_pp(const std::string &pool_name, Rados &cluster, bool fast_ec) {
   std::ostringstream oss;
   int ret = destroy_ec_profile_and_rule_pp(cluster, pool_name, oss);
   if (ret) {
-    cluster.shutdown();
     return oss.str();
   }
 
   if (ret) {
     bufferlist inbl;
     destroy_ec_profile_pp(cluster, pool_name, oss);
-    cluster.shutdown();
     oss << "mon_command osd pool create pool:" << pool_name << " pool_type:erasure failed with error " << ret;
     return oss.str();
   }
 
+  if (fast_ec) {
+    ret = cluster.mon_command(
+      "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name +
+      "\", \"var\": \"allow_ec_optimizations\", \"val\": \"true\"}",
+      inbl, NULL, NULL);
+    if (ret) {
+      destroy_ec_pool_pp(pool_name, cluster);
+      destroy_ec_profile_pp(cluster, pool_name, oss);
+      oss << "rados_mon_command osd pool create failed with error " << ret;
+      return oss.str();
+    }
+  }
+
   cluster.wait_for_latest_osdmap();
   return "";
 }
 
+std::string set_pool_flags_pp(const std::string &pool_name, librados::Rados &cluster, int64_t flags, bool set_not_unset) {
+  std::ostringstream oss;
+
+  std::string cmdstr = fmt::format(
+      R"({{"prefix": "osd pool set", "pool": "{}", "var": "{}", "val": "{}", "yes_i_really_mean_it": true}})",
+      pool_name,
+      (set_not_unset ? "set_pool_flags" : "unset_pool_flags"),
+      flags
+  );
+
+  char *cmd[2];
+  cmd[0] = (char *)cmdstr.c_str();
+  cmd[1] = NULL;
+
+  bufferlist inbl;
+  int ret = cluster.mon_command(cmdstr, inbl, NULL, NULL);
+  if (ret) {
+    oss << "rados_mon_command osd pool set set_pool_flags_pp failed with error " << ret;
+  }
+
+  return oss.str();
+}
+
+std::string set_split_ops_pp(const std::string &pool_name, librados::Rados &cluster, bool set_not_unset) {
+  return set_pool_flags_pp(pool_name, cluster, 1<<20, set_not_unset);
+}
+
 std::string set_allow_ec_overwrites_pp(const std::string &pool_name, Rados &cluster, bool allow)
 {
   std::ostringstream oss;
 {
   int ret = cluster.pool_delete(pool_name.c_str());
   if (ret) {
-    cluster.shutdown();
-    return ret;
-  }
-  cluster.shutdown();
-  return 0;
-}
-
-int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
-{
-  int ret = cluster.pool_delete(pool_name.c_str());
-  if (ret) {
-    cluster.shutdown();
     return ret;
   }
 
     std::ostringstream oss;
     ret = destroy_ec_profile_and_rule_pp(cluster, pool_name, oss);
     if (ret) {
-      cluster.shutdown();
       return ret;
     }
   }
+  return 0;
+}
+
+int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
+{
+  int ret = destroy_one_pool_pp(pool_name, cluster);
+  if (ret) {
+    cluster.shutdown();
+    return ret;
+  }
 
   cluster.wait_for_latest_osdmap();
   cluster.shutdown();
   return ret;
 }
+
+int destroy_ec_pool_pp(const std::string &pool_name, Rados &cluster) {
+  return cluster.pool_delete(pool_name.c_str());
+}
 
                               const std::map<std::string, std::string> &config);
 std::string create_one_ec_pool_pp(const std::string &pool_name,
                            librados::Rados &cluster);
+std::string create_ec_pool_pp(const std::string &pool_name,
+                            librados::Rados &cluster,
+                            bool fast_ec);
 std::string set_allow_ec_overwrites_pp(const std::string &pool_name,
                                       librados::Rados &cluster, bool allow);
 std::string connect_cluster_pp(librados::Rados &cluster);
                               const std::map<std::string, std::string> &config);
 int destroy_one_pool_pp(const std::string &pool_name, librados::Rados &cluster);
 int destroy_one_ec_pool_pp(const std::string &pool_name, librados::Rados &cluster);
+int destroy_ec_pool_pp(const std::string &pool_name, librados::Rados &cluster);
+std::string set_pool_flags_pp(const std::string &pool_name, librados::Rados &cluster, int64_t flags, bool set_not_unset);
+std::string set_split_ops_pp(const std::string &pool_name, librados::Rados &cluster, bool set_not_unset);
+
+// The following are convenient macros for defining test combinations
+// with each of the gtest suites.
+#define INSTANTIATE_TEST_SUITE_P_EC(CLASS) \
+INSTANTIATE_TEST_SUITE_P( CLASS ## ParamCombination, CLASS, \
+::testing::Combine( \
+::testing::Bool(),   /* fast_ec */ \
+::testing::Bool()))  /* split_ops */
+
+#define INSTANTIATE_TEST_SUITE_P_REPLICA(CLASS) \
+INSTANTIATE_TEST_SUITE_P( CLASS ## ParamCombination, CLASS, \
+::testing::Bool()) /* split_ops */
 
 }
 
 std::string RadosTestPP::pool_name;
-Rados RadosTestPP::s_cluster;
+Rados RadosTestPPBase::s_cluster;
 
 void RadosTestPP::SetUpTestCase()
 {
   ioctx.close();
 }
 
-void RadosTestPP::cleanup_default_namespace(librados::IoCtx ioctx)
+void RadosTestPPBase::cleanup_default_namespace(librados::IoCtx ioctx)
 {
   // remove all objects from the default namespace to avoid polluting
   // other tests
   cleanup_namespace(ioctx, "");
 }
 
-void RadosTestPP::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
+void RadosTestPPBase::cleanup_namespace(librados::IoCtx ioctx, std::string ns)
 {
   ioctx.snap_set_read(librados::SNAP_HEAD);
   ioctx.set_namespace(ns);
   }
 }
 
-std::string RadosTestECPP::pool_name;
+std::string RadosTestECPP::pool_name_default;
+std::string RadosTestECPP::pool_name_fast;
+std::string RadosTestECPP::pool_name_fast_split;
 Rados RadosTestECPP::s_cluster;
 
 void RadosTestECPP::SetUpTestCase()
 {
   SKIP_IF_CRIMSON();
   auto pool_prefix = fmt::format("{}_", ::testing::UnitTest::GetInstance()->current_test_case()->name());
-  pool_name = get_temp_pool_name(pool_prefix);
-  ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
+  pool_name_default = get_temp_pool_name(pool_prefix);
+  pool_name_fast = get_temp_pool_name(pool_prefix);
+  pool_name_fast_split = get_temp_pool_name(pool_prefix);
+  ASSERT_EQ("", connect_cluster_pp(s_cluster));
+  ASSERT_EQ("", create_ec_pool_pp(pool_name_default, s_cluster, false));
+  ASSERT_EQ("", create_ec_pool_pp(pool_name_fast, s_cluster, true));
+  ASSERT_EQ("", create_ec_pool_pp(pool_name_fast_split, s_cluster, true));
+  ASSERT_EQ("", set_split_ops_pp(pool_name_fast_split, s_cluster, true));
 }
 
 void RadosTestECPP::TearDownTestCase()
 {
   SKIP_IF_CRIMSON();
-  ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
+  ASSERT_EQ(0, destroy_ec_pool_pp(pool_name_default, s_cluster));
+  ASSERT_EQ(0, destroy_ec_pool_pp(pool_name_fast, s_cluster));
+  ASSERT_EQ(0, destroy_ec_pool_pp(pool_name_fast_split, s_cluster));
+  s_cluster.shutdown();
 }
 
 void RadosTestECPP::SetUp()
 {
   SKIP_IF_CRIMSON();
+  const auto& params = GetParam();
+  bool fast_ec = std::get<0>(params);
+  bool split_ops = std::get<1>(params);
+  if (fast_ec && split_ops) {
+    pool_name = pool_name_fast_split;
+  } else if (fast_ec) {
+    pool_name = pool_name_fast;
+  } else if (!split_ops) {
+    pool_name = pool_name_default;
+  } else {
+    GTEST_SKIP() << "Legacy EC does not support split ops";
+  }
   ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx));
   nspace = get_temp_pool_name();
   ioctx.set_namespace(nspace);
 
   RadosTestECPPNSCleanup() : RadosTestECPPNS(true) {}
 };
 
-class RadosTestPP : public ::testing::Test {
+class RadosTestPPBase {
 public:
-  RadosTestPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
+  RadosTestPPBase() : cluster(s_cluster) {};
+  ~RadosTestPPBase() = default;
+protected:
+  static librados::Rados s_cluster;
+  librados::Rados &cluster;
+  static void cleanup_default_namespace(librados::IoCtx ioctx);
+  static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
+};
+
+class RadosTestPP : public RadosTestPPBase, public ::testing::Test {
+public:
+  RadosTestPP(bool c=false) : cleanup(c) {}
   ~RadosTestPP() override {}
 protected:
   static void SetUpTestCase();
   static void TearDownTestCase();
-  static void cleanup_default_namespace(librados::IoCtx ioctx);
-  static void cleanup_namespace(librados::IoCtx ioctx, std::string ns);
-  static librados::Rados s_cluster;
   static std::string pool_name;
 
   void SetUp() override;
   void TearDown() override;
-  librados::Rados &cluster;
   librados::IoCtx ioctx;
   bool cleanup;
   std::string nspace;
   std::string nspace;
 };
 
-class RadosTestECPP : public RadosTestPP {
+class RadosTestECPP : public RadosTestPPBase,
+                      public ::testing::TestWithParam<std::tuple<bool, bool>> {
   bool ec_overwrites_set = false;
 public:
   RadosTestECPP(bool c=false) : cluster(s_cluster), cleanup(c) {}
   static void TearDownTestCase();
   void set_allow_ec_overwrites();
   static librados::Rados s_cluster;
-  static std::string pool_name;
+  static std::string pool_name_default;
+  static std::string pool_name_fast;
+  static std::string pool_name_fast_split;
 
+  std::string pool_name;
   void SetUp() override;
   void TearDown() override;
   librados::Rados &cluster;
 
 
 std::string LibRadosTwoPoolsECPP::cache_pool_name;
 
-TEST_F(LibRadosTierECPP, Dirty) {
+TEST_P(LibRadosTierECPP, Dirty) {
   SKIP_IF_CRIMSON();
   {
     ObjectWriteOperation op;
   ioctx.selfmanaged_snap_remove(my_snaps[0]);
 }
 
-TEST_F(LibRadosTierECPP, FlushWriteRaces) {
+TEST_P(LibRadosTierECPP, FlushWriteRaces) {
   SKIP_IF_CRIMSON();
   Rados cluster;
   std::string pool_name = get_temp_pool_name();
   cond.wait(locker, [] { return num_reads == 0;});
 }
 
-TEST_F(LibRadosTierECPP, CallForcesPromote) {
+TEST_P(LibRadosTierECPP, CallForcesPromote) {
   SKIP_IF_CRIMSON();
   Rados cluster;
   std::string pool_name = get_temp_pool_name();
   ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, cluster));
 }
 
-TEST_F(LibRadosTierECPP, HitSetNone) {
+TEST_P(LibRadosTierECPP, HitSetNone) {
   SKIP_IF_CRIMSON();
   {
     list< pair<time_t,time_t> > ls;
 
 // disable this test until hitset-get reliably works on EC pools
 #if 0
-TEST_F(LibRadosTierECPP, HitSetWrite) {
+TEST_P(LibRadosTierECPP, HitSetWrite) {
   int num_pg = _get_pg_num(cluster, pool_name);
   ceph_assert(num_pg > 0);
 
   }
 }
 
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosTierECPP);
 
   sem_destroy(&sem);
 }
 
-TEST_F(LibRadosWatchNotifyECPP, WatchNotify) {
+TEST_P(LibRadosWatchNotifyECPP, WatchNotify) {
   SKIP_IF_CRIMSON();
   ASSERT_EQ(0, sem_init(&sem, 0, 0));
   char buf[128];
   ASSERT_EQ(0, ioctx.unwatch("foo", handle));
 }
 
-TEST_F(LibRadosWatchNotifyECPP, WatchNotifyTimeout) {
+TEST_P(LibRadosWatchNotifyECPP, WatchNotifyTimeout) {
   SKIP_IF_CRIMSON();
   ASSERT_EQ(0, sem_init(&sem, 0, 0));
   ioctx.set_notify_timeout(1);
 
 INSTANTIATE_TEST_SUITE_P(LibRadosWatchNotifyPPTests, LibRadosWatchNotifyPP,
                        ::testing::Values("", "cache"));
+INSTANTIATE_TEST_SUITE_P_EC(LibRadosWatchNotifyECPP);