Dropdown filtering extremely slow

I’m surprised it’s all that slow but you can remove all but one of the database calls & most of the looping by passing the term data instead of just slugs to my_dropdown_categories() so get_terms() doesn’t have to be called, plus a few other improvements (see comments) eg:

    function my_dropdown_categories( $taxonomy, $current_selected = '', $terms = null, $no_select = false ) {
    // If all terms ever needed, uncomment following line.
    //if ( $terms === null ) $terms = get_terms($taxonomy, array('orderby' => 'name'));

    // our content variable - use array (& join at end) for efficiency
    $ret = array();

    if ( ! $no_select ) { // If not required, don't wrap in select.
        $ret[] = '<select id="location"  class="selectboxSingle" name="location">';
    }

    if ( $terms && ! is_wp_error( $terms ) ) {
        $parents = $children = array();
        foreach($terms as $term){
            if ($term->parent == 0 ) {
                $parents[] = $term;
            } else {
                $children[$term->parent][] = $term;
            }
        }

        foreach($parents as $term){
            $select = ($current_selected == $term->slug) ? "selected" : ""; // Note: ==

            if ( empty( $children[$term->term_id] ) ) {
                $ret[] = '<option value="'.$term->slug.'" '.$select.'>'. $term->name .' </option>';
            } else {
                //$ret[] = '<optgroup label="'. $term->name .'">';
                $ret[] = '<option class="group-result" value="'.$term->slug.'" '.$select.'>'. $term->name.' </option>';
                foreach ( $children[$term->term_id] as $child ) {
                     $select = ($current_selected == $child->slug) ? "selected" : "";
                     $ret[] = '<option value="'.$child->slug.'" '.$select.'>'. $child->name.' </option>';
                }
                //$ret[] = "</optgroup>";
            }
        }
    }

    if ( ! $no_select ) {
        $ret[] = '</select>';
    }

    return implode( "\n", $ret );
}

add_action( 'wp_ajax_wpse158929_get_terms_for_cpt', 'wpse158929_get_terms_for_cpt' );
add_action( 'wp_ajax_nopriv_wpse158929_get_terms_for_cpt', 'wpse158929_get_terms_for_cpt' );

function wpse158929_get_terms_for_cpt() {
    $ret = array( 'html' => '', 'error' => false );

    if ( ! check_ajax_referer( 'wpse158929_get_terms_for_cpt_submit_', 'nonce', false /*die*/ ) ) {
        $ret['error'] = __( 'Permission error', 'wpfm' );
    } else {
        $post_type = isset( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : '';
        $taxonomy = isset( $_REQUEST['taxonomy'] ) ? $_REQUEST['taxonomy'] : '';
        $current_selected = isset( $_REQUEST['current_selected'] ) ? $_REQUEST['current_selected'] : '';

        if ( ! $post_type || ! $taxonomy ) {
            $ret['error'] = __( 'Params error', 'wpfm' );
        } else {
            global $wpdb;
            // Get bare minimum of required data from database.
            $sql = $wpdb->prepare( 'SELECT t.term_id, t.name, t.slug, tt.parent FROM ' . $wpdb->terms . ' t'
                . ' JOIN ' . $wpdb->term_taxonomy . ' AS tt ON tt.term_id = t.term_id'
                . ' JOIN ' . $wpdb->term_relationships . ' AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id'
                . ' JOIN ' . $wpdb->posts . ' AS p ON p.ID = tr.object_id'
                . ' WHERE tt.taxonomy = %s AND p.post_type = %s AND p.post_status = %s'
                . ' GROUP BY t.slug'
                . ' ORDER BY t.name'
                , $taxonomy, $post_type, 'publish' );
            $terms = $wpdb->get_results( $sql );
            $ret['html'] = my_dropdown_categories( $taxonomy, $current_selected, $terms, true /*no_select*/ );
        }
    }

    wp_send_json( $ret );
}

Also you probably don’t need the call $('select[name="post_type"]').change(); in the javascript seeing as you’re overwriting the select immediately afterwards…