diff --git a/develop b/develop
index d0519d5809b751939f67cf4688dc6349ab17cead_ZGV2ZWxvcA==..b6ef49274d2962798362ac2cf73cd0ee27d30af4_ZGV2ZWxvcA== 100755
--- a/develop
+++ b/develop
@@ -2,6 +2,6 @@
 
 rm -f target/wheels/*
 
-maturin build --no-sdist --manylinux 1 -i python3 --release "$@"
+maturin build --no-sdist --manylinux 2014 -i python3 --release "$@"
 
 pip install --force $(find target/wheels -name "*cp3*")
diff --git a/src/deserialize/decode.rs b/src/deserialize/decode.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL2Rlc2VyaWFsaXplL2RlY29kZS5ycw==..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL2Rlc2VyaWFsaXplL2RlY29kZS5ycw== 100644
--- a/src/deserialize/decode.rs
+++ b/src/deserialize/decode.rs
@@ -119,10 +119,10 @@
     where
         E: de::Error,
     {
-        Ok(nonnull!(str_to_pyobject!(value.as_str())))
+        Ok(nonnull!(unicode_from_str(value.as_str())))
     }
 
     fn visit_borrowed_str<E>(self, value: &str) -> Result<Self::Value, E>
     where
         E: de::Error,
     {
@@ -123,13 +123,13 @@
     }
 
     fn visit_borrowed_str<E>(self, value: &str) -> Result<Self::Value, E>
     where
         E: de::Error,
     {
-        Ok(nonnull!(str_to_pyobject!(value)))
+        Ok(nonnull!(unicode_from_str(value)))
     }
 
     fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
     where
         E: de::Error,
     {
@@ -130,10 +130,10 @@
     }
 
     fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
     where
         E: de::Error,
     {
-        Ok(nonnull!(str_to_pyobject!(value)))
+        Ok(nonnull!(unicode_from_str(value)))
     }
 
     fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
@@ -178,7 +178,7 @@
                     let entry = map.entry(&hash).or_insert_with(
                         || hash,
                         || {
-                            let pyob = str_to_pyobject!(&key);
+                            let pyob = unicode_from_str(&key);
                             CachedKey::new(pyob, hash_str(pyob))
                         },
                     );
@@ -187,7 +187,7 @@
                     pyhash = tmp.1;
                 }
             } else {
-                pykey = str_to_pyobject!(&key);
+                pykey = unicode_from_str(&key);
                 pyhash = hash_str(pykey);
             }
             let value = map.next_value_seed(self)?;
diff --git a/src/lib.rs b/src/lib.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL2xpYi5ycw==..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL2xpYi5ycw== 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -154,6 +154,7 @@
 }
 
 #[cold]
+#[inline(never)]
 fn raise_loads_exception(msg: Cow<str>) -> *mut PyObject {
     unsafe {
         let err_msg =
@@ -171,6 +172,7 @@
 }
 
 #[cold]
+#[inline(never)]
 fn raise_dumps_exception(msg: Cow<str>) -> *mut PyObject {
     unsafe {
         let err_msg =
diff --git a/src/serialize/dataclass.rs b/src/serialize/dataclass.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS9kYXRhY2xhc3MucnM=..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9kYXRhY2xhc3MucnM= 100644
--- a/src/serialize/dataclass.rs
+++ b/src/serialize/dataclass.rs
@@ -11,7 +11,85 @@
 
 use std::ptr::NonNull;
 
-pub struct DataclassSerializer {
+pub struct DataclassFastSerializer {
+    dict: *mut pyo3::ffi::PyObject,
+    opts: Opt,
+    default_calls: u8,
+    recursion: u8,
+    default: Option<NonNull<pyo3::ffi::PyObject>>,
+}
+
+impl DataclassFastSerializer {
+    pub fn new(
+        dict: *mut pyo3::ffi::PyObject,
+        opts: Opt,
+        default_calls: u8,
+        recursion: u8,
+        default: Option<NonNull<pyo3::ffi::PyObject>>,
+    ) -> Self {
+        DataclassFastSerializer {
+            dict: dict,
+            opts: opts,
+            default_calls: default_calls,
+            recursion: recursion,
+            default: default,
+        }
+    }
+}
+
+impl<'p> Serialize for DataclassFastSerializer {
+    #[inline(never)]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let len = unsafe { PyDict_GET_SIZE(self.dict) as usize };
+        if unlikely!(len == 0) {
+            return serializer.serialize_map(Some(0)).unwrap().end();
+        }
+        let mut map = serializer.serialize_map(None).unwrap();
+        let mut pos = 0isize;
+        let mut str_size: pyo3::ffi::Py_ssize_t = 0;
+        let mut key: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
+        let mut value: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
+        for _ in 0..=len - 1 {
+            unsafe {
+                pyo3::ffi::_PyDict_Next(
+                    self.dict,
+                    &mut pos,
+                    &mut key,
+                    &mut value,
+                    std::ptr::null_mut(),
+                )
+            };
+            if unlikely!(ob_type!(key) != STR_TYPE) {
+                err!(KEY_MUST_BE_STR)
+            }
+            {
+                let data = read_utf8_from_str(key, &mut str_size);
+                if unlikely!(data.is_null()) {
+                    err!(INVALID_STR)
+                }
+                let key_as_str = str_from_slice!(data, str_size);
+                if unlikely!(key_as_str.as_bytes()[0] == b'_') {
+                    continue;
+                }
+                map.serialize_key(key_as_str).unwrap();
+            }
+
+            map.serialize_value(&PyObjectSerializer::new(
+                value,
+                self.opts,
+                self.default_calls,
+                self.recursion + 1,
+                self.default,
+            ))?;
+        }
+        map.end()
+    }
+}
+
+pub struct DataclassFallbackSerializer {
     ptr: *mut pyo3::ffi::PyObject,
     opts: Opt,
     default_calls: u8,
@@ -19,7 +97,7 @@
     default: Option<NonNull<pyo3::ffi::PyObject>>,
 }
 
-impl DataclassSerializer {
+impl DataclassFallbackSerializer {
     pub fn new(
         ptr: *mut pyo3::ffi::PyObject,
         opts: Opt,
@@ -27,7 +105,7 @@
         recursion: u8,
         default: Option<NonNull<pyo3::ffi::PyObject>>,
     ) -> Self {
-        DataclassSerializer {
+        DataclassFallbackSerializer {
             ptr: ptr,
             opts: opts,
             default_calls: default_calls,
@@ -37,7 +115,7 @@
     }
 }
 
-impl<'p> Serialize for DataclassSerializer {
+impl<'p> Serialize for DataclassFallbackSerializer {
     #[inline(never)]
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -84,7 +162,7 @@
             let value = ffi!(PyObject_GetAttr(self.ptr, attr));
             ffi!(Py_DECREF(value));
 
-            map.serialize_value(&SerializePyObject::new(
+            map.serialize_value(&PyObjectSerializer::new(
                 value,
                 self.opts,
                 self.default_calls,
diff --git a/src/serialize/datetime.rs b/src/serialize/datetime.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS9kYXRldGltZS5ycw==..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9kYXRldGltZS5ycw== 100644
--- a/src/serialize/datetime.rs
+++ b/src/serialize/datetime.rs
@@ -56,6 +56,7 @@
     }
 }
 impl<'p> Serialize for Date {
+    #[inline(never)]
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
@@ -108,6 +109,7 @@
 }
 
 impl<'p> Serialize for Time {
+    #[inline(never)]
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
@@ -266,6 +268,7 @@
 }
 
 impl<'p> Serialize for DateTime {
+    #[inline(never)]
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
diff --git a/src/serialize/default.rs b/src/serialize/default.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS9kZWZhdWx0LnJz..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9kZWZhdWx0LnJz 100644
--- a/src/serialize/default.rs
+++ b/src/serialize/default.rs
@@ -8,10 +8,11 @@
 
 use std::ptr::NonNull;
 
-macro_rules! obj_name {
-    ($obj:expr) => {
-        unsafe { CStr::from_ptr((*$obj).tp_name).to_string_lossy() }
-    };
+#[cold]
+#[inline(never)]
+fn format_err(ptr: *mut pyo3::ffi::PyObject) -> String {
+    let name = unsafe { CStr::from_ptr((*ob_type!(ptr)).tp_name).to_string_lossy() };
+    format_args!("Type is not JSON serializable: {}", name).to_string()
 }
 
 pub struct DefaultSerializer {
@@ -57,8 +58,5 @@
                     std::ptr::null_mut() as *mut pyo3::ffi::PyObject
                 ));
                 if unlikely!(default_obj.is_null()) {
-                    err!(format_args!(
-                        "Type is not JSON serializable: {}",
-                        obj_name!(ob_type!(self.ptr))
-                    ))
+                    err!(format_err(self.ptr))
                 } else {
@@ -64,5 +62,5 @@
                 } else {
-                    let res = SerializePyObject::new(
+                    let res = PyObjectSerializer::new(
                         default_obj,
                         self.opts,
                         self.default_calls + 1,
@@ -74,10 +72,7 @@
                     res
                 }
             }
-            None => err!(format_args!(
-                "Type is not JSON serializable: {}",
-                obj_name!(ob_type!(self.ptr))
-            )),
+            None => err!(format_err(self.ptr)),
         }
     }
 }
diff --git a/src/serialize/dict.rs b/src/serialize/dict.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS9kaWN0LnJz..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9kaWN0LnJz 100644
--- a/src/serialize/dict.rs
+++ b/src/serialize/dict.rs
@@ -13,6 +13,79 @@
 use smallvec::SmallVec;
 use std::ptr::NonNull;
 
+pub struct Dict {
+    ptr: *mut pyo3::ffi::PyObject,
+    opts: Opt,
+    default_calls: u8,
+    recursion: u8,
+    default: Option<NonNull<pyo3::ffi::PyObject>>,
+    len: usize,
+}
+
+impl Dict {
+    pub fn new(
+        ptr: *mut pyo3::ffi::PyObject,
+        opts: Opt,
+        default_calls: u8,
+        recursion: u8,
+        default: Option<NonNull<pyo3::ffi::PyObject>>,
+        len: usize,
+    ) -> Self {
+        Dict {
+            ptr: ptr,
+            opts: opts,
+            default_calls: default_calls,
+            recursion: recursion,
+            default: default,
+            len: len,
+        }
+    }
+}
+
+impl<'p> Serialize for Dict {
+    #[inline(never)]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut map = serializer.serialize_map(None).unwrap();
+        let mut pos = 0isize;
+        let mut str_size: pyo3::ffi::Py_ssize_t = 0;
+        let mut key: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
+        let mut value: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
+        for _ in 0..=self.len - 1 {
+            unsafe {
+                pyo3::ffi::_PyDict_Next(
+                    self.ptr,
+                    &mut pos,
+                    &mut key,
+                    &mut value,
+                    std::ptr::null_mut(),
+                )
+            };
+            if unlikely!(ob_type!(key) != STR_TYPE) {
+                err!(KEY_MUST_BE_STR)
+            }
+            {
+                let data = read_utf8_from_str(key, &mut str_size);
+                if unlikely!(data.is_null()) {
+                    err!(INVALID_STR)
+                }
+                map.serialize_key(str_from_slice!(data, str_size)).unwrap();
+            }
+
+            map.serialize_value(&PyObjectSerializer::new(
+                value,
+                self.opts,
+                self.default_calls,
+                self.recursion + 1,
+                self.default,
+            ))?;
+        }
+        map.end()
+    }
+}
+
 pub struct DictSortedKey {
     ptr: *mut pyo3::ffi::PyObject,
     opts: Opt,
@@ -80,7 +153,7 @@
         for (key, val) in items.iter() {
             map.serialize_entry(
                 key,
-                &SerializePyObject::new(
+                &PyObjectSerializer::new(
                     *val,
                     self.opts,
                     self.default_calls,
@@ -101,7 +174,7 @@
     UnsupportedType,
 }
 
-pub struct NonStrKey {
+pub struct DictNonStrKey {
     ptr: *mut pyo3::ffi::PyObject,
     opts: Opt,
     default_calls: u8,
@@ -110,7 +183,7 @@
     len: usize,
 }
 
-impl NonStrKey {
+impl DictNonStrKey {
     pub fn new(
         ptr: *mut pyo3::ffi::PyObject,
         opts: Opt,
@@ -119,7 +192,7 @@
         default: Option<NonNull<pyo3::ffi::PyObject>>,
         len: usize,
     ) -> Self {
-        NonStrKey {
+        DictNonStrKey {
             ptr: ptr,
             opts: opts,
             default_calls: default_calls,
@@ -225,7 +298,7 @@
     }
 }
 
-impl<'p> Serialize for NonStrKey {
+impl<'p> Serialize for DictNonStrKey {
     #[inline(never)]
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -283,7 +356,7 @@
         for (key, val) in items.iter() {
             map.serialize_entry(
                 str_from_slice!(key.as_ptr(), key.len()),
-                &SerializePyObject::new(
+                &PyObjectSerializer::new(
                     *val,
                     self.opts,
                     self.default_calls,
diff --git a/src/serialize/encode.rs b/src/serialize/encode.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS9lbmNvZGUucnM=..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9lbmNvZGUucnM= 100644
--- a/src/serialize/encode.rs
+++ b/src/serialize/encode.rs
@@ -7,4 +7,5 @@
 use crate::serialize::datetime::*;
 use crate::serialize::default::*;
 use crate::serialize::dict::*;
+use crate::serialize::list::*;
 use crate::serialize::numpy::*;
@@ -10,4 +11,5 @@
 use crate::serialize::numpy::*;
+use crate::serialize::str::*;
 use crate::serialize::tuple::*;
 use crate::serialize::uuid::*;
 use crate::serialize::writer::*;
@@ -38,7 +40,7 @@
         _ => {}
     }
     buf.prefetch();
-    let obj = SerializePyObject::with_obtype(ptr, obtype, opts, 0, 0, default);
+    let obj = PyObjectSerializer::with_obtype(ptr, obtype, opts, 0, 0, default);
     let res;
     if likely!(opts & INDENT_2 != INDENT_2) {
         res = serde_json::to_writer(&mut buf, &obj);
@@ -127,7 +129,7 @@
             ObType::Uuid
         } else if (*(ob_type as *mut LocalPyTypeObject)).ob_type == ENUM_TYPE {
             ObType::Enum
-        } else if is_subclass!(ob_type, Py_TPFLAGS_UNICODE_SUBCLASS)
-            && opts & PASSTHROUGH_SUBCLASS == 0
+        } else if opts & PASSTHROUGH_SUBCLASS == 0
+            && is_subclass!(ob_type, Py_TPFLAGS_UNICODE_SUBCLASS)
         {
             ObType::StrSubclass
@@ -132,6 +134,6 @@
         {
             ObType::StrSubclass
-        } else if is_subclass!(ob_type, Py_TPFLAGS_LONG_SUBCLASS)
-            && opts & PASSTHROUGH_SUBCLASS == 0
+        } else if opts & PASSTHROUGH_SUBCLASS == 0
+            && is_subclass!(ob_type, Py_TPFLAGS_LONG_SUBCLASS)
         {
             ObType::Int
@@ -136,6 +138,6 @@
         {
             ObType::Int
-        } else if is_subclass!(ob_type, Py_TPFLAGS_LIST_SUBCLASS)
-            && opts & PASSTHROUGH_SUBCLASS == 0
+        } else if opts & PASSTHROUGH_SUBCLASS == 0
+            && is_subclass!(ob_type, Py_TPFLAGS_LIST_SUBCLASS)
         {
             ObType::List
@@ -140,7 +142,7 @@
         {
             ObType::List
-        } else if is_subclass!(ob_type, Py_TPFLAGS_DICT_SUBCLASS)
-            && opts & PASSTHROUGH_SUBCLASS == 0
+        } else if opts & PASSTHROUGH_SUBCLASS == 0
+            && is_subclass!(ob_type, Py_TPFLAGS_DICT_SUBCLASS)
         {
             ObType::Dict
         } else if opts & PASSTHROUGH_DATACLASS == 0
@@ -157,7 +159,7 @@
     }
 }
 
-pub struct SerializePyObject {
+pub struct PyObjectSerializer {
     ptr: *mut pyo3::ffi::PyObject,
     obtype: ObType,
     opts: Opt,
@@ -166,7 +168,7 @@
     default: Option<NonNull<pyo3::ffi::PyObject>>,
 }
 
-impl SerializePyObject {
+impl PyObjectSerializer {
     #[inline]
     pub fn new(
         ptr: *mut pyo3::ffi::PyObject,
@@ -175,7 +177,7 @@
         recursion: u8,
         default: Option<NonNull<pyo3::ffi::PyObject>>,
     ) -> Self {
-        SerializePyObject {
+        PyObjectSerializer {
             ptr: ptr,
             obtype: pyobject_to_obtype(ptr, opts),
             opts: opts,
@@ -194,7 +196,7 @@
         recursion: u8,
         default: Option<NonNull<pyo3::ffi::PyObject>>,
     ) -> Self {
-        SerializePyObject {
+        PyObjectSerializer {
             ptr: ptr,
             obtype: obtype,
             opts: opts,
@@ -205,7 +207,7 @@
     }
 }
 
-impl<'p> Serialize for SerializePyObject {
+impl<'p> Serialize for PyObjectSerializer {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
@@ -219,14 +221,7 @@
                 }
                 serializer.serialize_str(str_from_slice!(uni, str_size))
             }
-            ObType::StrSubclass => {
-                let mut str_size: pyo3::ffi::Py_ssize_t = 0;
-                let uni = ffi!(PyUnicode_AsUTF8AndSize(self.ptr, &mut str_size)) as *const u8;
-                if unlikely!(uni.is_null()) {
-                    err!(INVALID_STR)
-                }
-                serializer.serialize_str(str_from_slice!(uni, str_size))
-            }
+            ObType::StrSubclass => StrSubclassSerializer::new(self.ptr).serialize(serializer),
             ObType::Int => {
                 let val = ffi!(PyLong_AsLongLong(self.ptr));
                 if unlikely!(val == -1) && !ffi!(PyErr_Occurred()).is_null() {
@@ -256,39 +251,13 @@
                 if unlikely!(len == 0) {
                     serializer.serialize_map(Some(0)).unwrap().end()
                 } else if likely!(self.opts & SORT_OR_NON_STR_KEYS == 0) {
-                    let mut map = serializer.serialize_map(None).unwrap();
-                    let mut pos = 0isize;
-                    let mut str_size: pyo3::ffi::Py_ssize_t = 0;
-                    let mut key: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
-                    let mut value: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
-                    for _ in 0..=len - 1 {
-                        unsafe {
-                            pyo3::ffi::_PyDict_Next(
-                                self.ptr,
-                                &mut pos,
-                                &mut key,
-                                &mut value,
-                                std::ptr::null_mut(),
-                            )
-                        };
-                        if unlikely!(ob_type!(key) != STR_TYPE) {
-                            err!(KEY_MUST_BE_STR)
-                        }
-                        {
-                            let data = read_utf8_from_str(key, &mut str_size);
-                            if unlikely!(data.is_null()) {
-                                err!(INVALID_STR)
-                            }
-                            map.serialize_key(str_from_slice!(data, str_size)).unwrap();
-                        }
-
-                        map.serialize_value(&SerializePyObject::new(
-                            value,
-                            self.opts,
-                            self.default_calls,
-                            self.recursion + 1,
-                            self.default,
-                        ))?;
-                    }
-                    map.end()
+                    Dict::new(
+                        self.ptr,
+                        self.opts,
+                        self.default_calls,
+                        self.recursion,
+                        self.default,
+                        len,
+                    )
+                    .serialize(serializer)
                 } else if self.opts & NON_STR_KEYS != 0 {
@@ -294,5 +263,5 @@
                 } else if self.opts & NON_STR_KEYS != 0 {
-                    NonStrKey::new(
+                    DictNonStrKey::new(
                         self.ptr,
                         self.opts,
                         self.default_calls,
@@ -318,6 +287,6 @@
                     err!(RECURSION_LIMIT_REACHED)
                 }
                 let len = ffi!(PyList_GET_SIZE(self.ptr)) as usize;
-                if len == 0 {
+                if unlikely!(len == 0) {
                     serializer.serialize_seq(Some(0)).unwrap().end()
                 } else {
@@ -322,28 +291,13 @@
                     serializer.serialize_seq(Some(0)).unwrap().end()
                 } else {
-                    let mut type_ptr = std::ptr::null_mut();
-                    let mut ob_type = ObType::Str;
-
-                    let mut seq = serializer.serialize_seq(None).unwrap();
-                    for i in 0..=len - 1 {
-                        let elem = unsafe {
-                            *(*(self.ptr as *mut pyo3::ffi::PyListObject))
-                                .ob_item
-                                .offset(i as isize)
-                        };
-                        if ob_type!(elem) != type_ptr {
-                            type_ptr = ob_type!(elem);
-                            ob_type = pyobject_to_obtype(elem, self.opts);
-                        }
-                        seq.serialize_element(&SerializePyObject::with_obtype(
-                            elem,
-                            ob_type,
-                            self.opts,
-                            self.default_calls,
-                            self.recursion + 1,
-                            self.default,
-                        ))?;
-                    }
-                    seq.end()
+                    ListSerializer::new(
+                        self.ptr,
+                        self.opts,
+                        self.default_calls,
+                        self.recursion,
+                        self.default,
+                        len,
+                    )
+                    .serialize(serializer)
                 }
             }
@@ -348,20 +302,15 @@
                 }
             }
-            ObType::Tuple => {
-                let mut seq = serializer.serialize_seq(None).unwrap();
-                for elem in PyTupleIterator::new(self.ptr) {
-                    seq.serialize_element(&SerializePyObject::new(
-                        elem.as_ptr(),
-                        self.opts,
-                        self.default_calls,
-                        self.recursion + 1,
-                        self.default,
-                    ))?
-                }
-                seq.end()
-            }
+            ObType::Tuple => TupleSerializer::new(
+                self.ptr,
+                self.opts,
+                self.default_calls,
+                self.recursion,
+                self.default,
+            )
+            .serialize(serializer),
             ObType::Dataclass => {
                 if unlikely!(self.recursion == RECURSION_LIMIT) {
                     err!(RECURSION_LIMIT_REACHED)
                 }
                 let dict = ffi!(PyObject_GetAttr(self.ptr, DICT_STR));
@@ -363,7 +312,7 @@
             ObType::Dataclass => {
                 if unlikely!(self.recursion == RECURSION_LIMIT) {
                     err!(RECURSION_LIMIT_REACHED)
                 }
                 let dict = ffi!(PyObject_GetAttr(self.ptr, DICT_STR));
-                if !dict.is_null() {
+                if likely!(!dict.is_null()) {
                     ffi!(Py_DECREF(dict));
@@ -369,46 +318,11 @@
                     ffi!(Py_DECREF(dict));
-                    let len = unsafe { PyDict_GET_SIZE(dict) as usize };
-                    if unlikely!(len == 0) {
-                        return serializer.serialize_map(Some(0)).unwrap().end();
-                    }
-                    let mut map = serializer.serialize_map(None).unwrap();
-                    let mut pos = 0isize;
-                    let mut str_size: pyo3::ffi::Py_ssize_t = 0;
-                    let mut key: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
-                    let mut value: *mut pyo3::ffi::PyObject = std::ptr::null_mut();
-                    for _ in 0..=len - 1 {
-                        unsafe {
-                            pyo3::ffi::_PyDict_Next(
-                                dict,
-                                &mut pos,
-                                &mut key,
-                                &mut value,
-                                std::ptr::null_mut(),
-                            )
-                        };
-                        if unlikely!(ob_type!(key) != STR_TYPE) {
-                            err!(KEY_MUST_BE_STR)
-                        }
-                        {
-                            let data = read_utf8_from_str(key, &mut str_size);
-                            if unlikely!(data.is_null()) {
-                                err!(INVALID_STR)
-                            }
-                            let key_as_str = str_from_slice!(data, str_size);
-                            if unlikely!(key_as_str.as_bytes()[0] == b'_') {
-                                continue;
-                            }
-                            map.serialize_key(key_as_str).unwrap();
-                        }
-
-                        map.serialize_value(&SerializePyObject::new(
-                            value,
-                            self.opts,
-                            self.default_calls,
-                            self.recursion + 1,
-                            self.default,
-                        ))?;
-                    }
-                    map.end()
+                    DataclassFastSerializer::new(
+                        dict,
+                        self.opts,
+                        self.default_calls,
+                        self.recursion,
+                        self.default,
+                    )
+                    .serialize(serializer)
                 } else {
                     unsafe { pyo3::ffi::PyErr_Clear() };
@@ -413,6 +327,6 @@
                 } else {
                     unsafe { pyo3::ffi::PyErr_Clear() };
-                    DataclassSerializer::new(
+                    DataclassFallbackSerializer::new(
                         self.ptr,
                         self.opts,
                         self.default_calls,
@@ -425,7 +339,7 @@
             ObType::Enum => {
                 let value = ffi!(PyObject_GetAttr(self.ptr, VALUE_STR));
                 ffi!(Py_DECREF(value));
-                SerializePyObject::new(
+                PyObjectSerializer::new(
                     value,
                     self.opts,
                     self.default_calls,
diff --git a/src/serialize/list.rs b/src/serialize/list.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9saXN0LnJz
--- /dev/null
+++ b/src/serialize/list.rs
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+use crate::opt::*;
+use crate::serialize::encode::*;
+
+use serde::ser::{Serialize, SerializeSeq, Serializer};
+use std::ptr::NonNull;
+
+pub struct ListSerializer {
+    ptr: *mut pyo3::ffi::PyObject,
+    opts: Opt,
+    default_calls: u8,
+    recursion: u8,
+    default: Option<NonNull<pyo3::ffi::PyObject>>,
+    len: usize,
+}
+
+impl ListSerializer {
+    pub fn new(
+        ptr: *mut pyo3::ffi::PyObject,
+        opts: Opt,
+        default_calls: u8,
+        recursion: u8,
+        default: Option<NonNull<pyo3::ffi::PyObject>>,
+        len: usize,
+    ) -> Self {
+        ListSerializer {
+            ptr: ptr,
+            opts: opts,
+            default_calls: default_calls,
+            recursion: recursion,
+            default: default,
+            len: len,
+        }
+    }
+}
+
+impl<'p> Serialize for ListSerializer {
+    #[inline(never)]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut type_ptr = std::ptr::null_mut();
+        let mut ob_type = ObType::Str;
+
+        let mut seq = serializer.serialize_seq(None).unwrap();
+        for i in 0..=self.len - 1 {
+            let elem = unsafe {
+                *(*(self.ptr as *mut pyo3::ffi::PyListObject))
+                    .ob_item
+                    .offset(i as isize)
+            };
+            if ob_type!(elem) != type_ptr {
+                type_ptr = ob_type!(elem);
+                ob_type = pyobject_to_obtype(elem, self.opts);
+            }
+            seq.serialize_element(&PyObjectSerializer::with_obtype(
+                elem,
+                ob_type,
+                self.opts,
+                self.default_calls,
+                self.recursion + 1,
+                self.default,
+            ))?;
+        }
+        seq.end()
+    }
+}
diff --git a/src/serialize/mod.rs b/src/serialize/mod.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS9tb2QucnM=..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9tb2QucnM= 100644
--- a/src/serialize/mod.rs
+++ b/src/serialize/mod.rs
@@ -5,4 +5,5 @@
 mod default;
 mod dict;
 mod encode;
+mod list;
 mod numpy;
@@ -8,4 +9,5 @@
 mod numpy;
+mod str;
 mod tuple;
 mod uuid;
 mod writer;
diff --git a/src/serialize/str.rs b/src/serialize/str.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS9zdHIucnM=
--- /dev/null
+++ b/src/serialize/str.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+use crate::exc::*;
+
+use serde::ser::{Serialize, Serializer};
+
+#[repr(transparent)]
+pub struct StrSubclassSerializer {
+    ptr: *mut pyo3::ffi::PyObject,
+}
+
+impl StrSubclassSerializer {
+    pub fn new(ptr: *mut pyo3::ffi::PyObject) -> Self {
+        StrSubclassSerializer { ptr: ptr }
+    }
+}
+
+impl<'p> Serialize for StrSubclassSerializer {
+    #[inline(never)]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut str_size: pyo3::ffi::Py_ssize_t = 0;
+        let uni = ffi!(PyUnicode_AsUTF8AndSize(self.ptr, &mut str_size)) as *const u8;
+        if unlikely!(uni.is_null()) {
+            err!(INVALID_STR)
+        }
+        serializer.serialize_str(str_from_slice!(uni, str_size))
+    }
+}
diff --git a/src/serialize/tuple.rs b/src/serialize/tuple.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS90dXBsZS5ycw==..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS90dXBsZS5ycw== 100644
--- a/src/serialize/tuple.rs
+++ b/src/serialize/tuple.rs
@@ -1,4 +1,8 @@
 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
+use crate::opt::*;
+use crate::serialize::encode::*;
+
+use serde::ser::{Serialize, SerializeSeq, Serializer};
 use std::ptr::NonNull;
 
@@ -3,8 +7,10 @@
 use std::ptr::NonNull;
 
-pub struct PyTupleIterator {
-    list: *mut pyo3::ffi::PyObject,
-    len: isize,
-    idx: isize,
+pub struct TupleSerializer {
+    ptr: *mut pyo3::ffi::PyObject,
+    opts: Opt,
+    default_calls: u8,
+    recursion: u8,
+    default: Option<NonNull<pyo3::ffi::PyObject>>,
 }
 
@@ -9,12 +15,20 @@
 }
 
-impl PyTupleIterator {
-    pub fn new(list: *mut pyo3::ffi::PyObject) -> Self {
-        PyTupleIterator {
-            list: list,
-            len: ffi!(PyTuple_GET_SIZE(list)),
-            idx: 0,
+impl TupleSerializer {
+    pub fn new(
+        ptr: *mut pyo3::ffi::PyObject,
+        opts: Opt,
+        default_calls: u8,
+        recursion: u8,
+        default: Option<NonNull<pyo3::ffi::PyObject>>,
+    ) -> Self {
+        TupleSerializer {
+            ptr: ptr,
+            opts: opts,
+            default_calls: default_calls,
+            recursion: recursion,
+            default: default,
         }
     }
 }
 
@@ -17,16 +31,21 @@
         }
     }
 }
 
-impl Iterator for PyTupleIterator {
-    type Item = NonNull<pyo3::ffi::PyObject>;
-
-    #[inline]
-    fn next(&mut self) -> Option<NonNull<pyo3::ffi::PyObject>> {
-        if self.len == self.idx {
-            None
-        } else {
-            let item = nonnull!(ffi!(PyTuple_GET_ITEM(self.list, self.idx as isize)));
-            self.idx += 1;
-            Some(item)
+impl<'p> Serialize for TupleSerializer {
+    #[inline(never)]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut seq = serializer.serialize_seq(None).unwrap();
+        for i in 0..=ffi!(PyTuple_GET_SIZE(self.ptr)) - 1 {
+            let elem = nonnull!(ffi!(PyTuple_GET_ITEM(self.ptr, i as isize)));
+            seq.serialize_element(&PyObjectSerializer::new(
+                elem.as_ptr(),
+                self.opts,
+                self.default_calls,
+                self.recursion + 1,
+                self.default,
+            ))?
         }
@@ -32,3 +51,4 @@
         }
+        seq.end()
     }
 }
diff --git a/src/serialize/uuid.rs b/src/serialize/uuid.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3NlcmlhbGl6ZS91dWlkLnJz..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3NlcmlhbGl6ZS91dWlkLnJz 100644
--- a/src/serialize/uuid.rs
+++ b/src/serialize/uuid.rs
@@ -51,6 +51,7 @@
     }
 }
 impl<'p> Serialize for UUID {
+    #[inline(never)]
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
diff --git a/src/unicode.rs b/src/unicode.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3VuaWNvZGUucnM=..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3VuaWNvZGUucnM= 100644
--- a/src/unicode.rs
+++ b/src/unicode.rs
@@ -109,7 +109,6 @@
     }
 }
 
-#[inline]
 pub fn read_utf8_from_str(op: *mut PyObject, str_size: &mut Py_ssize_t) -> *const u8 {
     unsafe {
         if (*op.cast::<PyASCIIObject>()).state & STATE_COMPACT_ASCII == STATE_COMPACT_ASCII {
diff --git a/src/util.rs b/src/util.rs
index d0519d5809b751939f67cf4688dc6349ab17cead_c3JjL3V0aWwucnM=..b6ef49274d2962798362ac2cf73cd0ee27d30af4_c3JjL3V0aWwucnM= 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -42,12 +42,6 @@
     };
 }
 
-macro_rules! str_to_pyobject {
-    ($obj:expr) => {
-        crate::unicode::unicode_from_str($obj)
-    };
-}
-
 macro_rules! ffi {
     ($fn:ident()) => {
         unsafe { pyo3::ffi::$fn() }